Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Test Infrastructure Code with Terratest

By Kokil Thapa | Last reviewed: September 2026

You ship Terraform modules, then production breaks because a security group rule never opened port 443. Static validation catches syntax errors, but it cannot prove your stack actually works. That gap is exactly why teams test infrastructure code with Terratest — a Go library that runs real terraform apply cycles and asserts on live cloud resources. If you already treat Terraform modules as reusable products, Terratest gives those modules the same integration-test discipline you expect from application code.

What is Terratest and why should you test infrastructure code with it?

Terratest is an open-source Go library maintained by Gruntwork. It wraps Terraform, Packer, Docker, Kubernetes, and AWS SDK calls inside standard Go tests. Your test binary becomes the orchestrator: provision, verify, tear down.

That model differs from lint-only checks. terraform plan shows intent. Terratest proves the result. On shared EC2 infrastructure I maintain with GitLab CI and Deployer 7, a bad Terraform change can take down multiple sister sites at once. A Terratest suite that applies a module into an isolated account or sandbox VPC pays for itself after one prevented outage.

Terratest Infrastructure Test FlowGo Testgo test ./...Terraforminit plan applyLive CloudAWS GCP AzureAssertionsHTTP DNS IAMdefer terraform.Destroy — always runs cleanupFailed tests still tear down; orphaned resources cost moneyCatches: wrong ports, missing tags, broken DNS, failed health checksMissed by validate and plan-only CI stages
How Terratest tests infrastructure code: Go drives Terraform apply, asserts on live resources, then destroys them.

Terratest sits near the top of the testing pyramid for infrastructure. Unit tests cover HCL parsing and policy checks. Integration tests provision real resources. Keep both layers; do not replace policy-as-code gates with slow apply tests alone.

Common assertions include:

  • Terraform output values match expected strings or CIDR blocks.
  • HTTP endpoints return 200 after a load balancer provisions.
  • DNS records resolve to the correct IP within a timeout window.
  • IAM policies grant only the permissions your module documents.
  • S3 buckets enforce encryption and block public access.

The official Terratest documentation at terratest.gruntwork.io remains the primary reference. Pair it with HashiCorp's Terraform docs for provider-specific behaviour that tests must account for.

How do you set up Terratest for Terraform module testing?

You need Go 1.21 or newer, Terraform CLI, cloud credentials, and a module directory. Terratest does not replace Terraform; it executes it as a subprocess from Go tests.

Install prerequisites

On Ubuntu 24.04 — the OS I use on production servers — install Go and Terraform first:

sudo apt update
sudo apt install -y golang-go unzip
wget https://releases.hashicorp.com/terraform/1.9.8/terraform_1.9.8_linux_amd64.zip
unzip terraform_1.9.8_linux_amd64.zip
sudo mv terraform /usr/local/bin/

go version
terraform version

Initialize a Go module inside your infrastructure repository:

mkdir -p infra/test && cd infra/test
go mod init github.com/yourorg/infra-tests
go get github.com/gruntwork-io/terratest/modules/terraform@latest
go get github.com/stretchr/testify/require

Organize tests to mirror your module tree. A typical layout:

infra/
├── modules/
│   └── vpc/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── test/
    ├── go.mod
    └── vpc_test.go

Configure cloud credentials safely

Never hard-code access keys inside test files. Use environment variables or OIDC in CI. For local runs, export a dedicated sandbox profile:

export AWS_PROFILE=terratest-sandbox
export AWS_DEFAULT_REGION=ap-south-1
export TF_VAR_environment=test

Restrict the sandbox account with SCPs or IAM boundaries. A forgotten defer terraform.Destroy in a shared production account creates real bills and security exposure. I've seen orphaned RDS instances from manual testing; automated cleanup is non-negotiable.

If you manage servers manually today, moving toward tested modules aligns with professional Linux system administration practices — infrastructure becomes versioned, reviewable, and repeatable.

How do you write your first Terratest integration test?

Start with one small module: an S3 bucket, a security group, or a single-purpose VPC. Large stacks make failures hard to diagnose and runs expensive.

Minimal VPC module test

Create infra/test/vpc_test.go:

package test

import (
    "testing"
    "time"

    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/require"
)

func TestVpcModuleCreatesExpectedCidr(t *testing.T) {
    t.Parallel()

    terraformOptions := &terraform.Options{
        TerraformDir: "../modules/vpc",
        Vars: map[string]interface{}{
            "vpc_cidr": "10.42.0.0/16",
            "name":     "terratest-vpc",
        },
        EnvVars: map[string]string{
            "AWS_DEFAULT_REGION": "ap-south-1",
        },
    }

    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)

    vpcCidr := terraform.Output(t, terraformOptions, "vpc_cidr")
    require.Equal(t, "10.42.0.0/16", vpcCidr)
}

Run it:

cd infra/test
go test -v -timeout 30m ./...

The -timeout flag matters. Cloud APIs are slow. A default 10-minute Go timeout kills legitimate applies mid-flight.

Add HTTP and retry assertions

For load-balancer or compute modules, use Terratest's HTTP helper with retries. CloudFront and ALB endpoints often need 30–90 seconds before they respond:

import (
    "github.com/gruntwork-io/terratest/modules/http-helper"
)

func TestAlbResponds200(t *testing.T) {
    t.Parallel()
    /* ... terraform InitAndApply ... */
    url := terraform.Output(t, terraformOptions, "alb_url")

    http_helper.HttpGetWithRetry(t, url, nil, 200, "body", 30, 5*time.Second)
}

Go's testing package supports parallel tests via t.Parallel(). Use it cautiously with Terratest. Parallel applies against one AWS account can hit rate limits or state-lock collisions. Group related tests or use separate state file keys per test via TerraformDir overrides and unique name variables.

Terratest Project Layoutinfra/test/go.mod + go.sumvpc_test.gos3_bucket_test.godefer Destroyin every test funcinfra/modules/vpc/main.tf outputs.tfs3_bucket/variables.tfOne test file per module keeps failures isolated
Mirror Terraform modules with Go test files — each test applies one module, asserts outputs, and defers destroy.

Follow idempotent infrastructure principles inside modules themselves. Tests should be able to re-run without manual state surgery. Unique resource names via random_id or test-scoped prefixes prevent collisions when a prior destroy partially failed.

For Packer golden images, Terratest can build an AMI and assert SSH connectivity. That pairs naturally with immutable infrastructure workflows — test the image before Terraform ever references it.

How do you run Terratest in CI/CD pipelines without blowing the budget?

Integration tests cost money and time. A full VPC plus NAT gateway test might run 12–20 minutes and spend Rs 200–400 (~USD 1.50–3.00) per run in AWS ap-south-1. Pipeline design must gate when real applies happen.

  1. Fast lint stageterraform fmt -check, terraform validate, tflint, and OPA/Conftest policy checks. Runs on every push.
  2. Plan-only stageterraform plan against a read-only role. Catches dependency errors without creating resources.
  3. Terratest stage — full apply and destroy. Run on main branch merges, nightly schedules, or when module paths change.
  4. Post-deploy smoke — optional HTTP checks against staging after zero-downtime Terraform updates.

Example GitLab CI job:

terratest:
  stage: integration
  image: golang:1.23
  services: []
  variables:
    AWS_DEFAULT_REGION: ap-south-1
  before_script:
    - apt-get update && apt-get install -y unzip
    - curl -fsSL https://releases.hashicorp.com/terraform/1.9.8/terraform_1.9.8_linux_amd64.zip -o tf.zip
    - unzip tf.zip && mv terraform /usr/local/bin/
  script:
    - cd infra/test
    - go test -v -timeout 45m ./...
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
    - changes:
        - infra/modules/**/*
  id_tokens:
    AWS_ROLE_ARN:
      aud: sts.amazonaws.com

Use OIDC federation instead of long-lived CI secrets. Rotate nothing; the pipeline assumes a short-lived role. This mirrors how I configure GitLab CI deploy keys on production — ephemeral credentials reduce blast radius.

Wire Terratest results into the same quality mindset as code coverage gates in CI. A failed infrastructure test should block merge the same way a failing PHPUnit suite would on a Laravel app. Treat infra repos as product code, not ops scratchpads.

Terratest CI Pipeline StagesLintEvery pushValidateEvery pushPlanPR + mainTerratestMain onlySandbox account + OIDC role45m timeout · path filters on modules/Pass → merge allowedFail → block deployNightly full suiteCatch drift + API changes
Run expensive Terratest applies only on main merges or module changes — keep lint and validate on every commit.

Manage test fixtures like application test data. Store minimal tfvars in infra/test/fixtures/ and document assumptions. The same discipline applies to test data management for pipelines — reproducible inputs, no secrets in Git.

For regex-heavy output parsing in custom assertions, a local regex tester saves debugging time before you embed patterns in Go.

Terratest vs other infrastructure testing tools — which should you choose?

No single tool covers every layer. Mature teams combine static analysis, policy checks, and selective integration tests. Terratest excels when you need Go-native control flow, retries, and multi-tool orchestration in one test binary.

ToolLanguageProvisions real resourcesBest forTrade-off
TerratestGoYesEnd-to-end module and stack verificationSlow, costs money, needs cloud sandbox
terraform test (CLI)HCLOptional (mock or real)Native Terraform 1.6+ unit testsLess flexible for HTTP retries and custom logic
Checkov / tfsecPython / GoNoSecurity misconfiguration scanningCannot prove runtime behaviour
OPA / ConftestRegoNoPolicy-as-code on plansPolicy only; no live endpoint checks
kitchen-terraformRubyYesChef-era Terraform testingSmaller community than Terratest in 2026
LocalStack + mocksVariousEmulatedFast feedback for AWS-shaped APIsEmulator gaps; not full cloud fidelity

My practical recommendation: run Checkov and Conftest on every PR. Add native terraform test blocks for pure HCL logic. Reserve Terratest for modules where a wrong apply breaks production — networking, IAM, load balancers, databases.

That layered approach resembles chaos engineering philosophy applied earlier in the pipeline. You validate assumptions before customers hit a 503.

Infrastructure Testing LayersTerratest — live apply + HTTP/DNS assertionsSlow · costly · highest confidencePolicy — OPA Conftest on terraform plan JSONFast · no cloud spendStatic — fmt validate tflint checkovEvery commit · secondsUse all three layers — not Terratest aloneMatches the testing pyramid for infrastructure
Layer static lint, policy checks, and Terratest integration tests — each catches failures the others miss.

Teams adopting GitOps for infrastructure still benefit from Terratest before modules enter the GitOps repo. Test the module, then let Argo CD or Flux reconcile known-good artefacts.

On infrastructure-heavy client work such as SRP Infrastructure Development Nepal, proving modules in a sandbox before production rollout reduces rollback incidents. The same mindset applies whether you deploy Laravel on EC2 or Kubernetes with Terraform.

Go's built-in test tooling is documented at go.dev. Terratest extends it; you do not learn a proprietary DSL. If your platform team already writes Go operators or CLI tools, Terratest fits naturally.

Common mistakes to avoid

  • Skipping defer terraform.Destroy — the most expensive bug in infra testing.
  • Running parallel tests against one state file — state lock errors and flaky CI.
  • Testing entire environments instead of isolated modules — 45-minute runs nobody waits for.
  • Hard-coding region-specific AMI IDs — tests break when AWS deprecates an image.
  • Ignoring cost alerts on the sandbox account — NAT gateways add up fast.

Professional testing and optimization services treat infrastructure repos as first-class software. Terratest is one piece of that broader quality stack alongside application test suites like ParaTest for PHP and deployment automation.

Key Takeaways

  • Terratest runs real terraform apply cycles from Go tests and catches failures that validate and plan miss.
  • Always call defer terraform.Destroy and use an isolated sandbox account with OIDC credentials in CI.
  • Mirror your module tree with one test file per module; keep tests small and timeout-friendly.
  • Run Terratest on main merges or module path changes — not on every feature-branch push.
  • Combine Terratest with Checkov, Conftest, and native terraform test for a complete IaC quality gate.
  • Treat infrastructure code like product code: reviewed, versioned, and blocked from merge on failure.

People Also Ask

Does Terratest work with Terraform Cloud and remote state?

Yes. Pass backend configuration through terraform.Options or use a generated backend config file in your test fixture directory. Ensure each test uses a unique state key so parallel runs do not corrupt shared state. Many teams create a dedicated Terraform Cloud workspace per module test.

How long should Terratest tests take?

A focused single-resource test often completes in 3–8 minutes. Full stack tests with NAT gateways, RDS, and ALBs can exceed 25 minutes. Set Go's -timeout to at least 30–45 minutes for CI. Split large environments into module-level tests to keep feedback loops tolerable.

Can you test infrastructure code with Terratest without AWS?

Terratest supports GCP, Azure, and Kubernetes modules through the same Go API. You can also test Docker and Packer builds locally without cloud spend. For AWS-shaped fast feedback, some teams use LocalStack — but validate critical paths against real APIs before production.

Is Terratest still maintained in 2026?

Gruntwork actively maintains Terratest on GitHub with regular releases. The ecosystem is mature. For greenfield Terraform 1.6+ projects, evaluate native terraform test alongside Terratest — use HCL tests for pure logic and Terratest when you need HTTP retries, SSH checks, or multi-step orchestration in Go.

Ship infrastructure you can prove works

Manual terraform apply in production is a gamble. When you test infrastructure code with Terratest, you replace that gamble with evidence — live resources that match your module contract before any customer traffic hits them. Start with one module, one sandbox account, and one CI job on main. Expand coverage as modules stabilize.

If you want help designing Terraform modules, CI pipelines, or a full IaC testing strategy for a Nepal or remote team, contact us to discuss your stack. You can also read more from Kokil Thapa on Laravel deployment, Linux administration, and the broader infrastructure-as-code landscape.

Frequently Asked Questions

Terratest is Gruntwork's open-source Go library that runs real Terraform apply cycles from Go tests and asserts on live cloud resources before destroying them.

A full VPC plus NAT gateway test in AWS ap-south-1 often costs Rs 200–400 (~USD 1.50–3.00) per run and takes 12–20 minutes.

Focused single-resource tests finish in 3–8 minutes; full stacks with NAT gateways, RDS, and ALBs can exceed 25 minutes.

You need Go 1.21 or newer, Terraform CLI, cloud credentials, and a module directory. On Ubuntu 24.04, install Go and Terraform, then initialize a Go module inside infra/test with go mod init and go get github.com/gruntwork-io/terratest/modules/terraform plus github.com/stretchr/testify/require. Organize tests to mirror your module tree, for example infra/test/vpc_test.go testing infra/modules/vpc. Export a dedicated sandbox profile like AWS_PROFILE=terratest-sandbox rather than hard-coding keys in test files.

Start with one small module such as an S3 bucket, security group, or single-purpose VPC. Create a Go test that builds terraform.Options with TerraformDir, Vars, and EnvVars, registers defer terraform.Destroy before InitAndApply, then asserts outputs with require.Equal. Run cd infra/test && go test -v -timeout 30m ./... Cloud APIs are slow, so do not rely on Go's default 10-minute timeout, which kills legitimate applies mid-flight. Use unique resource names via random_id or test-scoped prefixes so re-runs do not collide.

Layer your pipeline: run terraform fmt -check, validate, tflint, and OPA/Conftest on every push; add a plan-only stage against a read-only role; reserve full Terratest apply-and-destroy for main branch merges, nightly schedules, or when infra/modules paths change. A GitLab CI job using golang:1.23 can install Terraform 1.9.8 in before_script and run go test -v -timeout 45m ./... Authenticate with OIDC id_tokens and AWS_ROLE_ARN instead of long-lived secrets. A failed infrastructure test should block merge the same way a failing application test suite would.

No single tool covers every layer. Checkov and tfsec scan security without provisioning resources. OPA/Conftest enforces policy on plans only. Native terraform test in Terraform 1.6+ suits pure HCL logic with optional mocks. Terratest excels when you need Go-native control flow, HTTP retries, SSH checks, and multi-tool orchestration against real resources. Run Checkov and Conftest on every PR, add terraform test blocks for HCL logic, and reserve Terratest for modules where a wrong apply breaks production — networking, IAM, load balancers, and databases.

Skipping destroy is the most expensive bug in infrastructure testing. Terratest provisions real billable resources, and orphaned RDS instances or NAT gateways from forgotten cleanup create unexpected charges and security exposure. Always register defer terraform.Destroy immediately after defining terraform.Options, before InitAndApply runs. Restrict testing to an isolated sandbox account with SCPs or IAM boundaries so a failed cleanup cannot affect production. On shared EC2 infrastructure, one bad change can take down multiple sites at once — automated teardown is non-negotiable.

Never hard-code access keys inside test files. For local runs, export a dedicated sandbox profile such as AWS_PROFILE=terratest-sandbox and AWS_DEFAULT_REGION=ap-south-1, then restrict that account with SCPs or IAM boundaries. In CI, use OIDC federation — GitLab CI id_tokens with AWS_ROLE_ARN aud set to sts.amazonaws.com — so the pipeline assumes a short-lived role with nothing to rotate. This reduces blast radius the same way ephemeral deploy credentials should work for production pipelines.

Yes. Terratest supports GCP, Azure, and Kubernetes modules through the same Go API. You can test Docker and Packer builds locally without cloud spend — Terratest can build an AMI and assert SSH connectivity before Terraform ever references that image. For AWS-shaped fast feedback, some teams use LocalStack, but emulator gaps mean you should still validate critical paths against real cloud APIs before production rollout.

Yes. Pass backend configuration through terraform.Options or use a generated backend config file in your test fixture directory. Ensure each test uses a unique state key so parallel runs do not corrupt shared state or trigger lock collisions. Many teams create a dedicated Terraform Cloud workspace per module test. Store minimal tfvars in infra/test/fixtures/ with documented assumptions, and keep secrets out of Git.

Gruntwork actively maintains Terratest on GitHub with regular releases, and the ecosystem is mature. For greenfield Terraform 1.6+ projects, evaluate native terraform test alongside Terratest — use HCL tests for pure logic and Terratest when you need HTTP retries via http-helper, SSH checks, or multi-step orchestration in Go. If your platform team already writes Go operators or CLI tools, Terratest extends Go's standard testing package without a proprietary DSL.

Skipping defer terraform.Destroy leaves orphaned billable resources. Running t.Parallel() against one AWS account hits rate limits or state-lock collisions — group related tests or assign unique state file keys and name prefixes per run. Testing entire environments instead of isolated modules produces 45-minute runs nobody waits for. Hard-coding region-specific AMI IDs breaks tests when AWS deprecates images. Ignoring cost alerts on sandbox accounts lets NAT gateway charges accumulate silently between CI runs.

terraform validate catches syntax errors and terraform plan shows intent, but neither proves your stack actually works — a security group rule can pass validation yet never open port 443. Run Terratest when Terraform modules are reusable products whose failure takes down production. Keep lint, validate, and policy checks on every commit, and trigger expensive apply tests only on main merges or module path changes so feature-branch feedback stays fast.

After InitAndApply, Terratest verifies live cloud state: Terraform outputs matching expected strings or CIDR blocks, HTTP endpoints returning 200 using http-helper with retries because ALB and CloudFront endpoints often need 30–90 seconds, DNS records resolving within a timeout window, IAM policies granting only documented permissions, and S3 buckets enforcing encryption plus public access blocks. These runtime checks replace the gamble of manual terraform apply with evidence before customer traffic hits your infrastructure.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: