Terraform · IaC · AWS

Terraform consultant — infrastructure as code you can actually review.

Modular, peer-reviewed Terraform for AWS: reusable modules, remote state done right, environment promotion, and policy-as-code that blocks unsafe changes at plan time. 16+ years, CISA + AWS Solutions Architect Professional.

Book a free 30-min callEmail me

What you get

Modular IaC

  • Repos split by concern — networking, compute, data, security, observability
  • Reusable modules with clean inputs/outputs and versioning
  • DRY environments (dev/stage/prod) via workspaces or Terragrunt

Remote state, done right

  • S3 backend + DynamoDB locking, encrypted, least-privilege access
  • State isolation per environment; no shared-state blast radius
  • Import and refactor existing click-ops infrastructure into code

Policy-as-code

  • OPA/Conftest blocking public S3, open SGs, unencrypted RDS at plan time
  • Pre-merge checks in CI so bad infra never reaches apply
  • Cost + security guardrails baked into the pipeline

Migration & handoff

  • Convert existing AWS accounts to fully reproducible Terraform
  • CI/CD for plan/apply with approvals and drift detection
  • Runbooks + docs, or an ongoing retainer

Proof

11+Terraform repos run in production (fintech DevSecOps practice)
100%IaC coverage on greenfield builds
0public S3 objects — enforced at plan time
TerraformOpenTofuTerragruntAWSOPA/ConftestS3 remote stateDynamoDB lockCI/CDModules
## How a Terraform engagement runs

Teams usually call me at one of two points: they have clicked their way to a working AWS account and now need it reproducible, or they have Terraform already and it has become something nobody wants to run on a Friday. The work differs, but both start with reading what exists rather than proposing a rewrite.

1. Read the estate

  • What is actually deployed versus what the code claims
  • Where state lives, who can write it, and whether it is locked
  • Account and environment boundaries, and what crosses them
  • The manual changes people make and why - those are requirements in disguise

2. Get state safe

  • Remote state in S3 with versioning and encryption enabled
  • State locking so two applies cannot race each other
  • State split along blast-radius lines rather than one monolith
  • Least-privilege access - most people should never write state directly

3. Import, don't rebuild

  • Existing resources imported into state rather than recreated
  • Plans driven to a genuine no-op before anything changes
  • Each import verified individually - a clean plan is the acceptance test

4. Make it routine

  • Plan on pull request, apply on merge, with the plan output reviewable
  • Policy checks in the pipeline so violations fail the build
  • Drift detection scheduled, not discovered during an incident
  • A rollback path that has been used at least once deliberately
## State: the part that actually breaks

Almost every serious Terraform incident I have been called into traces back to state rather than to the resource code. It is worth being blunt about the failure modes.

Local state means one laptop is the source of truth for production. When that laptop is unavailable - or its owner has left - you are reconstructing reality by hand. Remote state in S3 with versioning is the fix, and versioning matters as much as remoteness because it is what lets you recover from a bad write.

No locking means two applies can run at once and interleave their writes. The result is state that describes an infrastructure that never existed. Locking is a small amount of configuration that removes an entire category of very bad day.

One giant state file is the most common structural mistake. Every plan touches everything, applies get slow, and the blast radius of a mistake is the whole estate. Splitting state along boundaries that already exist - per environment, per account, per bounded service - makes plans fast and failures local. The cost is wiring outputs between stacks, which is a fair trade.

Hand-edited state is where confidence gets lost. It is occasionally necessary, but it should be rare, deliberate, backed up first, and followed by a plan that proves the result is consistent.

## Modules that survive contact with a second team

Module design is where Terraform codebases either stay maintainable or quietly become worse than the console. A few principles I hold to.

Write modules for the pattern you have used at least twice. A module abstracted from a single use case encodes one team's assumptions as if they were universal, and the second consumer immediately needs an escape hatch. The result is a module with thirty variables that is harder to read than the resources it wraps.

Pin versions - provider versions, module versions, and the Terraform version itself. Unpinned dependencies mean your infrastructure changes when someone else publishes, which is the opposite of infrastructure as code. Pinning is what makes a plan from three months ago still mean something.

Keep the interface small and the defaults sensible. A module that requires twelve inputs to do the common thing will be copy-pasted instead of reused, and copies do not receive fixes.

Prefer boring composition over clever abstraction. Nested modules four layers deep are impressive until someone has to trace why a security group rule exists at three in the morning. Debuggability is a design requirement.

## Drift, policy, and keeping it honest

Terraform describes intent. Reality diverges from intent constantly - someone fixes something urgently in the console, a service updates a default, an autoscaling process changes a value. The question is not whether drift happens but whether you find out on your terms.

Scheduled drift detection - a plan run on a cadence that reports differences without applying them - turns drift from a surprise into a list. Most detected drift is benign, and the value is that the small proportion that is not gets seen while it is still small.

Policy as code is the other half. Checks that run in the pipeline - no unencrypted storage, no public access on data buckets, no security group open to the world on admin ports, mandatory tagging for cost allocation - catch the mistake at review time rather than at audit time. This matters more as AI-assisted code generation raises the volume of infrastructure changes: the review bottleneck moves, so the guardrails have to be automated rather than human.

Where this connects to compliance work: the same policy checks that stop a misconfiguration are also the evidence an auditor wants that misconfigurations are systematically prevented. Built once, they serve both. See DevOps and compliance and secure cloud landing zone for how that fits together, and Terraform vs CloudFormation if you are still choosing.

FAQ

Do you use Terraform or OpenTofu?

Both — I default to whichever your team standardizes on. Modules and patterns are portable across Terraform and OpenTofu.

Can you refactor our existing click-ops AWS into Terraform?

Yes. I import existing resources, restructure into modules, add remote state and CI, and hand you a fully reproducible codebase.

How do you keep state safe?

Encrypted S3 backend with DynamoDB locking, per-environment isolation, and least-privilege IAM — no shared-state blast radius.

Do you add security controls?

Yes — OPA/Conftest policies block public buckets, open security groups and unencrypted storage at plan time, mapped to SOC 2/ISO 27001.

We have infrastructure built by hand. Do we have to start over?

No, and you should not. Existing resources get imported into Terraform state and the code written to match what is already deployed, verified by driving the plan to a clean no-op. It is more careful work than a greenfield build, but it avoids downtime and avoids losing the undocumented settings that are keeping things running.

Terraform or OpenTofu?

For most teams the practical difference is licensing rather than capability, and the configuration language is compatible. If your organisation has a policy position on the BSL licence change, that decides it. If not, either is defensible - I will work with whichever you standardise on rather than push a migration you did not ask for.

How long before we can stop using the console?

For a moderate AWS footprint, four to eight weeks to have the core estate under code with a working pipeline. Full coverage takes longer and is often not the right goal - some things genuinely belong outside Terraform, and pretending otherwise creates worse problems than it solves.

Who runs applies after you leave?

Your pipeline does, which is the point. Handover covers the module structure, the state layout and why it is split the way it is, the rollback procedure, and a working session on the failure modes specific to your setup. If applies still require me afterwards, the engagement did not succeed.

Turn your AWS into reproducible code.

Free 30-minute call — show me your current setup and I'll map the path to clean IaC.

Book a callAll AWS DevOps services →