Skip to content
cirlume.
Menu
← All insights

Cloud & delivery · · 5 min read · By the Cirlume studio

Covers Terraform

Terraform from day one: infrastructure as code that lasts

How to start with Terraform in the first environment, so infrastructure stays reviewable and a new team can take it over.

A laptop showing an infrastructure diagram of connected boxes, beside a small server unit and a notebook

Most production setups that nobody understands started the same way: someone created a database and a few services in the cloud console to get a first version running, intending to tidy it up later. Later rarely comes. A year on, nobody is sure which settings matter, staging no longer matches production, and recreating the setup would mean reverse-engineering it.

Writing infrastructure as code from the first environment avoids that. SoulCare, our home-care platform, has its infrastructure in Terraform. This article covers how we would set up a new project the same way: repository layout, environments, state, review, secrets and what a team taking it over needs.

Why start on day one

Infrastructure as code is cheapest when there is little infrastructure. Importing an existing setup into Terraform means finding every resource, matching its settings and accepting whatever choices were made in a hurry. Starting with code means every resource has a reason, a history in version control and a reviewer.

It also makes a second environment straightforward. If production is defined in code, staging is a second set of inputs to the same code, not a weekend of clicking.

Repository layout

Keep the layout simple enough that someone new can find things. A common structure separates reusable modules from the environments that use them:

infra/
  modules/
    network/
    database/
    api/
  environments/
    staging/
      main.tf
      backend.tf
      terraform.tfvars
    production/
      main.tf
      backend.tf
      terraform.tfvars
  • Modules describe one part of the system, such as the network or the database, with inputs for anything that differs between environments.
  • Environments call those modules with their own values and their own state.
  • Start with fewer modules. It is easier to split a module later than to untangle one that was abstracted too early. A module that is used once is often just a folder.

Whether infrastructure lives in the application repository or its own is a judgement call. Keeping it alongside the application makes related changes reviewable together. A separate repository suits organisations where a platform team manages infrastructure for several products.

Modular blocks stacked beside a laptop showing code (illustration)

Environments

Each environment should be a separate state and, ideally, a separate cloud account. Account-level separation means a mistake in staging cannot touch production, and access can be granted per environment.

Keep the differences between environments explicit and small: instance sizes, domain names, scaling limits and retention periods. If staging is structurally different from production, it stops being a useful test of it.

Here is a simplified example of an environment calling a module, not our production code:

module "database" {
  source = "../../modules/database"

  name                = "app-staging"
  instance_class      = var.db_instance_class
  backup_retention    = 7
  deletion_protection = false
}

State

Terraform’s state file records what it has created. Treat it carefully.

  • Store it remotely. Use a remote backend, such as an S3 bucket with locking, so two people cannot apply changes at once and the state is not on one laptop.
  • One state per environment. A change in staging should never be able to plan changes to production.
  • Restrict and protect it. State can contain sensitive values. Encrypt the bucket, turn on versioning and limit who can read it.
  • Avoid editing it by hand. Moves and imports should be done with Terraform commands or moved blocks in code, so they are reviewed like any other change.

Reviewing plans in pull requests

The value of infrastructure as code comes from review. A change to a security group or a database setting should be read by a second person before it reaches production, in the same way as application code.

  • Run terraform plan in CI on every pull request and post the output where reviewers can see it.
  • Read the plan, not just the diff. The code diff shows intent. The plan shows what will actually be created, changed or destroyed. Pay particular attention to anything marked for replacement.
  • Apply from the pipeline, not from laptops. Once merged, apply the reviewed plan from CI using credentials scoped to that environment. This keeps a record of every change and who approved it.
  • Check formatting and validation automatically. terraform fmt and terraform validate catch simple mistakes before a reviewer has to.

Secrets

Secrets are where infrastructure code most often goes wrong.

  • Never commit them. Passwords, API keys and tokens do not belong in .tfvars files or anywhere else in the repository.
  • Use a secrets manager. Create secrets in the cloud provider’s secrets service and give applications permission to read them at runtime. Terraform can create the container for a secret without holding its value.
  • Prefer roles to keys. Give services and the CI pipeline roles with the permissions they need, rather than long-lived access keys.
  • Plan for rotation. Decide how each secret is changed and what restarts when it is. It is easier to design for this than to do it under pressure.

What a team inheriting the system needs

Infrastructure is often handed over: to an in-house team, a new supplier or a future version of the same team. Code alone is not enough. A useful handover includes:

  • a short guide to the repository layout and how to run a plan locally;
  • how environments map to cloud accounts and who has access to each;
  • where state is stored and how locking works;
  • the release process, from pull request to applied change;
  • where secrets live and how they are rotated;
  • anything created outside Terraform, with the reason, so it is not mistaken for drift;
  • known gaps and planned improvements.

If someone new can make a small, reviewed change to staging in their first week, the setup is in good shape.

The trade-offs

Terraform adds work at the start. A first environment takes longer to create in code than by hand, and the team needs to learn its model of state and plans. Some managed services change quickly, and their Terraform support can lag behind the console. There will be occasional manual steps, and they need to be written down.

For a throwaway prototype, clicking through the console is reasonable. For anything that will hold customer data or be handed to another team, the extra effort early is much smaller than reconstructing the setup later.

When to bring in help

If you are about to set up a first production environment, or you have one that was built by hand and nobody wants to touch, a review of how it is defined and deployed is a sensible first step. We can help structure the Terraform, set up plan review and applies in your pipeline, and write the handover your team will need to run it.

What’s changing

Shifts to plan for.

Dates that affect cloud & delivery work, checked against the official sources.

  1. June 2026

    Amazon Linux 2 reaches end of life

    Amazon Linux 2 reached end of life on 30 June 2026. Lambda runtimes built on it, such as Python 3.10 and 3.11 and older Java, get limited patches until their own deprecation dates.

  2. April 2026

    Lambda deprecates Node.js 20

    Lambda stopped patching the nodejs20.x runtime on 30 April 2026. Creating functions on it is blocked from 29 July 2027 and updating them from 31 August 2027.

  3. April 2026

    MySQL 8.0 reaches end of life

    Oracle moved MySQL 8.0 to Sustaining Support, so Oracle no longer releases security fixes for it. Oracle recommends moving to MySQL 8.4 LTS or 9.7 LTS.