Terraform
Write a Sentinel policy for a Terraform deployment
When writing a Sentinel policy, you can validate your policy's restrictions against Sentinel imports, which access mock data. Sentinel can use several types of imports from the HCP Terraform API: configuration, plan, state, and run.
In this tutorial, you will review a Sentinel policy and test it in the Sentinel CLI using pre-generated mock import data. The example policy enforces EC2 instance type and tag restrictions.
Prerequisites
For this tutorial, you will need:
Clone example Terraform configuration
In your terminal, clone the example code
repository. This
repository contains an example Sentinel policy and a tfplan/v2 Sentinel mock
import.
$ git clone https://github.com/hashicorp-education/learn-sentinel-write-policy
Navigate to the directory.
$ cd learn-sentinel-write-policy
Review the tfplan/v2 import mock data structure
You will use the tfplan/v2 import type to test your policy. This import type
provides access to Terraform plan data, which represents the changes that
Terraform needs to make to infrastructure to match the written configuration.
The tfplan import lets you determine if the proposed changes satisfy
your policy criteria.
Open the file named mock-tfplan-v2.sentinel in your text editor. Find the
resource_changes collection. This Terraform data is a key/value collection
for all of the proposed resource changes required by your configuration. The data below is
truncated, but your file should contain this collection with these values.
mock-tfplan-v2.sentinel
terraform_version = "1.0.1"
## ...
resource_changes = {
"aws_instance.ubuntu": {
"address": "aws_instance.ubuntu",
"change": {
"actions": [
"create",
],
"after": {
"ami": "ami-0fb1e27304d83032f",
"credit_specification": [],
"disable_api_termination": null,
"ebs_optimized": null,
"get_password_data": false,
"hibernation": null,
"iam_instance_profile": null,
"instance_initiated_shutdown_behavior": null,
"instance_type": "t2.micro",
"monitoring": null,
"source_dest_check": true,
"tags": {
"Name": "Provisioned by Terraform",
},
"timeouts": null,
"user_data": null,
"user_data_base64": null,
"volume_tags": null,
},
## ...
},
"deposed": "",
"index": null,
"mode": "managed",
"module_address": "",
"name": "ubuntu",
"provider_name": "registry.terraform.io/hashicorp/aws",
"type": "aws_instance",
},
## ...
}
Terraform captures the attributes of any created or modified resource in the plan. You can use this data in your Sentinel policies to verify that the policy appropriately restricts resources or attributes.
When testing a policy, select the appropriate import for your policy's
restrictions. For example, you can use a tfrun import to test a policy that
restricts resources based on cost estimation. You can use a tfconfig/v2
import to test a policy that limits the number of providers to use in
configuration.
Define a mock in Sentinel
Now open the sentinel.hcl file and review the configuration.
mock "tfplan/v2" {
module {
source = "mock-tfplan-v2.sentinel"
}
}
This defines a new
mock named
tfplan/v2 containing the data from the mock-tfplan-v2.sentinel file. Your
Sentinel policy will import this mock to test the policy.
Review your policy
Sentinel tests your requirements against your imported data, resulting in a
Pass or Fail. When writing policies, you can use
variables to store
values and
operators
to compare logical expressions.
Open the restrict-aws-instances-type-and-tag.sentinel file, which contains
the Sentinel policy.
This Sentinel policy enforces the following infrastructure requirements:
- EC2 instances must have a
Nametag. - EC2 instances must be of type
t2.micro,t2.small, ort2.medium.
If your EC2 instances do not meet all of these criteria, Sentinel will flag the
run with a FAIL.
restrict-aws-instances-type-and-tag.sentinel
# Imports mock data
import "tfplan/v2" as tfplan
# Get all AWS instances from all modules
ec2_instances = filter tfplan.resource_changes as _, rc {
rc.type is "aws_instance" and
(rc.change.actions contains "create" or rc.change.actions is ["update"])
}
# Mandatory Instance Tags
mandatory_tags = [
"Name",
]
# Allowed Types
allowed_types = [
"t2.micro",
"t2.small",
"t2.medium",
]
# Rule to enforce "Name" tag on all instances
mandatory_instance_tags = rule {
all ec2_instances as _, instance {
all mandatory_tags as mt {
instance.change.after.tags contains mt
}
}
}
# Rule to restrict instance types
instance_type_allowed = rule {
all ec2_instances as _, instance {
instance.change.after.instance_type in allowed_types
}
}
# Main rule that requires other rules to be true
main = rule {
(instance_type_allowed and mandatory_instance_tags) else true
}
This Sentinel policy can be divided into seven sections.
The
importstatement imports the mock data, defined insentinel.hcl. This mock data simulates a Terraform plan that provisions an EC2 instance.The
ec2_instancesvariable filters the mock data for EC2 instances that the plan will create or modify.Sentinel determines the specific resources or data to evaluate from the import based on a
filter expression. In the above policy,ec2_instancesuses afilterexpression. The expression returns a map oftfplan.resource_changes: a selector that searches thetfplandata collection for a field calledresource_changesfor EC2 instances created or updated in the plan data.The
mandatory_tagsvariable contains the list of required tags.The
allowed_typesvariable contains the list of permitted EC2 instances types.The
mandatory_instance_tagsrule ensures that EC2 instances have aNametag. The rule loops through the filteredec2_instancesand verifies that all EC2 instances have the tags defined in themandatory_tagsvariable.mock-tfplan-v2.sentinel
resource_changes = { "aws_instance.ubuntu": { "address": "aws_instance.ubuntu", "change": { ## ... "after": { ## .. "tags": { "Name": "Provisioned by Terraform", }, ## .. } ## ... }, ## ... }, }The
instance_type_allowedrule ensures that EC2 instances aret2.micro,t2.small, ort2.medium. The rule loops through the filteredec2_instancesand checks whether the instance'sinstance_typeisintheallowed_types.The
mainrule evaluates both themandatory_instance_tagsandinstance_type_allowedrules. If both are true, Sentinel allows the policy to pass. The Sentinel policy divides rules this way to keep the main rule short and allows you to evaluate your policy based on multiple rule criteria.
Run your policy in the Sentinel CLI
Now, use the apply command to test your policy against the mock data and
ensure your policy performs as expected.
$ sentinel apply restrict-aws-instances-type-and-tag.sentinel
Pass - restrict-aws-instances-type-and-tag.sentinel
The execution passed because the infrastructure changes in your plan data satisfy your policy criteria.
Next steps
You reviewed the parts of a Sentinel policy and used mock data to validate a policy. To learn more about Sentinel, review the following resources:
- Learn how to Generate Mock Policy Data
- Review how to Test a Sentinel Policy
- Learn how to Upload a Sentinel Policy Set to HCP Terraform
- Learn more about the different Terraform-specific
importtypes.