Skip to main content

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.

info

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 address
  • module: resource module
  • type: resource type
  • name: resource name
  • action: actions to be taken by Terraform on the resource
  • context: resource values contributing to the violation

The metadta.rule object fields are defined as:

  • id: unique identifier for the rule
  • title: rule title
  • description: rule details
  • rule_link: link to the rule in the Styra Docs
  • severity: rule severity, either low, medium, high, or critical
  • impact: explanation of rule violation impact
  • remediation: instructions to remediate rule violations
  • resource_category: categorization of the Terraform resource type
  • control_category: categorization of the rule
  • compliance_pack: rule association with compliance packs, if any
  • platform: returns "terraform" for all Terraform rules
  • provider: associated Terraform provider for the rule
  • rule_targets: the Terraform objects evaluated by the rule
  • parameters: user-specified rule configuration parameters, if any
note

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 with rules.rego located in the policy >> aws >> ec2 sub-folder.
  • package policy.azure.vms with rules.rego located in the policy >> azure >> vms sub-folder.
  • package policy.gcp.compute with rules.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).

note

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.