
In May 2021 I joined a US fintech for on-demand pay as its first DevOps engineer. A lot of the AWS estate had been built by hand in the console. It worked, but nobody could rebuild an environment from code, and each new region meant doing the same clicks again.
In May 2022 I proposed an infrastructure-as-code standard to the company's architecture forum. A month later, the repository held 116 Terragrunt units across four AWS regions. A 2022 internal review put the time to deploy infrastructure 70% lower than before.
Four years later I set up infrastructure for my own product with the same tools. Some things I did again exactly. Some I changed.
The proposal was short. Infrastructure is immutable: you change code, not servers. Environments and accounts are separate. Everything goes through a merge request. Terraform describes resources, and Terragrunt handles the layout and the repetition between environments and regions.
The named risk was the legacy. Many resources already existed. A standard that only applies to new things leaves the old things as they were, and the old things were the ones nobody fully understood.
So the first work was importing. Existing resources were brought into Terraform state and described in code before anything new was built on top.
Importing is slow and dull. You write the resource, import it, run a plan, and adjust the code until the plan shows no changes. A plan with no changes is the only proof that the code describes what exists. Any difference is either a bug in the code or a setting someone changed in the console and forgot.
I would do this the same way today. Teams that skip it end up with two worlds, one in code and one in the console, and every incident has to be debugged in both. Terraform has since added import blocks, which make the work easier to review, but the shape is the same: describe, import, plan until clean.
By June 2022 the repository had four environments (development, staging, production and sandbox) and four AWS regions, plus a global folder for resources that are not regional. Two regions held about 50 units each, and two smaller ones held 9 and 3.
Each unit was one Terragrunt folder calling a module. The modules covered networking, databases, container services and registries, identity, customer encryption keys, queues and messaging, DynamoDB, AWS Config rules, API Gateway with Lambda, and data ingestion.
The uneven split is normal. Not every region needs everything. A region for disaster recovery or a small market can have a handful of units, and the layout makes that visible: you can see what exists where by listing folders.
The review tied the 70% to reusable modules and code. From where I sat, three things worked together.
The first was reuse. A new environment or region was mostly copying folders and changing inputs, not writing new code.
The second was review. A merge request with a plan is something another engineer can read in a few minutes. A ticket that says "please create a queue in production" needs someone to go and click.
The third was CI. Each region had its own CI templates, so a merge request planned and applied in the right place, the same way every time.
When I set up infrastructure in 2026 for redir.rocks, I used Terragrunt again, with 15 modules. Three things are different.
CI runs only what changed. Planning everything in a region on every merge request gets slow as a repository grows. Today I run plans only for the units a merge request changed, plus the units that depend on them. Plans are faster and the output is shorter, which makes reviews better.
State locking uses S3 directly. Terraform can now lock state with S3 itself, without a separate DynamoDB table. It is one less resource to create in every account and one less thing to explain.
One account per product, from the first day. For my own estate, every product has its own account and the management account runs nothing. Billing, permissions and handover are simpler when the account boundary matches the product boundary.
Infrastructure as code tells you what exists and who changed it. It does not tell you what it costs. A few years later, a review of the same company's bill found log ingestion that had grown four times in two months and older volume types still running next to cheaper ones. All of it was in code. None of it was a code problem. That needs a separate habit: someone reads the bill every month.
If your AWS estate is half in code and half in the console, I can help get it into one place: AWS architecture.
154 import blocks, before and after snapshots, and zero changes to ACLs, policies or public access. Plus a deletion list that held live log destinations.
Our edge could not issue one TLS certificate because port 80 already redirected to HTTPS. How I fixed it without a proxy, and a new AWS account surprise.
A CDN forwarded the Origin header to S3 but left it out of the cache key, so the first site to load each font decided who else could use it.