Managing cloud infrastructure on AWS has become the standard for businesses of all sizes. As organizations grow, the scale and complexity of their AWS environments can expand exponentially. Infrastructure as Code (IaC) tools like Terraform have revolutionized this space, allowing teams to provision and manage resources declaratively and repeatably. However, this speed and automation introduce a new set of challenges: How do you ensure that every provisioned resource adheres to security best practices, compliance standards, and internal cost controls? Manual reviews are slow, error-prone, and simply cannot keep pace. This is the governance gap where combining Terraform and Sentinel provides a powerful, automated solution, enabling organizations to scale with confidence.
This article provides a comprehensive guide to implementing Policy as Code (PaC) using HashiCorp’s Sentinel within a Terraform workflow for AWS. We will explore why this approach is critical for modern cloud operations, walk through practical examples of writing and applying policies, and discuss best practices for integrating this framework into your organization to achieve secure, compliant, and cost-effective infrastructure automation.
Table of Contents
Understanding Infrastructure as Code with Terraform on AWS
Before diving into policy enforcement, it’s essential to grasp the foundation upon which it’s built. Terraform, an open-source tool created by HashiCorp, is the de facto standard for IaC. It allows developers and operations teams to define their cloud and on-prem resources in human-readable configuration files and manage the entire lifecycle of that infrastructure.
What is Terraform?
At its core, Terraform enables you to treat your infrastructure like software. Instead of manually clicking through the AWS Management Console to create an EC2 instance, an S3 bucket, or a VPC, you describe these resources in a language called HashiCorp Configuration Language (HCL).
- Declarative Syntax: You define the desired end state of your infrastructure, and Terraform figures out how to get there.
- Execution Plans: Before making any changes, Terraform generates an execution plan that shows exactly what it will create, update, or destroy. This “dry run” prevents surprises and allows for peer review.
- Resource Graph: Terraform builds a graph of all your resources to understand dependencies, enabling it to provision and modify resources in the correct order and with maximum parallelism.
- Multi-Cloud and Multi-Provider: While our focus is on AWS, Terraform’s provider-based architecture allows it to manage hundreds of different services, from other cloud providers like Azure and Google Cloud to SaaS platforms like Datadog and GitHub.
How Terraform Manages AWS Resources
Terraform interacts with the AWS API via the official AWS Provider. This provider is a plugin that understands AWS services and their corresponding API calls. When you write HCL code to define an AWS resource, you are essentially creating a blueprint that the AWS provider will use to make the necessary API requests on your behalf.
For example, to create a simple S3 bucket, your Terraform code might look like this:
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "data_storage" {
bucket = "my-unique-app-data-bucket-2023"
tags = {
Name = "My App Data Storage"
Environment = "Production"
ManagedBy = "Terraform"
}
}
Running terraform apply
with this configuration would prompt the AWS provider to create an S3 bucket with the specified name and tags in the us-east-1
region.
The Governance Gap: Why Policy as Code is Essential
While Terraform brings incredible speed and consistency, it also amplifies the impact of mistakes. A misconfigured module or a simple typo could potentially provision thousands of non-compliant resources, expose sensitive data, or lead to significant cost overruns in minutes. This is the governance gap that traditional security controls struggle to fill.
Challenges of IaC at Scale
- Configuration Drift: Without proper controls, infrastructure definitions can “drift” from established standards over time.
- Security Vulnerabilities: Engineers might unintentionally create security groups open to the world (0.0.0.0/0), launch EC2 instances from unapproved AMIs, or create public S3 buckets.
- Cost Management: Developers, focused on functionality, might provision oversized EC2 instances or other expensive resources without considering the budgetary impact.
- Compliance Violations: In regulated industries (like finance or healthcare), infrastructure must adhere to strict standards (e.g., PCI DSS, HIPAA). Ensuring every Terraform run meets these requirements is a monumental task without automation.
- Review Bottlenecks: Relying on a small team of senior engineers or a security team to manually review every Terraform plan creates a significant bottleneck, negating the agility benefits of IaC.
Policy as Code (PaC) addresses these challenges by embedding governance directly into the IaC workflow. Instead of reviewing infrastructure after it’s deployed, PaC validates the code before it’s applied, shifting security and compliance “left” in the development lifecycle.
A Deep Dive into Terraform and Sentinel for AWS Governance
This is where HashiCorp Sentinel enters the picture. Sentinel is an embedded Policy as Code framework integrated into HashiCorp’s enterprise products, including Terraform Cloud and Terraform Enterprise. It provides a structured, programmable way to define and enforce policies on your infrastructure configurations before they are ever deployed to AWS.
What is HashiCorp Sentinel?
Sentinel is not a standalone tool you run from your command line. Instead, it acts as a gatekeeper within the Terraform Cloud/Enterprise platform. When a terraform plan
is executed, the plan data is passed to the Sentinel engine, which evaluates it against a defined set of policies. The outcome of these checks determines whether the terraform apply
is allowed to proceed.
Key characteristics of Sentinel include:
- Codified Policies: Policies are written in a simple, logic-based language, stored in version control (like Git), and managed just like your application or infrastructure code.
- Fine-Grained Control: Policies can inspect the full context of a Terraform run, including the configuration, the plan, and the state, allowing for highly specific rules.
- Enforcement Levels: Sentinel supports multiple enforcement levels, giving you flexibility in how you roll out governance.
Writing Sentinel Policies for AWS
Sentinel policies are written in their own language, which is designed to be accessible to operators and developers. A policy is composed of one or more rules, with the main rule determining the policy’s pass/fail result. Let’s explore some practical examples for common AWS governance scenarios.
Example 1: Enforcing Mandatory Tags
Problem: To track costs and ownership, all resources must have `owner` and `project` tags.
Terraform Code (main.tf
):
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0" # Amazon Linux 2 AMI
instance_type = "t2.micro"
# Missing the required 'project' tag
tags = {
Name = "web-server-prod"
owner = "dev-team@example.com"
}
}
Sentinel Policy (enforce-mandatory-tags.sentinel
):
# Import common functions to work with Terraform plan data
import "tfplan/v2" as tfplan
# Define the list of mandatory tags
mandatory_tags = ["owner", "project"]
# Find all resources being created or updated
all_resources = filter tfplan.resource_changes as _, rc {
rc.change.actions contains "create" or rc.change.actions contains "update"
}
# Main rule: This must evaluate to 'true' for the policy to pass
main = rule {
all all_resources as _, r {
all mandatory_tags as t {
r.change.after.tags[t] is not null and r.change.after.tags[t] is not ""
}
}
}
How it works: The policy iterates through every resource change in the Terraform plan. For each resource, it then iterates through our list of `mandatory_tags` and checks that the tag exists and is not an empty string in the `after` state (the state after the plan is applied). If any resource is missing a required tag, the `main` rule will evaluate to `false`, and the policy check will fail.
Example 2: Restricting EC2 Instance Types
Problem: To control costs, we want to restrict developers to a pre-approved list of EC2 instance types.
Terraform Code (main.tf
):
resource "aws_instance" "compute_node" {
ami = "ami-0c55b159cbfafe1f0"
# This instance type is not on our allowed list
instance_type = "t2.xlarge"
tags = {
Name = "compute-node-staging"
owner = "data-science@example.com"
project = "analytics-poc"
}
}
Sentinel Policy (restrict-ec2-instance-types.sentinel
):
import "tfplan/v2" as tfplan
# List of approved EC2 instance types
allowed_instance_types = ["t2.micro", "t3.small", "t3.medium"]
# Find all EC2 instances in the plan
aws_instances = filter tfplan.resource_changes as _, rc {
rc.type is "aws_instance" and
(rc.change.actions contains "create" or rc.change.actions contains "update")
}
# Main rule: Check if the instance_type of each EC2 instance is in our allowed list
main = rule {
all aws_instances as _, i {
i.change.after.instance_type in allowed_instance_types
}
}
How it works: This policy first filters the plan to find only resources of type `aws_instance`. It then checks if the `instance_type` attribute for each of these resources is present in the `allowed_instance_types` list. If a developer tries to provision a `t2.xlarge`, the policy will fail, blocking the apply.
Sentinel Enforcement Modes
A key feature for practical implementation is Sentinel’s enforcement modes, which allow you to phase in governance without disrupting development workflows.
- Advisory: The policy runs and reports a failure, but it does not stop the Terraform apply. This is perfect for testing new policies and gathering data on non-compliance.
- Soft-Mandatory: The policy fails and stops the apply, but an administrator with the appropriate permissions can override the failure and allow the apply to proceed. This provides an escape hatch for emergencies.
- Hard-Mandatory: The policy fails and stops the apply. No overrides are possible. This is used for critical security and compliance rules, like preventing public S3 buckets.
Implementing a Scalable Policy as Code Workflow
To effectively use Terraform and Sentinel at scale, you need a structured workflow.
- Centralize Policies in Version Control: Treat your Sentinel policies like any other code. Store them in a dedicated Git repository. This gives you version history, peer review (via pull requests), and a single source of truth for your organization’s governance rules.
- Create Policy Sets in Terraform Cloud: In Terraform Cloud, you create “Policy Sets” by connecting your Git repository. You can define which policies apply to which workspaces (e.g., apply cost-control policies to development workspaces and stricter compliance policies to production workspaces). For more information, you can consult the official Terraform Cloud documentation on policy enforcement.
- Iterate and Refine: Start with a few simple policies in `Advisory` mode. Use the feedback to educate teams on best practices and refine your policies. Gradually move well-understood and critical policies to `Soft-Mandatory` or `Hard-Mandatory` mode.
- Educate Your Teams: PaC is a cultural shift. Provide clear documentation on the policies, why they exist, and how developers can write compliant Terraform code. The immediate feedback loop provided by Sentinel is a powerful teaching tool in itself.
Frequently Asked Questions
Can I use Sentinel with open-source Terraform?
No, Sentinel is a feature exclusive to HashiCorp’s commercial offerings: Terraform Cloud and Terraform Enterprise. For a similar Policy as Code experience with open-source Terraform, you can explore alternatives like Open Policy Agent (OPA), which can be integrated into a custom CI/CD pipeline to check Terraform JSON plan files.
What is the difference between Sentinel policies and AWS IAM policies?
This is a crucial distinction. AWS IAM policies control runtime permissions—what a user or service is allowed to do via the AWS API (e.g., “This user can launch EC2 instances”). Sentinel policies, on the other hand, are for provision-time governance—they check the infrastructure code itself to ensure it conforms to your organization’s rules before anything is ever created in AWS (e.g., “This code is not allowed to define an EC2 instance larger than t3.medium”). They work together to provide defense-in-depth.
How complex can Sentinel policies be?
Sentinel policies can be very sophisticated. The Sentinel language, detailed in the official Sentinel documentation, supports functions, imports for custom libraries, and complex logical constructs. You can write policies that validate network configurations across an entire VPC, check for specific encryption settings on RDS databases, or ensure that load balancers are only exposed to internal networks.
Does Sentinel add significant overhead to my CI/CD pipeline?
No, the overhead is minimal. Sentinel policy checks are executed very quickly on the Terraform Cloud platform as part of the `plan` phase. The time taken for the checks is typically negligible compared to the time it takes Terraform to generate the plan itself. The security and governance benefits far outweigh the minor increase in pipeline duration.

Conclusion
As AWS environments grow in scale and complexity, manual governance becomes an inhibitor to speed and a source of significant risk. Adopting a Policy as Code strategy is no longer a luxury but a necessity for modern cloud operations. By integrating Terraform and Sentinel, organizations can build a robust, automated governance framework that provides guardrails without becoming a roadblock. This powerful combination allows you to codify your security, compliance, and cost-management rules, embedding them directly into your IaC workflow.
By shifting governance left, you empower your developers with a rapid feedback loop, catch issues before they reach production, and ultimately enable your organization to scale its AWS infrastructure securely and confidently. Start small by identifying a critical security or cost-related rule in your organization, codify it with Sentinel in advisory mode, and begin your journey toward a more secure and efficient automated cloud infrastructure.Thank you for reading the DevopsRoles page!