Terraform module for AWS Control Tower — IAM users, roles, groups, and IAM Identity Center (SSO) permission sets, designed according to the AWS Well-Architected Framework Security Pillar.
┌─────────────────────────────────────────────────────────────┐
│ AWS Control Tower │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ IAM Identity Center (SSO) │ │
│ │ PermissionSet: Admin · Developer · ReadOnly · Sec │ │
│ │ AccountAssignments → member accounts │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Account Password Policy │ │
│ │ min 14 chars · MFA required · 90-day rotation │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ group ┌──────────────────────┐ │
│ │ IAM Users │ ─membership─▶ │ IAM Groups │ │
│ │ alice, bob │ │ admin · developer │ │
│ │ charlie... │ │ readonly · security │ │
│ └──────────────┘ └──────────┬───────────┘ │
│ │ sts:AssumeRole │
│ ┌────────▼───────────┐ │
│ │ IAM Roles │ │
│ │ + MFA condition │ │
│ │ + 1h max session │ │
│ └────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
| WAF Security Pillar | Implementation |
|---|---|
| SEC 02 — Strong sign-in | Account password policy: 14 chars min, MFA required on all roles |
| SEC 02 — Credentials | Console passwords PGP-encrypted; password_reset_required = true |
| SEC 03 — Least privilege | 4 role tiers — no user has direct policies |
| SEC 03 — Separation of duties | Users → Groups → Roles (no direct user-to-role trust) |
| SEC 03 — Session limits | max_session_duration = 3600s; admin SSO session = 1 h |
| SEC 08 — Auditability | All resources tagged; compatible with CloudTrail + AWS Config |
| Role | Policy | Use case |
|---|---|---|
admin |
AdministratorAccess |
Break-glass only |
developer |
PowerUserAccess |
Compute, storage, CI/CD — no IAM write |
readonly |
ReadOnlyAccess |
Auditing and observability |
security |
SecurityAudit + GuardDutyReadOnly |
Security team |
.
├── main.tf # Root: password policy + module calls
├── variables.tf # All inputs
├── outputs.tf
├── versions.tf # AWS ~> 5.50, TF >= 1.6.0
├── environments/
│ └── prod/terraform.tfvars # Example values
└── modules/
├── iam_roles/ # IAM roles + groups per tier
├── iam_users/ # IAM users + login profiles + group membership
└── sso/ # IAM Identity Center permission sets + assignments
- Terraform >= 1.6.0
- AWS CLI configured with credentials for the Control Tower management account
- GitHub CLI (
gh) authenticated (optional, for repo management) - An S3 bucket + DynamoDB table for remote state
Create environments/prod/backend.hcl:
bucket = "my-terraform-state"
key = "control-tower/users/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = trueEdit environments/prod/terraform.tfvars with your account IDs, users, and (optionally) your SSO instance ARN:
# Find your SSO instance ARN
aws sso-admin list-instances --query 'Instances[0].InstanceArn' --output textterraform init -backend-config=environments/prod/backend.hcl
terraform plan -var-file=environments/prod/terraform.tfvars
terraform apply -var-file=environments/prod/terraform.tfvarsIf users were created with a PGP key, decrypt their console passwords:
terraform output -json encrypted_passwords \
| jq -r '.alice' \
| base64 --decode \
| gpg --decrypt| Name | Type | Default | Description |
|---|---|---|---|
aws_region |
string | us-east-1 |
AWS region |
project |
string | — | Project name (used in resource names and tags) |
environment |
string | — | Environment (prod, staging, dev) |
password_min_length |
number | 14 |
Minimum IAM password length |
password_reuse_prevention |
number | 24 |
Number of previous passwords blocked |
max_password_age |
number | 90 |
Max password age in days |
trusted_account_ids |
list(string) | [] |
Accounts allowed to assume roles (e.g., SSO mgmt account) |
roles |
map(object) | 4 WAF tiers | IAM roles to create |
users |
map(object) | {} |
IAM users to create |
sso_instance_arn |
string | "" |
SSO instance ARN — leave empty to skip SSO resources |
sso_permission_sets |
map(object) | 4 WAF tiers | Permission sets to create in IAM Identity Center |
sso_account_assignments |
list(object) | [] |
Principal → account → permission-set assignments |
| Name | Description |
|---|---|
role_arns |
Map of role name → ARN |
group_names |
Map of role name → IAM group name |
user_arns |
Map of username → ARN |
encrypted_passwords |
PGP-encrypted console passwords (sensitive) |
sso_permission_set_arns |
Map of permission set name → ARN |
- Never commit
terraform.tfvarsfiles containing real account IDs, user names, or PGP keys — add them to.gitignoreif they contain sensitive data. - The
adminrole requires MFA and has a 1-hour session. Assign it only to break-glass users. - Rotate IAM access keys regularly. Prefer SSO/federated access over long-lived IAM user credentials.
MIT