How to attach multiple IAM policies to IAM roles using Terraform?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Attaching multiple IAM policies to a role in Terraform is straightforward once you model the relationship correctly. The common anti-pattern is duplicating many aws_iam_role_policy_attachment blocks manually, which becomes brittle as policy sets differ by environment. A better approach is to define policy ARNs as data and iterate with for_each.
This keeps role permissions explicit, reviewable, and easy to update. It also prevents accidental drift when teams copy-paste attachment resources and forget to remove or rename one during refactors. With a data-driven pattern, adding or removing a permission becomes a one-line change.
Core Sections
1. Create the role once with minimal trust policy
Start with a clean role definition and focused trust relationship.
Keep trust policy separate from permission policy attachments. They solve different authorization problems.
2. Attach multiple managed policies using for_each
Define the set of policy ARNs and iterate deterministically.
Using a set prevents duplicate attachments and keeps plans stable.
3. Mix AWS-managed and custom policies
If you have custom least-privilege policies, create them and merge ARNs into one iterable collection.
Then iterate over local.all_policy_arns in the same attachment resource.
4. Validate attachments in plan and AWS
Run:
After apply, verify role policies in IAM and run an identity-based integration test from the workload context. Terraform success does not guarantee runtime permissions are sufficient.
5. Scale across environments with variables
Store environment-specific policy ARNs in *.tfvars and pass them into a module. This avoids hardcoding production permissions in shared modules and supports least privilege per environment.
Common Pitfalls
- Copy-pasting many attachment resources instead of iterating from data.
- Mixing trust policy and permission policy logic in one place, making reviews harder.
- Attaching broad managed policies in production without workload-level validation.
- Using lists with duplicates, which can produce noisy plans and confusing diffs.
- Skipping runtime permission tests after Terraform apply.
Summary
The maintainable Terraform pattern for multiple IAM policy attachments is role definition plus iterable attachment resources. Use for_each over a set of policy ARNs, combine managed and custom policies cleanly, and validate both infrastructure state and runtime access. This approach keeps IAM readable, minimizes drift, and makes permission changes deliberate instead of accidental.
A practical way to keep this issue from returning is to turn the fix into a lightweight runbook. Capture the exact environment assumptions (tool versions, runtime flags, cluster or platform settings, and required dependencies), then store a short verification command sequence that any teammate can run from a clean setup. This makes troubleshooting deterministic instead of person-dependent and reduces rework during on-call incidents.
It also helps to add one automated guardrail in CI or pre-deploy checks that validates the critical assumption described above. That guardrail might be a linter rule, a smoke test, a schema check, a policy validation step, or a minimal integration test. When the same class of failure is caught before release, teams spend less time on emergency debugging and more time on controlled improvements.

