Skip to content
CloudOps
Newsletter
All guides
AI for Infrastructure as Code By James Joyner IV · · 9 min read

IaC Testing Strategies That Actually Catch Bugs

A layered approach to testing infrastructure as code — from static checks to integration tests — and where AI speeds up writing the test suite.

  • #iac
  • #testing
  • #ci-cd
  • #automation
  • #quality
  • #ai

“It’s just config, it doesn’t need tests” is a sentence I’ve heard right before a very expensive outage. Infrastructure as code is code. It has bugs, regressions, and edge cases like any other code — except its bugs delete databases and open firewalls.

The good news is that IaC testing follows the same pyramid as application testing: lots of cheap, fast checks at the bottom; a few slow, expensive ones at the top. Here’s the layered strategy I use across any IaC tool, and where AI cuts the busywork.

Layer 1: Static analysis (cheap, run constantly)

The bottom of the pyramid catches the most bugs for the least effort, and it runs in seconds. Three kinds:

Syntax and validation. Does it even parse? ansible-playbook --syntax-check, kubectl apply --dry-run=client, terraform validate. Catches typos before anything else runs.

Linting. Style and common-mistake checks: ansible-lint, yamllint, tflint, kube-linter. These encode hard-won lessons — deprecated syntax, missing names, dangerous defaults.

Security scanning. Tools like Checkov, tfsec, or Trivy scan for misconfigurations: public buckets, missing encryption, over-broad IAM. This is the highest-ROI scan you can add.

# CI: static layer
- run: yamllint .
- run: ansible-lint
- run: checkov -d . --quiet

All three are deterministic and fast. There’s no excuse not to run them on every commit. AI helps here mostly by explaining failures — paste a cryptic Checkov finding and ask “what’s the risk and how do I fix it in this manifest?”

Layer 2: Policy tests (your rules, enforced)

Static tools catch generic problems. Policy-as-code catches your problems — your tagging standard, your forbidden instance types, your required replica counts. Tools like Conftest/OPA let you assert these against config or plan output.

This layer is where I lean on AI most for authoring, because policy languages like Rego have a steep curve. Describe the rule in English, get a draft policy, then — critically — test it against a known-bad and known-good fixture to prove it actually fails and passes when it should. I keep policy and testing prompts tuned for this.

Layer 3: Unit tests (logic in isolation)

When your IaC has real logic — templates, conditionals, generated values — unit-test that logic without touching a cloud. For Ansible, molecule spins up a container, applies a role, and asserts the result. For programmatic IaC (Pulumi, CDK), you can assert on the resource graph in a normal test framework.

# Pulumi unit test: assert the bucket is encrypted
def test_bucket_encrypted(bucket):
    assert bucket.server_side_encryption_configuration is not None

These run in CI without provisioning anything real, so they’re fast enough to run on every PR. AI is genuinely useful for generating the assertion scaffolding — give it the resource definition and ask for “unit tests asserting encryption, versioning, and no public access.” You review; the boilerplate is written.

Layer 4: Integration tests (real resources, real cost)

The top of the pyramid: actually provision the infrastructure in a sandbox account, assert it behaves, then tear it down. Terratest, molecule with a real cloud driver, or kitchen-terraform do this. They catch the bugs nothing else can — the IAM policy that looks right but denies the actual call, the security group that blocks real traffic.

// Terratest-style: provision, assert, destroy
defer terraform.Destroy(t, opts)
terraform.InitAndApply(t, opts)
url := terraform.Output(t, opts, "endpoint")
http_helper.HttpGetWithRetry(t, url, nil, 200, "OK", 30, 5*time.Second)

These are slow and cost money, so run them sparingly — on merges to main, nightly, or before a release, not on every commit. The whole point of the pyramid is that the cheap layers below catch most bugs so this layer rarely fails.

The pyramid in CI

Map the layers to triggers:

  • Every commit / pre-commit: static analysis + policy tests. Seconds.
  • Every PR: the above plus unit tests. A couple of minutes.
  • Merge to main / nightly: integration tests in a sandbox. Minutes to tens of minutes.

This shape means developers get near-instant feedback on the common mistakes, and you only pay for slow cloud tests when the cheap gates have already passed.

Don’t forget the destroy path

A test that provisions but fails to destroy leaks resources and bills. Always wrap integration tests so teardown runs even on failure (defer, try/finally, or a CI cleanup step). And periodically run a sweep for orphaned test resources — a tag like purpose=ci-ephemeral plus a scheduled cleanup job saves you from a surprise invoice.

Where AI fits across the pyramid

To be concrete about the division of labor:

  • Authoring: AI drafts policy rules, unit-test assertions, and molecule scaffolding fast. Best ROI.
  • Explaining failures: paste a lint/scan/test error, get the cause and fix. Saves doc-diving.
  • Generating fixtures: ask for a known-good and known-bad version of a manifest to test a policy against.
  • Coverage gaps: ask “what failure modes of this resource am I not testing?” — it’s good at naming the IAM and network edge cases you forgot.

What AI should not do: be the test. It doesn’t run your infrastructure. Every generated test must execute against real fixtures or real resources to mean anything. Treat AI as a very fast test-author, then let the deterministic test runner be the judge.

Start small, build up

You don’t need all four layers on day one. The ordering by value:

  1. Add static analysis today — it’s a one-line CI change and catches a shocking amount.
  2. Add security scanning (Checkov/tfsec) — highest risk reduction per minute of effort.
  3. Add policy tests for the rules you care about most.
  4. Add unit tests for any IaC with real logic.
  5. Add integration tests for the critical-path infrastructure you can’t afford to get wrong.

Each layer is independently valuable. Keep your test-authoring prompts in a prompt library so the model knows your stack, and your IaC stops being the untested code that everyone’s quietly afraid of.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.