驻专讜讬拽讟 讛-IAM Users 讻讬住讛 诪住驻专 转讻讜谞讜转 诪转拽讚诪讜转 砖诇 Terraform:
yamldecode(), for_each, count, flatten, containsuser-roles.yaml# user-roles.yaml
# Schema reference:
# users: array of objects
# username: string
# roles: array of [readonly | developer | admin | auditor]
users:
- username: john
roles:
- readonly
- developer
- username: jane
roles:
- admin
- auditor
- username: laura
roles:
- readonly
provider.tfterraform {
required_version = "~> 1.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
users.tflocals {
# Load and decode the YAML file
users_from_yaml = yamldecode(file("${path.module}/user-roles.yaml")).users
# Transform list of objects into a map: username => roles
# This makes it easy to look up roles by username
users_map = {
for user_config in local.users_from_yaml :
user_config.username => user_config.roles
# Note: duplicate usernames will throw an error - this is intentional!
}
}
# Create one IAM user per username in the YAML
resource "aws_iam_user" "users" {
for_each = toset([for user in local.users_from_yaml : user.username])
name = each.value
tags = {
ManagedBy = "Terraform"
}
}
# Create login profiles so users can sign in to the AWS Console
resource "aws_iam_user_login_profile" "users" {
for_each = aws_iam_user.users
user = each.value.name
password_length = 8
lifecycle {
# Prevent Terraform from resetting passwords of existing users
# when these settings change in code
ignore_changes = [password_length, password_reset_required, pgp_key]
}
}
# Output passwords - FOR LEARNING ONLY, never do this in production!
output "passwords" {
value = {
for username, login in aws_iam_user_login_profile.users :
username => login.password
}
sensitive = true
}
roles.tf# Get current AWS account ID dynamically
data "aws_caller_identity" "current" {}
locals {
# Define which AWS Managed Policies are attached to each role
role_policies = {
readonly = [
"ReadOnlyAccess",
]
developer = [
"AmazonVPCFullAccess",
"AmazonEC2FullAccess",
"AmazonRDSFullAccess",
]
admin = [
"AdministratorAccess",
]
auditor = [
"SecurityAudit",
]
}
# Flatten map of role=>policies into a list of {role, policy} objects
# Needed so we can create one policy attachment per (role, policy) pair
#
# From: { developer: ["AmazonEC2FullAccess", "AmazonVPCFullAccess"] }
# To: [ {role: "developer", policy: "AmazonEC2FullAccess"},
# {role: "developer", policy: "AmazonVPCFullAccess"} ]
role_policies_list = flatten([
for role, policies in local.role_policies : [
for policy in policies : {
role = role
policy = policy
}
]
])
}
# Build a separate Assume Role Policy document for each role.
# Each document lists only the users who have that role in the YAML.
data "aws_iam_policy_document" "assume_role_policy" {
for_each = toset(keys(local.role_policies))
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
# Build the list of ARNs for users who have this role assigned
identifiers = [
for username in keys(aws_iam_user.users) :
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/${username}"
if contains(local.users_map[username], each.value)
]
}
}
}
# Create the four IAM Roles
resource "aws_iam_role" "roles" {
for_each = toset(keys(local.role_policies))
name = each.key
# Use the role-specific assume role policy document
assume_role_policy = data.aws_iam_policy_document.assume_role_policy[each.key].json
tags = {
ManagedBy = "Terraform"
}
}
# Fetch each AWS Managed Policy from AWS (to get its full ARN)
data "aws_iam_policy" "managed_policies" {
for_each = toset([for entry in local.role_policies_list : entry.policy])
arn = "arn:aws:iam::aws:policy/${each.value}"
}
# Attach each policy to its role - one attachment per (role, policy) pair
resource "aws_iam_role_policy_attachment" "role_policy_attachments" {
count = length(local.role_policies_list)
role = aws_iam_role.roles[local.role_policies_list[count.index].role].name
policy_arn = data.aws_iam_policy.managed_policies[local.role_policies_list[count.index].policy].arn
}