RightScale Policies provide a flexible solution for defining arbitrary policies that span across Cost, Security, Compliance, and Operational categories. Policies may analyze data exposed by the RightScale platform or by any other service. Policies can combine and validate data coming from multiple services. When a policy check fails an incident is created which may in turn trigger a sequence of actions. Actions may be automated and may take advantage of RightScale Cloud Workflow. This makes it possible for policies to trigger arbitrarily complex orchestration involving any service.
Policies are written in code making it possible to version, share and reuse parts of policies across multiple use cases. The policy template language is a simple DSL (Domain Specific Language) similar to the RightScale CAT (Cloud Application Template) language. Policy templates describe how to retrieve the data, how to validate it and what to do when a validation check fails.
Check out some sample policies as well as the complete list of RightScale policies.
Syntax
A policy template consists of a sequence of definitions. Definitions may be simple definitions of the form:
<name> <term>
Where <name>
is the name of the definition and <term>
is a term, for example:
short_description "Limit the total number of VMs running in a project."
Terms can be literal values like in the example above, function calls, certain reserved words or in some cases references to other definitions.
Definitions may also be composite definitions of the form:
<name> <term> [, <property>: <string literal>] do
<property> <term>
<property> <term>
…
end
Where <property>
is the name of a definition property, for example:
parameter "max_instances" do
type "number"
label "Maximum number of instances"
end
or
resources "instances", type: "rs_cm.instances" do
cloud_href href(@clouds)
state "operational"
end
Comments
Comments start with the character #
. They may appear at the end of a line or on a new line:
short_description "Limit number of VMs." # Note: only works on cloud X
# Comment on a new line
Reserved Words
The Policy Template Language also defines a few reserved words that make it possible to retrieve contextual information or iterate over data collections. The list of complete reserved words and their usage is described in the Reserved Word Reference. For example rs_org_id
returns the ID of the RightScale organization.
String Literals
A string literal begins and ends with a double or single-quote mark. Double-quoted string expressions are subject to backslash notation. A single-quoted string expression isn't; except for \' and \.
Backslash Notation
Also called escape characters, backslashes are used to insert special characters in a string.
Example:
"this is a\ntwo line string"
"this string has \"quotes\" in it"
Escape Sequence | Meaning |
---|---|
\n | newline |
\s | space |
\r | carriage return |
\t | tab |
\" | double quote character |
\' | single quote character |
\ | backslash |
Multiline String Literals
Multi-line string literals can be defined with here documents
, where the delimiter can be any string:
<<END
on the one ton temple bell
a moon-moth, folded into sleep,
sits still.
END
The syntax begins with <<
and is followed immediately by the delimiter. To end the string, the delimiter appears alone on a line.
Number Literals
A number literal can be a integer such as 5
or a floating point number such as 5.5
.
Array Literals
Arrays are defined using square brackets and commas to separate the elements:
["a", "b", "c"]
[1, 2, 3]
["a", [1, "b"]]
Object Literals
Objects are defined using curly braces and colons to separate keys and values:
{
"a": 1,
"b": "c",
"d": [2, 3],
"e": {
"f": 4
}
}
Metadata
The first section of a policy template provides generic information about the policy such as its name, a short and long description as well as the policy severity and category:
name "Instance Quota"
rs_pt_ver 20180301
type "policy"
short_description "Limit the total number of VMs running in a project."
# Optional metadata
default_frequency "daily"
long_description <<-EOF
The Instance Quota policy sends an email to developer@company.com when the
number of VMs running in the RightScale project exceeds 1,000.
EOF
severity "medium"
category "Cost"
tenancy "single"
info(
"version": "1.1",
"provider": "aws",
"service": "ec2"
)
name
defines the policy template name. It must be unique in the project.rs_pt_ver
indicates the policy template language version. The value must be20180301
.type
must be"policy"
short_description
provides a short description of what the policy does.
The following definitions are optional:
default_frequency
sets the interval between policy evaluations if another value is not selected by the person applying the policy. It must be one of"15 minutes"
(default),"hourly"
,"daily"
,"weekly"
or"monthly"
.long_description
is a long description of what the policy does.severity
indicates the policy severity, it must be one of"low"
(default),"medium"
,"high"
or"critical"
.category
is an arbitrary value used to group policies into categories, for example"Cost"
,"Compliance"
or"Security"
.info
specifies an arbitrary list of metadata attached to the policy and should be an mapping of string to string. For Flexera-built policies, the standard is to use the following metadata fields:version
- Version of the template, using semantic versioning. Updated every time a policy template is changed.provider
- An identifier for which provider the policy applies to, such asAWS
.service
- Where applicable, which service of the provider the policy applies to, such asEC2
tenancy
specifies whether this policy can be applied to more than one project at a time and how incidents are grouped together. It must be one of"multi"
, or"single"
. If a policy has acredential
declaration in it, onlysingle
is supported and is the default if not specified. If a policy does not have such a declaration, then this defaults tomulti
. Generally speaking this should only be set manually in the case where the policy acts on organization-wide resources such as Optima APIs, in which case it should be set tosingle
. Otherwise the default is sufficient and this can be omitted.
Parameters
Parameters allow you to get input from end users that can be passed to your policy, used to make decisions on what to validate, and used in custom operation logic. A parameter declaration has a name, a type and other optional fields that let you define things like a default value, or a set of potential values.
Parameters can be referred to in a Policy Template using the syntax $<parameter_name>
where parameter_name
is the string following the parameter
keyword.
Here is an example of a parameter declaration defining a tags
parameter which can take a list of tags to be used in the policy template.
parameter "tags" do
type "list"
label "Tags"
description "A list of tags for the policy"
end
Fields
The available fields are:
Name | Required? | Type | Description |
---|---|---|---|
type |
yes | string | Defines whether the parameter is a string, a number or a list of values. The possible values for this field are string , and number and list . |
label |
yes | string | The display name shown to the user. Must not be whitespace-only. |
category |
no | string | An optional category used to group parameters in the UI. |
description |
no | string | A description shown in the launch UI |
default |
no | Default value for parameter if none is specified. Note this must meet the requirements of the other fields (such as max_length, allowed pattern, etc). | |
no_echo |
no | boolean | Whether the value of the parameter should be hidden in UIs and API responses. The possible values for this field are true and false (default). |
allowed_values |
no | array | A comma-separated list of allowed values. Not valid when allowed_pattern is specified. |
min_length and max_length |
no | number | The minimum and maximum number of characters in the parameter value. Only valid when type is one of string or list . |
min_value and max_value |
no | number | The smallest and largest numeric value allowed for the parameter. Only valid when type is number . |
allowed_pattern |
no | regexp | Not valid when 'allowed_values' is specified. Note: the Ruby Regexp engine (a PCRE engine) is used to process and validate Regexp values, but there are a few unsupported features. These include modifiers other than 'm' and 'i', modifier groups (such as /(?mi)example/ ), and modifier spans (such as /(?mi:example)/ ). A helpful tool for developing PCRE regular expressions can be found here. |
constraint_description |
no | string | Message displayed when any of the constraint is violated. The system generates default error messages, this field allows overriding these to provide a clearer message to the user. |
Notes:
- Parameters are displayed in the UI in the same order as they are defined in the template.
- If a default value is set for a parameter, this value will be pre-populated in the UI. For example, for a parameter with allowed values of true/false and a default value of
true
, the resulting checkbox on the UI would be enabled. Additional information on UI behavior is provided below.
Usage
parameter "db_dump_bucket" do
type "string"
label "Database S3 bucket"
category "Database"
description "URL to S3 bucket that contains MySQL database dump"
min_length 3
max_length 63
allowed_pattern /[a-z0-9]+[a-z0-9\.-]*/
constraint_description <<-EOS
Bucket names must be at least 3 and no more than 63 characters long. Bucket names must be a series of one or more labels. Adjacent labels are separated by a single period (.). Bucket names can contain lowercase letters, numbers, and dashes. Each label must start and end with a lowercase letter or a number. Bucket names must not be formatted as an IP address (e.g., 192.168.5.4).
EOS
end
Parameters can be used in other declarations using the $
operator, for example:
parameter "tags" do
type "list"
label "A List of Tags"
end
# find all instances with the tags from the $tags parameter
resource "instances", type: 'rs_cm.instances' do
tags any($tags)
end
# pass the $tags parameter to the Cloud Work Flow
define get_code($tags) do
# use the $tags parameter
...
end
In the example above, the instances
resource declaration uses the value associated with the parameter tags
to initialize the instances resource tag field.
Permissions
Permission declarations validate that the user applying the policy has the required privileges to successfully run the policy. The declarations must include the privileges required to retrieve the data as well as the privileges required to run the policy actions. Permission declarations are not required to apply the policy or run the policy actions, however, they are recommended. The users own privileges are verified against the permission declarations when they attempt to apply the policy.
Each permission declaration can list multiple required privileges. The set is defined by providing a list of resource types and a list of actions that the policy needs to perform on these resources:
permission do
label "List Instances"
resources "rs_cm.instances"
actions "rs_cm.index"
end
permission do
label "List and delete servers and instances"
resources "rs_cm.servers", "rs_cm.instances"
actions "rs_cm.index", "rs_cm.destroy"
end
The complete set of resources and actions as well as the mapping between privileges and roles is listed in the Permission Reference section.
Retrieving Data
The first step performed when a policy is evaluated is to retrieve the data that needs to be validated. There are two ways to retrieve data from a policy template:
- Resources make it possible to list resources using the RightScale APIs.
- Datasources make it possible to retrieve data from any arbitrary service.
Datasources can also be used to further process data retrieved from either resources or other datasources as described in Chaining Datasources.
Listing Resources
The resources
definition identifies a resource type and an optional set of filters used to list resources:
resources <name>, type: <resource type> do
filter do
<filter> <filter comparator> <filter value>
<filter> <filter comparator> <filter value>
...
end
<property> <property value>
<property> <property value>
...
end
name
defines the resource name and must be unique. It can be referenced using @elsewhere in the code. resource type
is the type of resource.filter
is the API field to filter on, such asstate
. If multiple filters are specified it will AND together the results. Only resources which satisfy all the filters will be returned.filter comparator
is optional and must be eitherne:
oreq:
. If not specified, it defaults toeq:
.filter value
is either a term or array of terms. Caution: Only ever specify an array of terms withne:
as the comparator. If you specify it witheq:
it will do a logical AND and try and set the filter be equal to ALL the values passed in, which will return no results.property
is a property of the resource such ascloud_href
orview
.property value
is value of the property.
The complete list of supported resource types, filters, and properties is described in the Resource Reference.
This example filters returns active Windows instances on AWS clouds. The first resources block filters upon cloud_type to only return AWS regions. The filter comparator is left off and defaults to eq:
. The second resources block filters upon the os_platform and state of the instances to get active instances. It can be referenced later in the policy as @instances
.
resources "aws_regions", type: "rs_cm.clouds" do
filter do
cloud_type "amazon"
end
end
resources "instances", type: "rs_cm.instances" do
iterate @aws_regions
cloud_href href(iter_item)
filter do
os_platform "windows"
state ne: ["stopped", "provisioned", "terminated"]
end
end
Including tags
You can query for resources by tag using tags
field in your resource. You can also use the all
, any
, none
methods to expand your search with tags. Only one tag field is supported per resource.
resources "instances", type: "rs_cm.instances" do
iterate @aws_regions
cloud_href href(iter_item)
tags "ns:name=*"
end
resources "volumes", type: "rs_cm.volumes" do
iterate @aws_regions
cloud_href href(iter_item)
tags any(["ns:name=*","foo:bar=hurray"])
end
# include fields in your datasource
datasource "instances" do
iterate @instances
field "href", href(iter_item)
field "id", val(iter_item,'resource_uid')
field "name", val(iter_item,'name')
field "state", val(iter_item,'state')
field "type", "instances"
field "tags", val(iter_item,'tags')
end
Additional tag filters examples.
tags "ns:name=*" # Filter by namespace and key
tags "ns:*" # Filter by namespace only
tags "*" # Return any resource with a tag
tags any(["ns:name=*","foo:bar=hurray"]) # Resource must include any tag in array.
tags all(["ns:name=*","foo:bar=hurray"]) # Resource must include all tags in array.
tags none(["ns:name=*","foo:bar=hurray"]) # Resource must not include any tags in array
API Data with Datasources
There are three forms of datasource
definitions. The first one, discussed here makes it possible to retrieve data from HTTP APIs. The second one, described in the next section makes it possible to use JavaScript to process existing data and the last one described in Chaining Datasources makes it possible to derive a datasource from existing data.
Authorization
Authorization against any external APIs that a Policy uses is handled using credentials entered in the credentials page of Policy Manager. To use credentials to make API calls, add a credentials
declaration in the policy template. This declaration specifies all the details needed to use credentials and will allow the user of the policy to select the appropriate credential when applying the policy.
credentials <name> do
schemes <type1>, <type2>
label <label>
description <description>
tags <tag filters>...
end
name
is the name in the policy template language. The credential is referenceable by this name indatasource
anddefine
declarations.schemes
is the authorizationscheme
in the credentials service and must be one ofaws_sts
,aws
,basic
,ntlm
,api_key
,jwt
, oroauth2
, matching the API that the credential is used with. Multiple schemes can be listed if the credential and code work with multiple types. When a user selects a credential on the Apply Policy screen, they will only be able to select credentials that are of one of the specified schemes.label
is a short human readable label for the credential -- it is shown to the user on the Apply Policy screendescription
is a longer description of what the credential is used for in the policy and is also shown to the user on the Apply Policy screentags
is an optional field used to filter the credentials that are shown to a user when applying a policy. It may contain tags in the form ofkey=value
. By default, the credential management UI and all Flexera-built policies use the tag keyprovider
for credential matching purposes.
Each credentials
declaration will require a separate selection from the user. Note: older Policies may use define authorization inline using the auth declaration, but this approach is no longer considered best practice.
The following example will authenticate against an AWS API using either an aws
or aws_sts
scheme type and export a $cred_aws
reference.
credentials "cred_aws" do
schemes "aws", "aws_sts"
label "AWS Credential"
description "This credential should have read/write access to AWS EC2 Instances"
tags "provider=aws"
end
The following example will authenticate against an Azure API using either an oauth2
scheme type. Since there many be many APIs using the oauth2
scheme, an optional tags declaration is added to this example. Credentials can be tagged with arbitrary key value pairs. In this case, this block is telling it to filter for credentials marked with cloud=azure
credentials "cred_azure_compute" do
schemes "oauth2",
label "Azure compute credential"
description "Enter a credential with read/write access to Microsoft.Compute resources."
tags "provider=azure"
end
It is highly recommended to provide a provider
tag filter and then set the provider
field when entering credentials into the dashboard so policy managers can easily find credentials when applying the authored policy.
Pagination
If results from a custom datasource are paginated, you must define a pagination block above your datasource that describes how to extract the next page token from a response and where to insert the token into subsequent queries.
pagination <name> do
get_page_marker do
body_path <path term>
header <header name>
end
set_page_marker do
query <query param name>
header <header name>
body_field <body field name>
uri <true|false>
end
<name>
is the name of the paginator. The name can be referred to in thepagination
field of datasource request blocks with a$
in front.<path term>
is a call to either thejmes_path
,jq
, orxpath
method describing how to extract the page token from the response body (for backward compatibility, it can also be a string which will be interpreted as either JMESPath for JSON encoding or XPath for XML encoding).<header name>
is the name of the http header containing the page token.<query param name>
is the name of the query string parameter to set.<body field name>
is the name of the field in the body to set.uri
should be set to true if the page token marker is a full URL, such as anextLink
attribute returned by Azure ARM services.
For get_page_marker exactly one of body_path
or header
must be set. For set_page_marker exactly one of query
, header
, body_field
, or uri
must be set.
Many AWS API requests contain a variable in the response body called PageToken
. This variable should be set as a HTTP query parameter NextToken
to subsequent requests. AWS supports both XML and JSON encoding. The datasource definition has an encoding
of xml
below so an XPath expression is used for the body_path.
pagination "aws_pagination_xml" do
get_page_marker do
body_path xpath(response, "//PageToken")
end
set_page_marker do
query "NextToken"
end
end
This is the same paginator except for JSON data.
pagination "aws_pagination_json" do
get_page_marker do
body_path jmes_path(response, "PageToken")
end
set_page_marker do
query "NextToken"
end
end
The following service has a X-Page-Token header that it both returns and expects:
pagination "my_service" do
get_page_marker do
header "X-Page-Token"
end
set_page_marker do
header "X-Page-Token"
end
end
The following service contains a nextLink
parameter in the body that is a full URL to the next page of results. It is assumed that the datasource will specify we want JSON encoding.
pagination "azure_arm_pagination_json" do
get_page_marker do
body_path jmes_path(response, "nextLink")
end
set_page_marker do
uri true
end
end
The following service contains a Link
header that includes the full URL to the next page of results.
pagination "github_api" do
get_page_marker do
header "Link"
end
set_page_marker do
uri true
end
end
The following service is an example of an AWS service where the NextPageToken
is retrieved and set in the body.
pagination "aws_body_pagination" do
get_page_marker do
body_path jmes_path(response, "NextPageToken")
end
set_page_marker do
body_field "NextPageToken"
end
end
Some APIs use page numbers rather than a token, this is a case where the power of jq
comes in handy as it can express mathematical concepts. Here is an example where the JSON body contains the current page number, the number of items per page, and the total number of items:
{
"paging": {
"pageIndex": 1,
"pageSize": 100,
"total": 381
},
...
}
In this case, you would define the following pagination with a jq
expression which will return the next page number unless we have reached the last page:
pagination "sonarqube_pagination" do
get_page_marker do
body_path jq(response, "if .paging.pageIndex * .paging.pageSize < .paging.total then .paging.pageIndex + 1 else null end")
end
set_page_marker do
query "p"
end
end
There are also APIs that use page numbers without specifying the page number in the body. In these cases, you can take advantage of the $marker
variable which is passed in when a jq
expression is evaluated for a pagination. This $marker
variable contains the string value of the previous pagination marker (for the first page, the previous page marker is an empty string: ""
). Here is an example of the JSON that one of these APIs might have:
{
"items": [
...
]
}
Here is a pagination that will work with this API given that there are 500 items per page:
pagination "500_items_per_page_pagination" do
get_page_marker do
body_path jq(response, "if .items | length < 500 then null else if $marker != "" then $marker | tonumber + 1 else 2 end end")
end
end
Just like the previous pagination, this one also uses null
to signify that we have reached the last page.
Request
The syntax of a datasource that retrieved data from an HTTP API is:
datasource <name> do
request do
auth $<credentials reference OR auth definition>
pagination $<pagination definition>
host <host>
verb ["GET"|"POST"] # defaults to "GET"
scheme ["http"|"https"] # defaults to "https"
path <path>
ignore_status [<http status code>, <http status code>...]
query <query string name>, <query string value>
query <query string name>, <query string value>
...
header <header name>, <header value>
header <header name>, <header value>
...
body_field <body field name>, <body field value>
body_field <body field name>, <body field value>
...
body <body value>
end
result do
encoding ["json"|"xml"|"text"]
[collect jmes_path(response, <jmes_path>) do]
[collect jq(response, <jq>) do]
[collect xpath(response, <xpath>) do]
field <field name>, <term>
field <field name>, <term>
...
[end]
end
end
Where:
<name>
is the name of the datasource.<auth definition>
is the name of the credentials reference or auth definition that describes how to authenticate requests made to the API.<pagination definition>
is the name of the pagination definition that describes how to iterate through the response pages if any.<host>
is the API hostname.<verb>
is the HTTP request method and defaults to"GET"
.<path>
is the HTTP path<ignore_status>
are http status codes to ignore failure on. Normally any status code other than 200-299 will cause the policy to stop evaluation and return a failure. Supply a list of values or a list parameter such as [403, 404] to cause the policy to treat these calls as an empty dataset and continue running.<query string name>
and<query string value>
describe the request query string elements if any.<header name>
and<header value>
describe any additional headers you wish to send with the API request.<body field name>
and<body field value>
describe the body JSON object fields if any. When abody_field
is specified,body
may not be specified as well.<body value>
describes the entire body. Whenbody
is specified, nobody_field
s may be specified as well.
Note: only JSON encoding is supported in the REQUEST body at this time when using
body_field
; however, other encodings can be sent usingbody
.
Result
The result
property details how to parse the HTTP response to produce the resulting data. The properties are:
encoding
: specifies the response encoding, must be"json"
,"xml"
, or"text"
. With the"text"
encoding the body of the response will be returned as a string or an array of strings if multiple requests are made (either in the case of iteration or pagination).field
: identifies a field in the resulting data. The provided term is evaluated to initialize the field value. Typically the term is either thejmes_path
,jq
, orxpath
function.
result
may also make use of collect
to iterate over the response elements. collect
accepts a term and a block. The term must be jmes_path
, jq
, or xpath
. The term must return an array which collect
iterates over. Each element of the array can then be used to define the data fields. The current element is accessed using the col_item
reserved word as shown in the example below.
datasource "volumes_us_east" do
request do
auth $cred_aws_us_east_1
pagination $aws_pagination_xml
host "ec2.amazonaws.com"
query "Action", "DescribeVolumes"
query "Filter.1.Name", "encrypted"
query "Filter.1.Value.1", "false"
header "User-Agent" "My-app"
end
result do
encoding "xml"
collect xpath(response, "/DescribeVolumesResponse/volumeSet/item") do
field "id", xpath(col_item, "volumeId")
field "region", "us-east-1"
field "size", xpath(col_item, "size")
field "type", xpath(col_item, "volumeType")
field "tag_set" do
collect xpath(item, "tagSet") do
field "key", xpath(col_item, "key")
field "value", xpath(col_item, "value")
end
end
end
end
end
Processing Datasources With JavaScript
Datasources may use JavaScript to combine and transform data from one or more datasources to create a new datasource. The syntax used in this case is:
datasource <name> do
run_script $<script>[, <term>]*
end
Where <script>
is the name of script definition and the terms are parameters or datasources given to the script for execution. The script result defines the data returned by the datasource. Terms can be any type (including other datasources, which will show up as arrays of JavaScript Objects).
The JavaScript Datasource supports the JavaScript ECMAScript 5/ES 5 Specification. Please review the reference when writing a JavaScript Datasource.
The JavaScript Datasource includes the underscore library. This library includes some useful functions to assist in managing the data objects passed into the datasource. Version 1.4.4 of the library is currently included.
Scripts are defined using script definitions with the following syntax:
script <name>, type: "javascript" do
parameters <string literal>[, <string literal]*
result <string literal>
code <string literal>
end
parameters
is the list of parameters the script accepts. The parameters will automatically be set as JavaScript variables at the start of script execution.
result
identifies the name of the JavaScript variable used to extract the data returned by the script. result
is required.
code
contains the actual JavaScript code. code
is required.
Example:
datasource "merge_instances" do
run_script $merge, $instances_us_east, $instances_us_west
end
script "merge", type: "javascript" do
parameters "instances_us_east", "instances_us_west"
result "res"
code <<-EOS
res = []
// Concatenate the different arrays of instances
var instances = []
instances = instances_us_east.concat(instances_us_west)
// Build the array of json objects containing the instance information as described above.
var server_json = {}
for (var i = 0; i < instances.length; i++) {
var instance = instances[i]
var name = instance["name"]
var state = instance["state"]
var href = instance["href"]
var tags = instance['tags']
// build the json object
server_json = { "name": name, "href": href, "state": state, "tags": tags }
// push object onto the output array
res.push(server_json)
}
EOS
end
For more complicated examples of JavaScript datasources including scripts that combine together multiple datasources see policy examples such as the open ports policy
Chaining Datasources
A Datasource describes one type of API call. However, sometimes multiple levels of requests need to be made if you have a resource that is a subresource to another resource. For example, a policy validating that there are no publicly accessible S3 buckets must first fetch all the bucket names then fetch the ACLs for each bucket. In this case a datasource
definition can reference a resources
definition or another datasource
definition. The syntax used to refer to a resources
definition is @<resource definition name>
and the syntax used to refer to another datasource definition is $<datasource definition name>
. For example:
resources "clouds", type: "rs_cm.clouds"
resources "instances", type: "rs_cm.instances" do
iterate @clouds # iterate through the data retrieved by the
# "clouds" resource definition.
cloud_href href(iter_item) # iter_item returns the cloud data currently
# being iterated on.
end
Note:
iterate
may appear only once in a given datasource definition.
As shown in the example above references are typically used together with the iterate
reserved word to iterate over the elements of the data. If the data is not an array then iterate
takes care of wrapping it with a single element array.
References may also be used directly as argument of other functions such as val
, href
, size
or select
.
Finally, references can be used when defining the parameters given to run_script
:
datasource "permissions" do
run_script "get_permissions", @instances, @security_groups
end
Describing the Policy Conditions
Now that we know how to retrieve the data the next step consists of actually writing the conditions they must validate. This is done in the policy
definition. There can be only one such definition in a given policy template.
A policy definition consists of a list of validations. Each validation may in turn describe multiple checks. A validation also defines one or more escalations that trigger when a check fails and resolutions that trigger when an underlying issue is fixed. Finally a validation also provides a default summary and details text templates used to render the incident message.
Each validate
or validate_each
is run independently and will generate either 0 or 1 incidents.
The syntax for a policy definition is:
policy <string literal> do
validate[_each] $<datasource>|@<resources> do
summary_template <string literal>
detail_template <string literal>
hash_include <field_name>, <field_name>, ...
hash_exclude <field_name>, <field_name>, ...
escalate $<escalation>
escalate $<escalation>
resolve $<resolution>
resolve $<resolution>
...
check <term>
check <term>
...
export <path expression> do
resource_level <boolean>
field <name> do
label <string literal>
format <string literal>
path <path expression>
end
field ...
end
end
validate...
end
Where:
- Each validation starts with
validate
orvalidate_each
.validate
applies the checks on the given datasource or resources as a whole.validate_each
iterates over the given datasource or resources (which must be an array or is wrapped into a single element array) and applies the checks on each element.
summary_template
provides a text template that gets applied to the escalation data to render the incident message summary.detail_template
provides a text template that gets applied to the escalation data to render the incident message details. It will be displayed above the export table, if one is specified.hash_include
is array of fields in the escalation data to check in determining whether data has changed and thus actions should be re-run. By default, all fields are checked so if any value changes at all, all actions are run again. This includes emails and cloud workflows. In general, this field does not have to be specified. The general exception is when you have a value such as a timestamp that changes constantly.hash_exclude
is an array of fields in the escalation data to exclude in determining whether data hash changed. This field is mutually exclusive with hash_include.escalate
indicates an escalation to trigger when a check fails.resolve
indicates an resolution to trigger when all existing violations are resolved.check
identifies a term that must return anything BUTfalse
,0
, an empty string, an empty array or an empty object. If the term returns one of these values then the check fails, an incident is created and any associated escalation triggers.export
controls whether or not a table of resources is exported for the incident.path expression
is a string literal corresponding to a jmes_path expression acting upon the violation data. The jmespath can be used to extract a table of resources if the resources exist as a subpath in data. This field is optional.resource_level
is a boolean stating if the data being exported is resource level data or not. If the data is resource level, available actions can be run on a select group of resources or all of them.field
specifies a field in the data, such as id. Each field corresponds to a column in the data table. Fields values should be simple types such as integers, strings, booleans, or arrays of simple values.name
is the object field key/name in the violationdata
rowlabel
is a human readable label associated with the name and shows up as the header for the column. If omitted,name
will be used.format
controls formatting for the column. Currentlyleft
,center
, andright
keywords are supported. By default, columns are left formatted.path
is a string literal corresponding to a jmes_path expression acting upon each resource. The jmespath can be used to extract a field from a embedded data structure or to rename a field. By default,name
is used.
The policy engine runs each check in order and stops when a check fails
. A check fails if the corresponding term returns false
, 0
, an empty string, an empty array or an empty object. In the case of validate_each
the policy engine applies that algorithm for each element of the datasource or resources.
Each time a check fails the corresponding data is added to the violation data. In the case of validate
this can only happen once and thus the escalation data ends up being the validated datasource or resources. In the case of validate_each
this means that only the elements that fail a check are added to the escalation data.
The violation data is exported as a table in the incident view page.
Example:
Assume that the $reservations
datasource has data like:
[
{
"account": {
"id": 1,
"name": "my account"
},
"region": "us-west-1",
"instance_type": "m1.small",
"instance_count": 10,
"end_time": "2020-01-01 01:02:03",
"time_left": 10200
},
...
]
policy "ri_expiration" do
validate_each $reservations do
summary_template "Reserved instances are nearing expiration."
detail_template <<-EOS
Found {{ len data }} expired reservations in account id {{ rs_project_id }}
EOS
export do
field "account_name" do
label "Account Name"
path "account.name"
end
field "account_id" do
label "Account ID"
path "account.id"
end
field "region" do
label "Region"
end
field "instance_type" do
label "Instance Type"
end
field "instance_count" do
label "Instance Count"
end
field "end_time" do
label "End Time"
end
field "time_left" do
label "Time Left In Seconds"
format "right"
end
end
hash_include "id", "end_time"
escalate $alert
check gt(dec(to_d(val(item, "end_time")), now), 3*24*3600))
end
end
In the example above the policy defines a single validation with a single check. The check returns a boolean value which is false when the duration between a reserved instance expiration data and now is less than 3 days. In this case the alert
escalation triggers. The violation data consists of an array that contains all the reservations that are expiring in less than 3 days.
A table of information is defined to display in the mail as well as display on the incident show page in the dashboard.
Triggering Actions
Actions are run anytime the underlying violation data changes. By default, all fields are used in determining whether the data changes. In the case above, the time_left field will be continually changing and causing actions like email to retrigger. hash_include
and hash_exclude
can be used to modify this behavior by excluding certain fields form this calculation. By supplying id
and end_time
to the hash_include method, we ensure that we only get new alerts when one of these two values is changed. We could have also achieved the same ends by doing hash_exclude "time_left"
as well -- all other fields in the datasource be relatively stable.
Only top-level fields will be considered for hash_include
and hash_exclude
. If you have a nested structure such as:
[
{
"id": "abc",
"config": {
"foo": "bar",
"baz": "biz"
}
}
]
Then you may specify hash_include "config"
to detect any changes in config but not individual fields within config itself. If you wish to hash only a specific field within config such as config.foo, then use a JavaScript based script
block to transform the nested fields into top-level fields first.
Escalations
Escalation definitions describe a sequence of action items to be taken when a policy fails to validate. Action items are run in order and the escalation will run until all actions are complete or until an error is encountered running one of the action items. Action items are describe below. Action items may appear any number of times in any order, except when noted.
Syntax:
escalation <name> do
automatic <term>
label <string literal>
description <string literal>
parameter <parameter definition 1>
parameter <parameter definition 2>
parameter ...
<action item 1>
<action item 2>
<action item 3>
<action item ...>
end
name
is the name of the escalation. It may be referenced in validate and validate_each blocks byescalate $<name>
.label
gives a human readable label for the escalation.description
is optional and should give a description of what will happen when the action is run.automatic
controls whether the escalation is automatically run when an incident is generated, or is manually run. Automatic can be true or false, or a function that evaluates to true or false. If unspecified, it defaults to true for backwards compatibility. Functions used can operate on parameter to the applied policy in order to make whether actions are automatically run configurable.parameter
can appear any number of times. parameters are optional parameters to inject into the request approval. They can be referenced by subsequent actions such as Cloud Workflows or emails within the same escalation. See the Parameters section for more information about defining parameters.
Action Items
email
causes an email to be sent to the given address or addresses.
email <addresses> do
subject_template <subject template>
body_template <body template>
end
addresses
can be a string, an array of strings corresponding to email addresses. It can also be parameter or function which evaluates to a string or array of strings.subject template
must be a go template that is evaluated to be the email subject line. If no subject template is provided, the incident summary is used.body template
must be a go template that is evaluated to be the email body. If no body template is provided, the incident detail is used.
Request Approval
request_approval
creates an approval request, causing the escalation to pause and wait for manual input. The manual input can either be an approval or denial. If its an approval, actions after the request approval will be taken. If its a denial, no further actions will be taken. When applying a policy, policy managers can choose to skip all approvals in the policy using the Skip Approvals
option
request_approval do
label <string literal>
description <string literal>
parameter <parameter definition 1>
parameter <parameter definition 2>
parameter ...
end
label
gives a human readable label for the request approval.description
is optional and should give a description of what will happen when the action is approved.parameters
are optional parameters to inject into the request approval. They can be referenced by subsequent cloud workflows or emails within the same escalation. See the Parameters section for more information.
Cloud Workflow
run
launches a Cloud Workflow
using the given RCL definition. The RCL definition may be given parameters
specified in a comma separated list. Parameters may be references or any of
the applicable functions.
run <rcl definition name>[, <parameter>]*
parameter
can be any term. It may be a reference to a datasource, a parameter, a literal value of any type, a function, or a reserved keyword such asdata
.
Cloud Workflow variables
Besides parameters passed explicitly in the define
declaration, a handful of variables are available automatically:
Variable | Type | Value |
---|---|---|
@@account |
Integer | The RightScale project you're currently running in, i.e. 60073 |
$$cred_* |
Credentials reference | Any credentials or auth declaration is exported based upon name. For example if you have a declaration credentials "cred_azure" do... it will be available for signing http_* requests as $$cred_azure . Note this differs slightly from the reference name in datasource declarations where the reference name would be $cred_azure . For an example, see custom policy |
Examples
Here is an example employing all previously described actions. It does not run automatically. It first optionally sends an email to a third party asking them to look at the approval request that will be created. If the approval request is approved, a Cloud Workflow is run in order to delete the volumes.
# RCL used to delete volumes using Cloud Management.
define run_delete_volumes($data) do
# loop through each data item
foreach $volume in $data do
# get the volume resource
@volume = rs_cm.get(href: $volume['href'])
#delete the volume
@volume.delete
end
end
escalation "delete_volumes_with_approval" do
automatic false
label "Deleted Volumes"
description "Delete the selected volumes"
parameter "param_email" do
type "string"
label "Person to notify of action"
description "If this is not-empty, a notification email will be sent to the following address."
end
email $param_email do
subject_template "Approval request: delete unencrypted volumes found in project {{ rs_project_name }}"
body_template <<-EOS
Unencrypted Volumes
The following volumes are tagged with one of the tags shown below and are unencrypted:
{{ range $index, $tag := parameters.enforced_tags }}{{ if $index }}, {{ end }}{{ $tag }}{{ end }}
{{ range data }}
| Region | Volume ID | Volume Tags |
| ------------ | --------- | ----------- |
| {{.region }} | {{.id}} | {{.tag_set}} |
{{ end }}
We would like your approval to delete the following volumes, please approve or deny this request.
[JIRA Ticket]({{ parameters.jira_url }}/{{ data.jira_ticket_id }})
EOS
request_approval do
label "Delete volumes"
description "Approving this will delete the listed volumes."
end
run "run_delete_volumes", data, $param_top_level, rs_project_id
end
Resolutions
Resolutions definitions describe a sequence of action items to be taken when a failing policy is resolved. A policy is resolved when the underlying checks which caused the policy to go into a failed state have been been corrected, either by escalation actions or by manual intervention. Resolutions may not happen as soon as the condition is fixed; conditions are checked on a schedule (typically 15 minutes) chosen when the policy is applied. An applied policy may be manually run by selecting Run Now
on the Applied Policies page for a given account.
Syntax:
resolution <name> do
automatic <term>
label <string literal>
description <string literal>
parameter <parameter definition 1>
parameter <parameter definition 2>
parameter ...
<action item 1>
<action item 2>
<action item 3>
<action item ...>
end
name
is the name of the resolution. It may be referenced in validate and validate_each blocks byresolve $<name>
.label
gives a human readable label for the resolution.description
is optional and should give a description of what will happen when the action is run.automatic
controls whether the resolution is automatically run when an incident is resolved, or is manually run. Automatic can be true or false, or a function that evaluates to true or false. If unspecified, it defaults to true for backwards compatibility. Functions used can operate on parameter to the applied policy in order to make whether actions are automatically run configurable.parameter
can appear any number of times. parameters are optional parameters to inject into the request approval. They can be referenced by subsequent actions such as Cloud Workflows or emails within the same resolution. See the Parameters section for more information about defining parameters.
Action Items
Action items for resolutions follow the exact same format as action items for escalations. email
, request_approval
, and run
are supported.
Examples
resolution "handle_unencrypted_volumes" do
label "Email resolution"
description "Email the resolution report to the email list"
email $escalate_to do
subject_template "Unencrypted volumes resolved in project {{ rs_project_name }}"
body_template <<-EOS
Unencrypted volumes have all been encrypted or deleted.
EOS
end
Incident Message and Email Templates
The Policy Template Language makes it possible to use text templates to define the content of messages shown to users when an incident is created both in the UI and in emails.
Templates have access to the escalation data via the built-in data
function. They also have access to the policy template parameter values via the parameters
function. Other functions listed below provide access to contextual information such as the RightScale organization and project. The output of the template is interpreted as markdown before being rendered into HTML.
The complete list of functions available to templates is:
data
: The policy escalation data.parameters
: The applied policy parameter values as a hash.rs_org_id
: The RightScale organization ID where the policy is applied.rs_org_name
: The RightScale organization name where the policy is applied.rs_project_id
: The RightScale project ID where the policy is applied.rs_project_name
: The RightScale project name where the policy is applied.rs_cm_host
: The RightScale Cloud Management API hostname.rs_ss_host
: The RightScale Self-Service API hostname.rs_optima_host
: The RightScale Optima API hostname.
Text Template Syntax Overview
Control structures in templates are delimited by {{
and }}
. For example the following template:
"Project {{ rs_project_id }}: {{ rs_project_name }}"
produces Project 123: foo
where 123
is the ID of the RightScale project and foo
is its name.
If a control structure left delimiter is followed immediately by a minus sign and space character ({{-
), all trailing white space is trimmed from the immediately preceding text. Similarly, if the right delimiter is preceded by a space and minus sign (-}}
), all leading white space is trimmed from the immediately following text. The following template produces the same output as the previous one:
<<EOS
Project
{{- rs_project_id }}:
{{- rs_project_name }}
EOS
Elements of an object or hash are accessed using .
, for example the following prints the value of the field name
in the escalation data:
"{{ data.name }}"
If the escalation data is an array then the values can be iterated over using the function range
:
<<EOS
{{ range data -}}
* "{{ .name }}"
{{ end -}}
EOS
Note how the fields of the current element are accessed using .<name-of-field>
. range
supports an alternative syntax that makes it possible to access the index and the current element explicitly:
<<EOS
{{ $index, $elem := range data -}}
{{ $index }}. "{{ $elem.name }}"
{{ end -}}
EOS
You may want to include your result in a table. Below is an example how to build a table from the data array.
# If a value is passed as first parameter of export, we'll extract that subpath into a table.
export "reportData" do
# resource_level is false by default
# if true, there must be an "id" field and it must be unique
resource_level true
field "id" do
# label is for display purposes, if left blank the field name (in this case "id") will be used
label "Billing Center ID"
end
field "name" do
label "Billing Center Name"
end
field "budget" do
label "Budget"
end
field "actual" do
label "Month to Date Spend"
# format tells the UI how the field should be displayed. It can be semantic
format "currency"
# or css style directive
format "right $%.2f"
end
end
Control structures may also test the value of data for conditional output, for example:
<<EOS
{{- if .data.email }}
Email: {{ data.email }}
{{- end }}
EOS
A condition is true if it evaluates to anything other than false, 0, an empty string, array or data structure. There is also a set of binary comparison operators defined as functions:
eq
returns true if arg1 == arg2ne
returns true if arg1 != arg2lt
returns true if arg1 < arg2le
returns true if arg1 <= arg2gt
returns true if arg1 > arg2ge
returns true if arg1 >= arg2
Reserved Word Reference
The reserved words are defined below
rs_org_id
rs_org_id
returns the numerical identifier for RightScale Organization, that the account belongs to which is currently running the policy. It can be used any place a term can be used.
rs_org_name
rs_org_name
returns the name of the RightScale Organization Name that the account belongs, which is currently running the policy. It can be used any place a term can be used.
rs_project_name
rs_project_name
returns the name of the RightScale account that is currently running the policy. It can be used any place a term can be used.
rs_project_id
rs_project_id
returns the numerical identifier of the RightScale account that is currently running the policy. It can be used any place a term can be used.
rs_cm_host
rs_cm_host
returns the RightScale CloudManagement endpoints.
rs_optima_host
rs_optima_host
returns the RightScale Optima endpoint.
rs_ss_host
rs_ss_host
returns the RightScale Self-Service endpoints.
data
data
is used in a policy validate to refer to the whole datasource structure.
item
item
is used in a policy validate_each to refer to each item element in the datasource
response
response
is used to refer to the response XML or JSON data in a datasource result
iter_item
iter_item
is used to refer to each item when doing iterate in a resource or datasource
iter_index
iter_index
is the index of each item when doing iterate in a resource or datasource
col_item
col_item
is used to refer to each item when doing a collect in a datasource
col_index
col_index
is the index of each item when doing a collect in a datasource
Resource Reference
The following table lists all the resources supported by the policy template language. For each resource the table lists the available filters as well as other available properties. Required properties are marked with (*)
.
Resource | Filters | Properties | Docs |
---|---|---|---|
rs_cm.alert | alert_spec_href status |
view | Alerts |
rs_cm.alert_spec | description escalation_name name subject_href |
instance_href server_href server_array_href server_template_href view with_inherited |
AlertSpecs |
rs_cm.cloud | cloud_type description name |
view | Clouds |
rs_cm.credential | description name |
view | Credentials |
rs_cm.datacenter | name resource_uid |
view | Datacenters |
rs_cm.deployment | description name resource_group_href server_tag_scope |
cloud_href(*) view |
Deployments |
rs_cm.image | cpu_architecture description image_type name os_platform resource_uid visibility |
cloud_href(*) view |
Images |
rs_cm.instance | datacenter_href deployment_href name os_platform parent_href placement_group_href private_dns_name private_ip_address public_dns_name public_ip_address resource_uid server_template_href state |
cloud_href(*) view |
Instances |
rs_cm.instance_type | cpu_architecture description name resource_uid |
cloud_href(*) view |
InstanceTypes |
rs_cm.ip_address | deployment_href name |
cloud_href(*) | IpAddresses |
rs_cm.network | cidr_block cloud_href deployment_href name resource_uid |
view | Networks |
rs_cm.network_gateway | cloud_href network_href name |
NetworkGateways | |
rs_cm.placement_group | cloud_href deployment_href name state |
view | PlacementGroups |
rs_cm.resource_group | cloud_href name state |
ResourceGroups | |
rs_cm.route_table | cloud_href name network_href |
view | RouteTables |
rs_cm.routes | cloud_href description network_href next_hop_href next_hop_ip next_hop_type next_hop_url state |
route_table_href(*) | Routes |
rs_cm.security_group | deployment_href name network_href resource_uid |
cloud_href(*) | SecurityGroups |
rs_cm.security_group_rule | security_group_href view |
SecurityGroupRules | |
rs_cm.server | cloud_href deployment_href name |
Servers | |
rs_cm.server_array | cloud_href deployment_href name |
view | ServerArrays |
rs_cm.ssh_key | resource_uid name |
cloud_href(*) view |
SshKeys |
rs_cm.subnet | datacenter_href instance_href resource_uid name network_href visibility |
cloud_href(*) | Subnets |
rs_cm.user | email first_name last_name |
Users | |
rs_cm.volumes | datacenter_href deployment_href resource_uid name description parent_volume_snapshot_href |
cloud_href(*) view |
Volumes |
rs_cm.volume_attachment | instance_href resource_uid volume_href |
cloud_href(*) view |
VolumeAttachments |
rs_cm.volume_snapshot | state deployment_href resource_uid name description parent_volume_snapshot_href visibility |
cloud_href(*) view |
VolumeSnapshots |
rs_cm.volume_type | name resource_uid |
cloud_href(*) view |
VolumeTypes |
Functions
Functions make it possible to compute values dynamically, for example by concatenating multiple values together or extracting sub-text from an API response. The syntax for calling a function is:
<name>(<term>, <term>, …)
For example:
href(col_item)
A function may have zero or more parameters. The function reference page lists all the functions available for use in policy templates. The list describes the parameters and return values as well as where the function may be used.