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"
)
namedefines the policy template name. It must be unique in the project.rs_pt_verindicates the policy template language version. The value must be20180301.typemust be"policy"short_descriptionprovides a short description of what the policy does.
The following definitions are optional:
default_frequencysets 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_descriptionis a long description of what the policy does.severityindicates the policy severity, it must be one of"low"(default),"medium","high"or"critical".categoryis an arbitrary value used to group policies into categories, for example"Cost","Compliance"or"Security".infospecifies 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
tenancyspecifies 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 acredentialdeclaration in it, onlysingleis 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
namedefines the resource name and must be unique. It can be referenced using @elsewhere in the code. resource typeis the type of resource.filteris 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 comparatoris optional and must be eitherne:oreq:. If not specified, it defaults toeq:.filter valueis 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.propertyis a property of the resource such ascloud_hreforview.property valueis 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
nameis the name in the policy template language. The credential is referenceable by this name indatasourceanddefinedeclarations.schemesis the authorizationschemein 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.labelis a short human readable label for the credential -- it is shown to the user on the Apply Policy screendescriptionis a longer description of what the credential is used for in the policy and is also shown to the user on the Apply Policy screentagsis 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 keyproviderfor 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 thepaginationfield of datasource request blocks with a$in front.<path term>is a call to either thejmes_path,jq, orxpathmethod 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.urishould be set to true if the page token marker is a full URL, such as anextLinkattribute 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_fieldis specified,bodymay not be specified as well.<body value>describes the entire body. Whenbodyis specified, nobody_fields 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, orxpathfunction.
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:
iteratemay 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
validateorvalidate_each.validateapplies the checks on the given datasource or resources as a whole.validate_eachiterates 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_templateprovides a text template that gets applied to the escalation data to render the incident message summary.detail_templateprovides 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_includeis 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_excludeis an array of fields in the escalation data to exclude in determining whether data hash changed. This field is mutually exclusive with hash_include.escalateindicates an escalation to trigger when a check fails.resolveindicates an resolution to trigger when all existing violations are resolved.checkidentifies 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.exportcontrols whether or not a table of resources is exported for the incident.path expressionis 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_levelis 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.fieldspecifies 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.nameis the object field key/name in the violationdatarowlabelis a human readable label associated with the name and shows up as the header for the column. If omitted,namewill be used.formatcontrols formatting for the column. Currentlyleft
,center
, andright
keywords are supported. By default, columns are left formatted.pathis 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,nameis 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
nameis the name of the escalation. It may be referenced in validate and validate_each blocks byescalate $<name>.labelgives a human readable label for the escalation.descriptionis optional and should give a description of what will happen when the action is run.automaticcontrols 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.parametercan 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
addressescan 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 templatemust 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 templatemust 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
labelgives a human readable label for the request approval.descriptionis optional and should give a description of what will happen when the action is approved.parametersare 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>]*
parametercan 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
nameis the name of the resolution. It may be referenced in validate and validate_each blocks byresolve $<name>.labelgives a human readable label for the resolution.descriptionis optional and should give a description of what will happen when the action is run.automaticcontrols 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.parametercan 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:
eqreturns true if arg1 == arg2nereturns true if arg1 != arg2ltreturns true if arg1 < arg2lereturns true if arg1 <= arg2gtreturns true if arg1 > arg2gereturns 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.