HashiCorp Sentinel Policies 3rd Gen (Part 2 of 3)
How to write third-generation Sentinel Policies for Terraform (TF) Cloud
Disclaimer: this is a paid feature and offering of TF Cloud, hence information is scarce about Sentinel in the internet, hence I would like to do my part to help people to learn it easier.
Prerequisites:
- Terraform Cloud setup with a proper workspace for making infrastructure mocks
- Terraform Cloud central configuration to be set to point to a repository of Sentinel Policies
Example UI policy check upon a Terraform Plan run on the TF cloud

You may refer to some of the examples found in this repository
To start off, you need to create a repository to create mock data for testing infrastructure as code. For e.g an EBS Volume
resource "aws_ebs_volume" "example" {
availability_zone = "ap-southeast-1"
size = 40
encrypted = true
tags = {
managed_by = "mock_infra"
environment = "Staging"
Project = "EcsEc2"
}
}
With that, in a folder where the Terraform code is linked to a workspace, run terraform plan
. A link will be generated from the Terminal to bring you to the TFC UI. With that, at the bottom left hand corner of the page’s “Plan Finished”, you would find a button called “Download Sentinel Mocks”

A tar file will be downloaded, the file looks something run-asdfasf123-sentinel-mocks.tar.gz. After we unzip the file, this is the file we need to look for: mock-tfplan-v2.sentinel
All sentinel policy checking is done against the plan data found in this file. We use this file to create pass and fail cases.
Writing the Sentinel Policy and its repository
First, we need to be the admin for TFC and set the sentinel policy checking to point to a repository.
This repository would then contain a main sentinel.hcl
file. We can also clone this repository from https://github.com/hashicorp/learn-sentinel-policies
The repository structure consists of the main policies in the root directory and the main sentinel.hcl
file to determine which policies to enforce and what enforcement level. Eg. below
policy "ebs-volume-must-be-encrypted" {
source = "./ebs-volume-must-be-encrypted.sentinel"
enforcement_level = "advisory"
}
We will need to add sentinel modules which provides helper functions in navigating through the Terraform Plan data:
module "tfplan-functions" {
source = "./common-functions/tfplan-functions/tfplan-functions.sentinel"
}
module "tfstate-functions" {
source = "./common-functions/tfstate-functions/tfstate-functions.sentinel"
}
module "tfconfig-functions" {
source = "./common-functions/tfconfig-functions/tfconfig-functions.sentinel"
}
module "aws-functions" {
source = "./aws-functions/aws-functions.sentinel"
}
Here I provide an example of enforcing the EBS volumes to be encrypted
ebs-volume-must-be-encrypted.sentinel
# This policy uses the Sentinel tfplan/v2 import to require that
# all EBS volumes should be encrypted
# Import common-functions/tfplan-functions/tfplan-functions.sentinel
# with alias "plan"
import "tfplan-functions" as plan
# Get all EBS Volumes
allEBSVolumes = plan.find_resources("aws_ebs_volume")
# Get all Launch Templates
allLaunchTemplates = plan.find_resources("aws_launch_template")
# Filter to EBS volumes that are not encrypted
# Warnings will be printed for all violations since the last parameter is true
nonEncryptedEBSVolumes = plan.filter_attribute_is_not_value(allEBSVolumes,
"encrypted",
true, true)
nonEncryptedEBSVolumesInLaunchTemplates = plan.filter_attribute_is_not_value(allLaunchTemplates,
"block_device_mappings.0.ebs.0.encrypted",
"true", true)
# Main rule
validated = length(nonEncryptedEBSVolumes["messages"]) is 0 and length(nonEncryptedEBSVolumesInLaunchTemplates["messages"]) is 0
main = rule {
validated is true
}
Testing the policy
We will first need to create a test
folder in the root repository, followed by a folder having the same name as the policy ebs-volume-must-be-encrypted
Within this folder again we create a folder called test-data
- This is where we store the Sentinel mocks provided at the first steps shown above (we can copy all the sentinel mock files into it)
Inside ebs-volume-must-be-encrypted
folder, we write the test cases. For e.g.
fail.hcl and pass.hcl
These test cases files look similar to the main sentinel file determined at the root level but using the test
keyword instead. Example below of fail.hcl:
module "tfplan-functions" {
source = "../../common-functions/tfplan-functions/tfplan-functions.sentinel"
}
module "tfconfig-functions" {
source = "../../common-functions/tfconfig-functions/tfconfig-functions.sentinel"
}
module "aws-functions" {
source = "../../aws-functions/aws-functions.sentinel"
}
mock "tfplan/v2" {
module {
source = "./test-data/mock-tfplan-fail.sentinel"
}
}
mock "tfconfig/v2" {
module {
source = "./test-data/mock-tfconfig.sentinel"
}
}
test {
rules = {
main = false
}
pass.hcl
module "tfplan-functions" {
source = "../../common-functions/tfplan-functions/tfplan-functions.sentinel"
}
module "tfconfig-functions" {
source = "../../common-functions/tfconfig-functions/tfconfig-functions.sentinel"
}
module "aws-functions" {
source = "../../aws-functions/aws-functions.sentinel"
}
mock "tfplan/v2" {
module {
source = "./test-data/mock-tfplan-pass.sentinel"
}
}
mock "tfconfig/v2" {
module {
source = "./test-data/mock-tfconfig.sentinel"
}
}
test {
rules = {
main = true
}
}
If you observe you would see that only the main
value and the tfplan/v2
‘s source attribute being different.
If your mock data has the EBS volume encrypted, then the plan data would be in the pass.hcl reference. Either we can manually modify the plan data, or we can use the mock infra repo’s code to generate another plan data that fails the policy check to be used for fail.hcl reference.
With all the files above in place. Sentinel Policy checking would automatically be activated on each terraform plan that runs on TFC.
With this, I conclude the part two of the series, in the next part I will share more about publishing the policies to the public registries.
I welcome feedback on sharing more examples on Sentinel Policy writing. Thanks for reading!