Deploying to multiple AWS accounts with Terraform?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Deploying Terraform into multiple AWS accounts is a standard pattern for separating development, staging, and production. The key is to keep one source of truth for infrastructure code while isolating credentials, state, and permissions per account.
The cleanest design uses AWS IAM roles for cross-account access, provider aliases in Terraform, and separate state for each environment. That structure scales from two accounts to dozens without turning deployments into manual credential switching.
Use AWS Profiles That Assume Roles
Start by making account access explicit in the AWS CLI configuration. A common pattern is to authenticate once with a base identity and then assume a deployment role in each target account.
With this setup, Terraform does not need long-lived access keys for every account. It can rely on short-lived role sessions, which is better for security and easier to audit. Before touching Terraform, verify the role assumptions work:
If those commands return the expected account IDs, the authentication layer is ready.
Model Accounts with Provider Aliases
Terraform can talk to multiple AWS accounts in one configuration by declaring multiple aws providers. Each provider alias represents one account or one access pattern.
This keeps account selection near the module that uses it. It also avoids a fragile pattern where operators manually export a different profile and rerun the same command. For a multi-account setup, explicit provider aliases are easier to review and much harder to misuse.
Keep Terraform State Separate Per Account
Do not store development and production resources in the same state file. Separate state is what prevents a routine change in one account from accidentally affecting another.
One practical pattern is to keep small backend configuration files outside the Terraform code:
Then initialize each deployment target explicitly:
You can also split accounts into separate root modules if the infrastructure differs substantially. Provider aliases work well when the architecture is mostly shared. Separate roots are usually simpler when environments have meaningfully different stacks or release cycles.
Make Modules Account-Aware but Not Account-Coupled
Reusable Terraform modules should accept inputs such as CIDR ranges, naming prefixes, and feature flags, but they should not hard-code account IDs unless there is a strong reason. If a module truly needs the account ID, obtain it from the caller or from a data source.
That small detail matters because modules copied between accounts often break when account-specific values are embedded directly in resource names or policies.
Common Pitfalls
The most common mistake is sharing one state file across multiple accounts. That makes plans harder to reason about and increases the blast radius of every apply.
Another problem is hiding account selection in shell state, such as switching AWS_PROFILE manually and hoping the correct account is active. Explicit provider aliases are far safer because the target account is visible in the code.
Teams also underestimate IAM setup. Terraform can only be repeatable if every target account exposes a predictable deployment role with the permissions your modules require.
Finally, avoid assuming Terraform workspaces alone solve multi-account design. Workspaces can help with environment naming, but they are not a substitute for proper credential separation and separate state layout.
Summary
- Use AWS IAM roles and CLI profiles to access each account safely.
- Declare one Terraform
awsprovider alias per target account. - Pass providers into modules explicitly so account selection is visible in code.
- Keep state isolated per account, usually with separate backends or root modules.
- Treat IAM role design and state layout as core parts of the solution, not afterthoughts.

