Terraform remains one of the most popular infrastructure-as-code (IaC) tools in the DevOps and platform engineering ecosystem. Although it solves many infrastructure automation problems, critical gaps remain in orchestration and guardrails.
Terraform guardrails are the policies, checks, and controls you put in place to ensure infrastructure changes are safe, compliant, and intentional before they’re ever applied.
In this article, we will explore common types of guardrails, how to implement them, and best practices for Terraform guardrails.
What we’ll cover:
What do guardrails mean in Terraform?
In Terraform, guardrails are automated checks and controls that validate your infrastructure code against a set of predefined rules before they are applied. They ensure your organization’s rules are enforced, so you will have fewer surprises when your infrastructure is deployed.
Every time a pull request is created or a branch is pushed, you can replace manual review with automated checks that run whenever someone proposes a change. This will help you catch problems that human reviewers can miss, or in some cases, they might not be aware of what’s allowed and what isn’t.
Guardrails can be used in multiple stages of your workflow. Some of them can run on your local machine before you commit any code, others are integrated into your pipelines and run every time you open a pull request/push a branch, and some can even run before you apply your changes, blocking deployments that break your policies.
Common types of Terraform guardrails
The guardrails you implement depend on where in your workflow risk is most likely to surface.
1. Security
Security guardrails should be the first ones to implement because a simple misconfigured S3 bucket, an open security group, or a database without encryption can expose your entire organization.
Some common security guardrails include:
- Blocking publicly accessible buckets
- Enforcing at-rest encryption for databases
- Requiring HTTPS on load balancers
- Applying least privilege to all IAM roles
- Ensuring security groups do not allow unrestricted ingress from 0.0.0.0/0.
2. Compliance
If your organization operates under frameworks such as SOC 2, HIPAA, PCI DSS, or GDPR, you will need guardrails that enforce these compliance requirements in your code.
In this case, you will need to implement things like: every resource should have an Owner, Environment, and CostCenter tag, and be deployed only in approved regions.
You also need to make sure that your sensitive data stores meet retention and encryption standards.
Open Policy Agent (OPA) is often used by organizations to implement compliance-related guardrails. With OPA, you get platform-specific policy tools that enable centralized, reusable policy definitions to be applied across Terraform, OpenTofu, Kubernetes, and others.
3. Cost controls
Your infrastructure costs can change without notice. For example, you can accidentally change an instance type from t3.micro to a c5.xlarge one, so now your costs are twenty times bigger. Using cost guardrails helps you prevent these situations.
You can integrate tools like Infracost into your Terraform workflow to estimate the monthly (or even hourly) spend for each change. This tool gives you a better overview and control of your monthly budget. Read more: How to use Terraform with Infracost.
For example, you can set a guardrail that requires manual approval for any change that increases monthly costs by more than 30%, or that blocks anyone from provisioning GPU instances without explicit sign-off.
4. Operational safety
Operational safety guardrails help teams avoid accidentally deleting resources, restrict changes to specific environments, enforce resource sizing limits, and prevent misconfigurations. For example, they will prevent you from mistakenly deleting a database in a production environment by
allowing Terraform to block these changes before they are applied. They reduce human error, prevent mistakes that will incur extra costs, and protect uptime and reliability.
How to implement Terraform guardrails
Let’s look at some of the common ways you can implement Terraform guardrails:
Policy as code
You can implement Terraform guardrails by leveraging policy-as-code (PaC) tools. The industry standard for this is Open Policy Agent (OPA), which uses the Rego language to define policies.
The learning curve for the Rego language can be steep, but it enables you to define policies for anything from restricting certain resources or certain resource types to controlling your workflow approvals.
Here is an example that restricts EC2 instances that have an instance type different from t2.micro:
resource "aws_instance" "this" {
ami = "ami-0c02fb55956c7d316"
instance_type = "t3.micro"
}The above resource is an EC2 instance that uses a t3.micro type.
Now, let’s define an OPA policy that restricts instances that don’t use a t2.micro type:
package terraform.aws.ec2
deny contains msg if {
resource := input.resource_changes[_]
resource.type == "aws_instance"
instance_type := resource.change.after.instance_type
instance_type != "t2.micro"
msg := sprintf("EC2 instance '%s' uses disallowed instance type '%s'. Only 't2.micro' is permitted.", [resource.address, instance_type])
}To test it, we will need to run a Terraform plan, save it to a json file, and then use the OPA CLI to evaluate the policy against it:
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
opa eval -i plan.json -d policy.rego "data.terraform.aws.ec2.deny"This is the output:
{
"result": [
{
"expressions": [
{
"value": [
"EC2 instance 'aws_instance.this' uses disallowed instance type 't3.micro'. Only 't2.micro' is permitted."
],
"text": "data.terraform.aws.ec2.deny",
"location": {
"row": 1,
"col": 1
}
}
]
}
]
}If we change the instance to t2.micro and recreate the plan, we will see that our policy permits the creation of this resource:
{
"result": [
{
"expressions": [
{
"value": [],
"text": "data.terraform.aws.ec2.deny",
"location": {
"row": 1,
"col": 1
}
}
]
}
]
}You should always use OPA policies in your CI/CD pipelines or in the infrastructure orchestration platform you use.
Pre-commit and CI checks
Pre-commit and CI checks are powerful mechanisms for implementing shift-left security. This means all security checks take place before deployment, ensuring the code you deploy is always up to standard, rather than checking for these constraints after your infrastructure is deployed.
Here’s an example pre-commit file that can help you verify:
- Terraform format
- Terraform validate
- TFlint
- Check your policies
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.96.2
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_tflint
- repo: local
hooks:
- id: opa-fmt
name: OPA format
language: system
entry: opa fmt --write
files: \.rego$
- id: opa-check
name: OPA check
language: system
entry: opa check
files: \.rego$If you want to commit your files, the pre-commit check will automatically evaluate. You must ensure that all its conditions pass before you can do the commit:
git commit -m 'Initial commit'
[WARNING] Unstaged files detected.
[INFO] Stashing unstaged files to /Users/flavius/.cache/pre-commit/patch1773841816-27371.
Terraform fmt............................................................Passed
Terraform validate.......................................................Passed
Terraform validate with tflint...........................................Failed
- hook id: terraform_tflint
- exit code: 2Because one of the hooks is failing in this case, we can’t commit my changes. The commit is successful once everything is fixed:
git commit -m 'Initial commit'
Terraform fmt............................................................Passed
Terraform validate.......................................................Passed
Terraform validate with tflint...........................................Passed
OPA format...............................................................Passed
OPA check................................................................Passed
[main (root-commit) 54e006a] Initial commitPre-commit hooks can also be evaluated in your CI pipelines, as you can leverage them as a step in your CI pipeline. It is a best practice to use these hooks on every pull request created against your main branch to ensure that, even if a developer bypasses the local pre-commit hooks, the CI pipeline catches the violation before the code can be merged.
In pre-commit, you can also use tools such as Checkov or Trivy to scan for security misconfigurations, making it a compelling option for your CI checks.
Workflow controls and approvals
You should always implement guardrails in your workflows to avoid vulnerable or untested code being deployed directly into your production environments.
Implementing shift-left security in your CI with pre-commit hooks or by using tools like Checkov, Trivy, or Tflint standalone is a best practice for catching issues before they reach your main branch, but it’s not enough.
Even if your changes do not create vulnerabilities, you should understand the impact of your changes. Before deploying your code to production, ensure you add a manual approval step before triggering the deployment. As a best practice for Terraform, your CI pipeline should finish with a Terraform plan written in the pull request as a comment, so other engineers can take a look and understand the impact of that particular change.
For example, a misconfigured CIDR block in a security group might be a valid change that passes all your CI checks and OPA policies, but this can still open your network to unintended access. The human review process will catch what automated tools can’t.
In addition, you should always leverage drift detection and remediation because even with a solid CI/CD pipeline or an infrastructure orchestration platform in place, manual changes can still occur. By running periodic drift detection, you can capture these manual changes and decide what to do about them (incorporate them into your code or revert to the version you have in your code).
Best practices for effective Terraform guardrails
Here are some of the best practices you should take advantage of when you are implementing Terraform guardrails:
- Start in advisory mode: Do not begin by deploying hard-blocking policies, as you can frustrate your engineers. You should start with policies that only issue warnings and log violations, without blocking anything. After your team understands the rules and the false-positive rate is low, you can proceed to enforcement.
- Keep policies close to the code: Store your policy files in a VCS system (it can be the same repository as your Terraform code, or in a dedicated policy repo). Keep your PR process in place for making changes to your policies, too, so you know when a change is done and by whom.
- Use modules as guardrails: You can create pre-approved Terraform modules that automatically include your security, tagging, and compliance requirements. For example, every time one of your developers wants to use your vpc module, they will automatically receive the correct CIDR ranges and network ACLs. They cannot skip those steps because they are part of the module.
- Tag your resources: Make tagging mandatory for every resource; they should have at a minimum Owner, Environment, ManagedBy, and CostCenter. Tagging your resources will help you with cost allocation, compliance reporting, and incident response.
- Prepare your defenses in layers: Because no tool catches everything, you should understand that a mix of tools is needed in order to implement proper defenses. You can use pre-commit hooks for fast local feedback, or even in your CI, to get security checks, validation, policy checks, and standard enforcement.
- Secure your state file: Because it contains sensitive information (Database passwords, ARNs, IDs, secrets, IP addresses, and more), you need to encrypt it at rest and never commit it to version control. You can take advantage of native state encryption if you are using OpenTofu. If you are using Terraform, do not forget to store it remotely in an S3 bucket (or equivalent) with versioning and encryption enabled, and strict IAM policies to access it.
- Measure and iterate continuously: You should monitor how many policy violations are detected, which rules are triggered most often, and how long it takes to resolve issues. All the above data will help you identify which are your biggest risk areas and where guardrails need to be refined.
How to implement Terraform guardrails with Spacelift
Spacelift is an infrastructure orchestration platform that gives you centralized governance and visibility across Terraform, OpenTofu, Pulumi, CloudFormation, Ansible, Kubernetes, and other infrastructure tools.
When it comes to implementing Terraform guardrails, Spacelift can help you with:
- Policy as code: Spacelift supports PaC through OPA, and you build policies to restrict certain resources and certain resource parameters, require multiple approvals for runs, control what happens when a PR is merged, and where to send notifications
- Drift detection and remediation: With Spacelift, you get out-of-the-box drift detection and remediation, so that you can easily detect changes that were made outside of your IaC processes and remediate them with a click of a button
- Lifecycle hooks: Spacelift lets you control what happens before and after each runner phase, so you can easily embed any guardrail tool into your workflow. You can also leverage Spacelift’s built-in plugins for this (there are plugins available for Checkov, Trivy, Wiz, and others)
- Self-service: You can implement self-service using Blueprints and Templates, making it easy to enforce all the guardrails you need. Spacelift also integrates with Backstage and ServiceNow, making it easy for your developers to self-serve infrastructure from the tools they already know and use
- AI-powered diagnostics: Spacelift Intelligence adds an AI layer that helps you troubleshoot failed runs, understand error context, and get operational insight across your infrastructure workflows
If you want to see how Spacelift can help you implement Terraform guardrails, book a demo with one of our engineers.
Key points
Implementing guardrails for Terraform helps you prevent infrastructure misconfigurations before they become security breaches. They protect resources from accidental changes, prevent cost surprises, block vulnerabilities, and enforce regulatory requirements.
By leveraging static analysis tools like Trivy and Checkov, linters such as tflint, policy-as-code with OPA, and an infrastructure orchestration platform like Spacelift, you can easily shift left and reduce incidents in your production environments.
Manage Terraform better with Spacelift
Orchestrate Terraform workflows with policy as code, programmatic configuration, context sharing, drift detection, resource visualization, and more.
Frequently asked questions
What are Terraform guardrails?
Terraform guardrails are policies and constraints that prevent misconfigurations and enforce compliance before infrastructure is provisioned. They typically take the form of policy-as-code frameworks like HashiCorp Sentinel or Open Policy Agent (OPA), which evaluate Terraform plans against predefined rules and block non-compliant changes.
What’s the difference between policy-as-code and static scanning?
Policy-as-code enforces organizational rules (access controls, compliance requirements) by evaluating infrastructure definitions against codified policies before or during deployment. Static scanning analyzes code or configuration files for known vulnerabilities, misconfigurations, or anti-patterns without executing them.
How do guardrails differ from policy-as-code?
Guardrails enforce boundaries on what infrastructure can or cannot do (e.g., blocking noncompliant resources before deployment), while policy-as-code defines those rules programmatically in version-controlled files.
Where should guardrails run?
Guardrails should run as close to the model call as possible, ideally at the API gateway or orchestration layer, so every request and response is validated before reaching end users. For infrastructure-as-code tools like Terraform or OpenTofu, policy guardrails (OPA, Sentinel) run during the plan phase before any changes are applied.
