Terraform Without a Platform Team
10 Sep 2026Terraform without a platform team is a different discipline than Terraform at a company with a dedicated infra org. The tool is the same; the guardrails you’d normally get from a team of reviewers have to come from your workflow instead. Here’s a setup one person can actually run.
State: remote, locked, from day one
Local state works until two people (or you, from two laptops) apply at the same time and corrupt it. Set up remote state before you write your first resource, not after the first incident.
terraform {
backend "s3" {
bucket = "yourco-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
The dynamodb_table gives you state locking — a second apply while one is running fails loudly instead of racing. Without it, the failure mode is silent state corruption, which is a much worse afternoon.
Structure: one state file per environment, not per resource
Splitting state too finely means managing dependencies between states with terraform_remote_state data sources, which is real overhead for a solo operator. Splitting too coarsely (one state for everything, all environments) means a bad apply in staging can touch prod. The workable middle for a small team:
environments/
prod/main.tf
staging/main.tf
modules/
network/
app-service/
database/
Each environment is its own state, and shared logic lives in modules both environments call with different variables. You get isolation between prod and staging without the overhead of a state file per microservice.
Workflow: plan in CI, apply from one place
terraform plan on every pull request, posted as a comment, catches most mistakes before they touch anything real — a resource you didn’t mean to destroy shows up in the diff, not in the incident channel.
flowchart LR
A[Open a PR] --> B[CI runs terraform plan]
B --> C[Plan posted as PR comment]
C --> D{Diff looks right?}
D -- No --> E[Fix and push again]
E --> B
D -- Yes --> F[Merge to main]
F --> G[CI applies from main]
# .github/workflows/terraform-plan.yml
on: pull_request
jobs:
plan:
steps:
- uses: hashicorp/setup-terraform@v3
- run: terraform init
- run: terraform plan -out=tfplan
- run: terraform show -no-color tfplan
Apply only from CI on merge to main, or from one person’s machine with a documented habit of always running plan immediately before apply and reading the diff. Never apply from whichever laptop happens to have the freshest checkout — that’s how drift and surprise deletions happen.
Drift: check it, don’t assume it away
Without a platform team, someone will eventually click a change in the cloud console under time pressure, and now your state lies. Run a scheduled terraform plan (a weekly cron in CI is enough) and alert on unexpected diffs, rather than discovering drift the next time you touch that resource for real.
What to skip until you actually need it
- Terraform Cloud/Enterprise for remote runs and policy-as-code — a GitHub Actions workflow does the job for a team this size.
- A custom internal module registry — a
modules/folder in the same repo is fine until multiple repos need to share modules. - Workspaces (
terraform workspace) as your environment-separation strategy — they’re easy to reach for but easy toapplyinto the wrong one by accident. Separate state files per environment, as above, are harder to get wrong.
The gotcha
The single most common solo-Terraform incident isn’t a bad apply, it’s someone running terraform destroy (or an apply that implies a destroy) against the wrong workspace or directory because they didn’t check which environment their shell was pointed at. Alias terraform to a wrapper that prints the target state file before every plan and apply, or put the environment name in your shell prompt. Cheap insurance against the one mistake that actually hurts.