Terraform Policy Authoring
Policy authoring with Terraform systems allows you to write Rego rules which are evaluated against the contents of the Terraform plan JSON file. This page assumes you are familiar with the OPA concepts of packages and the rules they contain as well as the Styra DAS Policy Lifecycle.
Terraform plans enable you to see what changes Terraform needs to make to cloud resources before it makes them. The policies you define in Styra DAS determine whether the changes in the plan are safe to make. The basic rules that you write are partial-set rules that either reject or monitor a request. The Terraform system type supports the following entry points for policy evaluation: enforce
and deny
to reject a Terraform resource change, and monitor
and warn
to provide a warning about a Terraform resource change.
Each of the partial sets contain either a single string indicating the message to return to the user or an object which includes the message
field. Styra-built policy library rules also return an additional metadata
field in the decision object, which is detailed below. The object can also include custom user-defined fields by editing the Rego code directly. Styra DAS will always add "allowed": false
to the returned decision object if not explicitly defined.
Terraform policy rules which return evaluation decisions must be defined in policy packages which follow the naming format package policy.<provider>.<resource>
. See the Policy Structure section below for additional details.
Rule Formats
The following rule formats are supported when defining policies for a Terraform system.
Enforce - Results in a policy failure.
enforce[decision] {
...
decision := {
"allowed": false,
"message": "Message explaining why there is a problem",
"metadata": {}
}
}
Monitor - Results in a policy warning.
monitor[decision] {
...
decision := {
"allowed": false,
"message": "Message explaining why there is a problem",
"metadata": {}
}
}
The optional metadata
object above is intended to provide additional context for each Terraform rule violation, including the context of the rule itself and the context of the resource resulting in the violation. The Styra DAS pre-built Terraform Policy Library Rules include this metadata
object with populated resource
and rule
objects (KICS library rules do not currently include the full populated contents of the metadata
object). Below is an example of the metadata
object for the AWS: EC2: Restrict instances with unapproved AMIs
rule.
{
"resource": {
"module": "root_module",
"type": "aws_instance",
"address": "aws_instance.example_instance",
"name": "example_instance",
"action": "create",
"context": {
"ami": "ami-0022c769"
}
},
"rule": {
"id": "",
"title": "AWS: EC2: Restrict instances with unapproved AMIs",
"severity": "high",
"resource_category": "",
"control_category": "",
"rule_link": "https://docs.styra.com/systems/terraform/snippets",
"compliance_pack": null,
"description": "Require EC2 instances to use an AMI from a pre-approved list",
"impact": "Running an unapproved AMI may result in using vulnerable images",
"remediation": "Launch instances using only AMI images defined in the pre-approved list",
"platform": "terraform",
"provider": "aws",
"rule_targets": [
{
"scope": "resource",
"service": "ec2",
"name": "instance",
"identifier": "aws_instance",
"argument": "ami"
},
{
"scope": "resource",
"service": "ec2",
"name": "launch_template",
"identifier": "aws_launch_template",
"argument": "image_id"
},
{
"scope": "resource",
"service": "autoscaling_group",
"name": "launch_configuration",
"identifier": "aws_launch_configuration",
"argument": "image_id"
}
],
"parameters": {
"allowed_ami_ids": ["ami-830c94e3"]
}
}
}
The metadata.resource
object fields are defined as:
address
: the full resource addressmodule
: resource moduletype
: resource typename
: resource nameaction
: actions to be taken by Terraform on the resourcecontext
: resource values contributing to the violation
The metadta.rule
object fields are defined as:
id
: unique identifier for the ruletitle
: rule titledescription
: rule detailsrule_link
: link to the rule in the Styra Docsseverity
: rule severity, either low, medium, high, or criticalimpact
: explanation of rule violation impactremediation
: instructions to remediate rule violationsresource_category
: categorization of the Terraform resource typecontrol_category
: categorization of the rulecompliance_pack
: rule association with compliance packs, if anyplatform
: returns "terraform" for all Terraform rulesprovider
: associated Terraform provider for the rulerule_targets
: the Terraform objects evaluated by the ruleparameters
: user-specified rule configuration parameters, if any
The metadata
object in the returned decision is only available in the Styra DAS Terraform system type v2. The id
, resource_category
, control_category
, compliance_pack
, impact
, and remediation
fields may be null
or blank as rules in the Styra DAS policy library get updated with this additional data in upcoming updates.
Additional Supported Formats
For compatibility reasons, the following rule formats are also supported, though we recommend you define rules using the formats in the Rule Formats section above.
Deny - Results in a policy failure.
deny[decision] {
...
decision := {
"message": "Message explaining why there is a problem"
}
}
deny[message] {
...
message := "Message explaining why there is a problem"
}
Warn - Results in a policy warning.
warn[decision] {
...
decision := {
"message": "Message explaining why there is a problem"
}
}
warn[message] {
...
message := "Message explaining why there is a problem"
}
Policy Package and Module Structure
Styra DAS Terraform system policies are organized based on the provider and the resources/services within that provider (i.e., package policy.<provider>.<resource>
) to mirror the provider >> resource structure of Terraform. This structure also helps organize your policy rules according to the resources/services they impact. For example, policy rules relating to AWS S3 buckets should reside within modules in the policy.aws.s3
package. You can create multiple policy modules within each package for further organization. For example, the policy.aws.s3
package could include encryption.rego
, versioning.rego
, and replication.rego
.
While policy rules may be placed at any level within the policy hierarchy (e.g., a policy.utils
package with helpers), only the enforce/deny and monitor/warn rules in modules following the package policy.<provider>.<resource>
hierarchy will be applied to Terraform plans.
For example, the enforce rule in the first example policy below will be applied to Terraform plans while the following two monitor and enforce rules will not as they reside in packages which do not follow the policy.<provider>.<resource>
hierarchy.
package policy.myprovider.myresouce
# This enforce rule will be applied to Terraform plan evaluation
enforce[decision] {
...
}
package policy.myprovider
# This monitor rule will not be applied to Terraform plan evaluation
monitor[decision] {
...
}
package policy.myprovider.myresouce.utils
# This enforce rule will not be applied to Terraform plan evaluation
enforce[decision] {
...
}
The structure of policies within Styra DAS Stacks is the same as the structure of policies within Styra DAS Systems. Rules you add to packages following the stacks.<stackID>.policy.<provider>.<resource>
hierarchy at the stack level are applied to Terraform plans of associated Terraform systems.
The names of provider
and resource
in the package path may be any values useful to you to organize your rules (e.g., policy.foo.bar
), however, the aws
, azure
, and gcp
provider names are meaningful in the Styra DAS UI as they enable displaying rules associated with those providers from Styra's Terraform Policy Library within the policy builder UI (accessible via the Add rule button in the policy editor).
Default Terraform Policy Packages and Modules
Upon creation of a new Styra DAS Terraform system, three policy packages and modules are automatically added to your system to get you started and to provide a policy organization guide. These default packages and modules are:
package policy.aws.ec2
withrules.rego
located in the policy >> aws >> ec2 sub-folder.package policy.azure.vms
withrules.rego
located in the policy >> azure >> vms sub-folder.package policy.gcp.compute
withrules.rego
located in the policy >> gcp >> compute sub-folder.
When editing these policy modules in Styra DAS, usage of Styra's Terraform Policy library is enabled. You can add rules from the policy library by clicking the Add rule button when editing policies.
Add additional modules to the default policy packages by clicking on the options menu on the policy.<provider>
folder and selecting Add Policy. Define a new or existing package path (e.g., kms
if adding KMS policies to the policy.aws
package) and define a module name.
If not needed for your use case, delete any default packages and modules. If needed in the future, manually add them using Add Policy.
Custom Terraform Policy Packages and Modules
Create custom policy packages and modules to meet your specific Terraform use cases, whether for Terraform providers other than AWS, Azure, or Google Cloud, or for policies relating to Terraform Cloud or Terraform Enterprise runs.
For example, for Terraform systems integrated with Terraform Cloud/Enterprise, create a policy which prevents Friday deploys via Terraform Cloud/Enterprise by defining rules in a policy.tfc.runs
package:
package policy.tfc.runs
enforce[decision] {
message := "No deployments allowed on Fridays"
time.weekday(time.now_ns()) == "Friday"
decision := {
"allowed": false,
"message": message
}
}
Terraform Policy Tests and Data Sources
Test policies can be placed anywhere within a Styra DAS Terraform system, however, Styra typically recommends you put them in separate packages so that they are not downloaded by the OPA agent as part of the policy bundle, helping to reduce the bundle size.
Data sources can also be added to your Terraform system. Data sources provide a dedicated location for injecting external data which can be used as part of policy evaluation (e.g., an LDAP data source with user roles and permissions).
Data sources cannot be placed at a path which is a prefix of a policy package path or another data source path.
Styra's Terraform Policy Library
Styra's growing Terraform Policy Library includes pre-built rules for AWS, Azure, and Google Cloud to help organizations follow IaC best practices. Rules from the policy library can be added via the Styra DAS policy builder UI by clicking the Add rule button when viewing/editing a policy module with a package prefix of policy.<provider>
, where <provider>
is aws
, azure
, or gcp
.
For example, a pre-built rule which does not require any configuration options, such as requiring S3 buckets to be encrypted, would be added to your policy file like so:
enforce[decision] {
data.global.systemtypes["terraform:2.0"].library.provider.aws.s3.unencrypted.v1.unencrypted_s3_bucket[violation]
decision := {
"allowed": false,
"message": violation.message,
"metadata": violation.metadata
}
}
The list of current pre-built rules can also be found on the Terraform Policy Library Rules page.
For information on writing custom policies, see the Styra Academy or the Open Policy Agent policy language documentation.