← ResourcesDEVOPS Β· IAC TESTS

A Testing Strategy for Infrastructure-as-Code

Syntax validation, compliance and a test deployment: three levels to catch the error before it gets expensive.

STRALYA13 min readJuly 2026

Why test infrastructure-as-code before deployment Infrastructure-as-code has revolutionized the management of cloud environments by making configurations and deployments reproducible and versioned. However, this power comes with a new and often underestimated risk: a small error in a Terraform, CloudFormation or Ansible file can deploy a completely broken infrastructure, open critical security holes, or generate an exploding AWS bill in a matter of seconds. Unlike application bugs that affect one user or one feature, an infrastructure error deploys the problem across the entire platform, often with no immediate safety net. Testing infrastructure-as-code before production lets you catch these errors when they cost zero euros and zero downtime, rather than after the faulty infrastructure is already in place. This is why the most mature DevOps and platform engineering teams build these tests in as a mandatory step in their CI/CD pipeline, exactly as they do for application code. The absence of infrastructure-as-code tests creates a false economy: you "save" pipeline time but lose peace of mind, the ability to roll back quickly, and you pay hidden costs for emergency remediation.

The three essential categories of infrastructure-as-code tests Infrastructure-as-code tests fall into three complementary levels, each addressing a specific type of error. The first level is syntactic and structural validation: verifying that the Terraform or CloudFormation file is syntactically correct and that the references between resources are consistent. A badly formatted Terraform file, or one with an undeclared variable, will fail at this stage. This is the fastest and cheapest test, ideally run immediately after a commit, before the pipeline even requests cloud resources. The second level is checking business rules and compliance: making sure that each cloud resource respects the company's security standards, budget constraints and governance rules. For example, no database is publicly exposed, AWS security groups do not open port 3389 (RDP) to 0.0.0.0, and mandatory tags are present on every resource. These tests do not require an API call to AWS, just a static analysis of the IaC code: they catch deviations before the infrastructure is even created. The third level is validation in a test environment: actually deploying the IaC configuration to a staging or test environment, then verifying that the deployed infrastructure works as expected (the instances start, the databases accept connections, the load balancers route traffic correctly). This last level requires real cloud resources and therefore cost and time, but it catches the errors that would slip past the previous levels, such as an overly restrictive IAM role that prevents an application from reading an environment variable stored in Secrets Manager.

Syntax validation and static analysis of infrastructure-as-code Syntax validation is the most elementary and most useful filter in the infrastructure-as-code testing pipeline. For Terraform, it starts with a simple terraform validate command that verifies that all the .tf files parse correctly, that the configuration blocks (resource, variable, output) are well formed, and that the references between resources have no typos. A variable named aws_region used in a block that expects var.aws_region_name will fail here. For CloudFormation, the aws cloudformation validate-template tool performs a similar check. These basic commands run without any API call to AWS, so instantly and for free, which makes them the ideal first steps of the CI/CD pipeline. Beyond pure syntax, specialized linters like TFLint (for Terraform) or cfn-lint (for CloudFormation) analyze the code and flag common anti-patterns: variables declared but never used, inconsistent resource names, configurations that do not follow the cloud provider's recommendations. TFLint can be extended with plugins to check business rules specific to your organization. These tools integrate easily into a CI/CD pipeline: a commit that introduces a syntax error or an anti-pattern detected by the linter will be rejected immediately, before it is even merged into the main branch. This fast feedback encourages developers to fix issues locally before pushing, which reduces noise and back-and-forth in the pipeline.

Compliance and security testing for infrastructure-as-code After verifying that the configuration is syntactically valid, the next step is to ensure that it respects the security and compliance rules defined by the company. This is where tools like Checkov, Snyk, or AWS Config Rules come in. Checkov scans the IaC code statically (without deploying it) to verify that there are no recognized security deviations: an AWS security group that exposes port 3306 (MySQL) to 0.0.0.0, an overly permissive IAM policy (action:*), an RDS without encryption enabled, a container image or a Lambda function with no source restriction. Each deviation detected by Checkov or Snyk is assigned a standard control ID (from frameworks like CIS, NIST or PCI-DSS), which helps trace and manage overall compliance. Rather than manually defining generic controls, many teams write custom compliance tests using frameworks like Terraform Cloud Policy as Code or Conftest, which let you express the organization's business rules in declarative logic: all S3 buckets must have versioning enabled, all resource names must carry an environment prefix (dev-, staging-, prod-), databases may only be created in certain authorized AWS regions. Once these rules are codified, they apply identically to every deployment, without depending on a human's vigilance or memory. Violations of these custom rules block the pipeline, forcing the team either to fix the configuration or to justify an explicit exception (which then becomes traceable and auditable).

Test deployment and validating that the infrastructure works Once the IaC configuration is validated syntactically and checked against the security rules, the final and most valuable step is to actually deploy the infrastructure to a test or staging environment, then verify that everything works as intended. This step incurs a cost in AWS resources and execution time (deploying a CloudFormation stack or a Terraform module can take several minutes), but it catches the errors that the previous steps cannot see: an overly restrictive IAM policy that prevents a Lambda function from accessing a role, insufficient database performance parameters for the workload, a security group that unintentionally blocks a port essential to a dependency. The typical pattern is a commented deployment plan generated by terraform plan (for Terraform) or a CloudFormation changeset, which the engineers review before approval. This human review verifies that the resources to be created or modified match the intent of the configuration change. Then, terraform apply or aws cloudformation create-stack/update-stack performs the real deployment to the test environment. Next, integration tests or smoke tests verify that things work: can an application deployed on EC2 instances access the RDS database, receive HTTP requests through the load balancer, read files from the S3 bucket specified in the environment variables. These tests can be simple HTTP calls with curl, database queries with tools like mysql-cli, or complete application tests that create and delete data to validate the full cycle. At the end, a teardown step deletes the test environment, which avoids leaving costly AWS resources lying around and also verifies that deletion works correctly (terraform destroy or aws cloudformation delete-stack must be idempotent and error-free). This complete cycle, although longer and costlier than a simple static analysis, provides strong confidence in the reliability of the IaC code before it affects production.

Integrating infrastructure-as-code tests into the CI/CD pipeline For infrastructure-as-code tests to deliver their full value, they must be integrated automatically and mandatorily into the CI/CD pipeline, on every commit or pull request that modifies IaC code. A typical architecture places these tests in several sequential stages: first, a 'lint and syntax check' stage that runs in under a minute and immediately rejects basic errors (this is the finest and fastest net). Next, a 'security and compliance scan' stage that statically analyzes the configurations against the business rules (Checkov, Conftest, etc.), without deploying anything. These first two stages must block the pipeline's progress if they fail: a commit that does not respect the elementary rules or minimum security must never reach the environment. Third, if the IaC code concerns infrastructure, a 'plan' stage (terraform plan) or 'change set' (CloudFormation) produces a readable summary of the changes to apply, which must be validated by an owner (a human gate) before continuing. Fourth, after approval, the 'apply' stage actually deploys to a test environment and runs the validation smoke tests. Finally, for production pipelines, a 'deploy to prod' stage requests approval again before updating the production infrastructure. This funnel structure ensures that critical errors are caught very early (at minimal cost and latency), while the more costly and binding validations (real deployment, human approvals) only run if the previous stages succeeded. Integration into the standard CI/CD pipeline (Jenkins, GitLab CI, GitHub Actions, etc.) is typically done through shell scripts or dedicated stages, reusing the environment variables and secrets (AWS keys, tokens) of the existing pipeline. This avoids maintaining two parallel systems and ensures consistency between application tests and infrastructure tests.

Popular tools and frameworks for testing infrastructure-as-code The ecosystem of infrastructure-as-code testing tools has grown considerably in recent years, offering a range of options suited to different IaC technologies and use cases. For Terraform, terraform validate and terraform plan are part of the base; TFLint adds style analysis and detects anti-patterns; Checkov (or Snyk) scans configurations for security risks; Terratest (a Go framework) lets you write complex integration tests that actually deploy and validate the resulting behavior. For CloudFormation, cfn-lint validates the syntax and detects common errors, while additional linters and the AWS CloudFormation Linter bring advanced checks. Conftest is a particularly flexible tool that lets you write declarative compliance rules (in the Rego language) applicable to any format (Terraform, Kubernetes, CloudFormation, JSON, YAML), which makes it an ideal candidate to standardize checks across several infrastructure technologies. AWS Config, the native AWS cloud service, provides continuous drift detection: it analyzes the real infrastructure deployed in your AWS account and flags when it drifts from the rules you defined (for example, has a security group been modified manually outside the pipeline?). For multi-cloud or heterogeneous environments, Terraform Cloud Policy as Code integrates the checks directly into the terraform workflow, blocking the apply if the rules are not respected. Choosing the right tools depends on your stack (Terraform vs CloudFormation), your needs (basic syntax vs deep compliance), and your DevOps maturity. Many teams combine several tools: TFLint for syntax, Checkov for security, Terratest for critical integrations, and AWS Config for continuous monitoring in production.

AWS TEARDOWN Β· FREE

Get the AWS Teardown: where your bill really goes.

The guide listing the 12 cost areas that leak the most at scale-ups, and how to plug them. Free, by email, no obligation.