Infrastructure as Code Meets Zero Trust
Infrastructure as Code (IaC) is both a powerful enabler of Zero Trust and a potential attack vector that must itself be secured. On the enabling side, IaC makes security policies repeatable, auditable, and version-controlled. Every firewall rule, IAM policy, encryption setting, and network configuration is expressed in code, reviewed through pull requests, tested in pipelines, and applied deterministically. On the risk side, IaC templates often contain overly permissive configurations copied from tutorials, the CI/CD pipelines that execute IaC hold highly privileged credentials, and the state files that track deployed resources contain sensitive information including database passwords and API keys. Securing the IaC lifecycle is inseparable from implementing Zero Trust.
The core IaC tools, Terraform, Pulumi, AWS CloudFormation, Azure Bicep, and Google Cloud Deployment Manager, each have different security characteristics and integration points. Terraform’s provider ecosystem spans all major cloud providers and hundreds of SaaS services, making it the dominant choice for multi-cloud environments. Pulumi uses general-purpose programming languages (Python, TypeScript, Go), enabling complex logic and testing patterns that declarative languages cannot express. CloudFormation and Bicep provide native integration with their respective cloud platforms, with features like drift detection and stack policies that third-party tools must implement separately.
Policy as Code: Enforcing Zero Trust in the Pipeline
Policy as Code evaluates IaC templates against security rules before they are applied to the cloud environment. This shift-left approach catches misconfigurations during the development process rather than discovering them after deployment through compliance scans or, worse, through a security incident. The policy evaluation happens during the CI/CD pipeline, typically after the Terraform plan phase, and blocks the pipeline if any policy violation is detected.
Open Policy Agent with the Conftest framework evaluates Terraform plan JSON output against Rego policies. For example, a policy can verify that every S3 bucket has server-side encryption enabled, that no security group allows ingress from 0.0.0.0/0 on port 22, and that every IAM role has a permission boundary attached. The Rego language is expressive enough to handle complex policies like ensuring that cross-account IAM role trust policies reference only known account IDs from an approved list maintained in a separate data file.
Checkov and tfsec are static analysis tools specifically designed for IaC security scanning. They come with hundreds of built-in rules mapped to CIS benchmarks, AWS Foundational Security Best Practices, Azure Security Benchmark, and GCP Security Command Center findings. Checkov supports Terraform, CloudFormation, Kubernetes manifests, Helm charts, and Dockerfiles, making it a comprehensive pre-deployment security gate. Custom rules extend the built-in checks to enforce organization-specific requirements that benchmarks do not cover.
- Run Checkov or tfsec in every CI pipeline that modifies infrastructure code; fail the build on high and critical findings
- Use OPA/Conftest for organization-specific policies that go beyond generic benchmarks
- Implement Sentinel policies in Terraform Cloud/Enterprise for enforcement levels (advisory, soft-mandatory, hard-mandatory)
- Maintain a policy library in a separate repository with its own testing and release cycle
- Track policy violations over time to identify teams or patterns that consistently produce non-compliant configurations
Securing the IaC Pipeline Itself
The CI/CD pipeline that executes IaC has the permissions to create, modify, and destroy cloud infrastructure. Compromising this pipeline gives an attacker the ability to deploy backdoors, disable security controls, exfiltrate data, and destroy resources. Securing the pipeline is therefore a critical Zero Trust requirement, yet it is frequently overlooked in favor of securing the IaC templates themselves.
Pipeline credentials should follow the same Zero Trust principles as all other credentials: short-lived, narrowly scoped, and dynamically generated. GitHub Actions supports OIDC federation with AWS, Azure, and GCP, allowing workflows to exchange a GitHub-issued JWT for cloud provider credentials without storing any static secrets. The trust policy on the cloud role should restrict the OIDC subject to specific repositories and branches: a workflow running on a feature branch in a forked repository should not be able to assume the same role as the main branch of the canonical repository.
Branch protection rules enforce that IaC changes cannot be applied without peer review. The main branch should require at least one approving review, passing status checks (including policy-as-code evaluation), and linear history (no merge commits) to maintain a clean audit trail. Environment protection rules in GitHub Actions add an approval gate before the deployment job runs, ensuring that a human verifies the Terraform plan output before terraform apply executes. For sensitive environments like production, require approval from a member of the security team in addition to the requesting team.
Pipeline Security Checklist
- Use OIDC federation instead of static credentials for all cloud provider authentication in CI/CD
- Restrict OIDC trust policies to specific repositories, branches, and environments
- Enable branch protection requiring reviews and passing status checks before merge
- Use environment protection rules with required reviewers for production deployments
- Pin GitHub Actions to commit SHAs rather than mutable tags to prevent supply chain attacks
- Run pipeline workers in ephemeral environments that are destroyed after each execution
State File Security and Sensitive Data
Terraform state files contain the complete current state of managed infrastructure, including resource IDs, configuration values, and, critically, any sensitive values that Terraform resolved during apply. Database passwords, API keys, and TLS certificate private keys that are managed through Terraform appear in the state file in plaintext. The state file is, effectively, a comprehensive inventory of your infrastructure and its secrets, making it one of the most sensitive artifacts in the entire environment.
Remote state backends with encryption and access controls are mandatory for Zero Trust. Terraform Cloud and Terraform Enterprise encrypt state at rest and restrict access through workspace-level permissions. For self-managed backends, S3 with server-side encryption (SSE-KMS with a customer-managed key), DynamoDB state locking, and a bucket policy that restricts access to specific IAM roles provides equivalent protection. Azure Storage with customer-managed encryption and GCS with Cloud KMS encryption serve the same purpose for their respective clouds.
State file access should be logged and monitored with the same rigor as any other sensitive data access. CloudTrail data events on the S3 bucket storing Terraform state, Azure Storage diagnostic logs, or GCS data access audit logs capture every read and write operation. Alert rules should flag state file access from unexpected principals or IP addresses, as this could indicate an attacker attempting to extract secrets from the state or understand the infrastructure layout before launching a targeted attack.
Drift Detection and Continuous Compliance
Configuration drift occurs when the actual state of cloud resources diverges from the desired state defined in IaC. Drift can result from manual console changes, emergency fixes applied outside the pipeline, or direct API calls by automated tools. In a Zero Trust context, drift is a security concern because it means the verified, policy-checked IaC templates no longer accurately describe the environment. A security group modified through the console to allow broader ingress will not be caught by the IaC policy checks that only evaluate code changes.
Terraform’s plan output detects drift by comparing the state file against the actual cloud resources. Running terraform plan on a schedule (daily or more frequently) and alerting on any detected drift provides continuous assurance that the deployed environment matches the code. AWS Config, Azure Policy, and GCP Security Command Center provide cloud-native drift detection that operates independently of the IaC tool, catching changes regardless of how they were made.
Automated drift remediation is the final step: when drift is detected, an automated pipeline reverts the change by applying the IaC configuration. This self-healing behavior ensures that manual changes, whether accidental or malicious, are automatically corrected. The remediation pipeline should log the drift it detected and the correction it applied, creating an audit trail of unauthorized changes. Organizations should review these drift events regularly to identify patterns: frequent drift from a specific team may indicate a workflow gap that should be addressed through better tooling rather than after-the-fact correction.
Module Governance and Supply Chain Trust
Terraform modules and Pulumi component resources encapsulate reusable infrastructure patterns, but they also introduce supply chain risk. A module sourced from the public Terraform Registry or a third-party Git repository could contain malicious resources, overly permissive configurations, or backdoor access. Zero Trust applied to the IaC supply chain means verifying every module before use, pinning module versions to specific commits or releases, and hosting approved modules in a private registry.
A private module registry (Terraform Cloud private registry, Artifactory, or a simple Git repository with version tags) serves as the curated source of approved infrastructure patterns. Module authors submit modules through a pull request process where security reviewers verify the module’s IAM policies, network configurations, encryption settings, and logging configurations against organizational standards. Only approved modules in the private registry are permitted in production configurations, enforced through Sentinel or OPA policies that deny module sources outside the approved list.
Module testing using Terratest, the Terraform test framework (introduced in Terraform 1.6), or Pulumi’s testing framework validates that modules produce secure configurations. Tests deploy the module in an isolated test account, assert that the resulting resources have the expected security properties (encryption enabled, public access disabled, logging configured), and destroy the resources after verification. These tests run in the module’s CI pipeline and must pass before a new version is published to the private registry. This chain of verification, from code review through automated testing to registry publication, ensures that the building blocks of your infrastructure are trustworthy before they are ever used in production.
