Skip to content
← Platform engineering

Learning bite

Terraform modules and state boundaries

Build a provisioning interface with explicit state ownership and reviewed changes.

Documentation reviewed2026-10-01 · 3 min read
On this page

A module is an interface, a state file is a record

A Terraform module groups configuration behind named inputs and outputs. State records the relationship between Terraform addresses and managed objects. These serve different purposes: placing resources in separate modules does not automatically place them in separate states or give them different permissions.

Reuse interfaces, separate authority

The Foundations path introduced Terraform modules. At platform scope, a module becomes a supported provisioning interface: inputs express allowed choices, outputs provide dependencies, and version changes communicate compatibility. Keep unrelated network, database, and application lifecycles visible instead of hiding them behind many switches in one module.

Choose state boundaries based on ownership, credentials, and change lifecycle. Cloud network infrastructure and an application workload need not share state. Keep Kubernetes application reconciliation under Argo CD once that ownership is chosen; do not also manage the same Deployment using Terraform.

Workspaces are not an authorization boundary

Terraform CLI workspaces select distinct state instances in a backend. They are not appropriate as the sole boundary for deployments requiring separate credentials or access controls. HCP Terraform workspaces have their own configuration and execution model; do not treat the two features as interchangeable.

For a first cloud adapter, use an explicit root configuration and backend for that target, with reviewed provider credentials. Never copy the LocalStack state file into a real-cloud root. Keep saved plans and state private, including when outputs are marked sensitive.

Split by operating responsibility

Consider a small platform design with three independently maintained parts:

PartChanges whenUseful outputsIntended manager
Network foundationNetwork design changesSubnet or network identifiersA reviewed Terraform root
Cluster targetCluster capacity/version changesConnection and target metadataA separate target root where appropriate
MicroBank workloadsApplication configuration changesDeployment and health observationsArgo CD

The rows are a design example, not a command to split an existing state file immediately. Splitting changes how dependencies are passed, how access is controlled and how failures are recovered. If two roots both declare the same cloud object, the design has duplicate ownership rather than useful separation.

Draft one module interface on paper. Inputs should express supported choices with types and validation. Outputs should provide dependencies without unnecessarily exposing credentials. Identify the person who approves an upgrade and the consumer test that checks compatibility.

Then ask where the actual state lives and which identity can write it. CLI workspaces choose state instances within the backend model; a workspace name does not enforce separate credentials. Reuse the Terraform foundations lessons for the precise backend and migration mechanics before making any real split.

Try it

Write a module interface for the first dependency you intend to provision. Define input types, validation, stable outputs, provider requirements, state owner, and upgrade notes. Prefer a small proven module over implementing both EKS and GKE behind an untested generic switch.

Review the sequence init → validate → plan → inspect → approved apply and identify where the exact reviewed plan is retained. A changed configuration or target requires a new plan. Drift detection identifies a difference; the owner still decides whether to update the declaration or restore the intended resource.

Checkpoint and revision

Explain why a local emulator plan cannot establish a cloud plan, and why state locking does not replace correct credentials or review. Make provider differences visible in the provisioning interface so callers can make informed choices.

Compare your reasoning

Do not migrate emulator state into a real-cloud root: the recorded objects and provider target are different. A locking backend helps serialize state-changing operations, but it does not correct a wrong identity, target or ownership design.

Sources

Terraform module composition↗, CLI workspaces↗, Terraform S3 backend↗, Terraform plan↗.

Your notes and evidence

Record observations, questions, or links to your work. Keep credentials out of your notes.

Loading saved progress…

Back up or restore this path

Progress and notes stay in this browser. A backup contains only this learning path.