馃幆 诪讛 讘谞讬谞讜 讘驻专讜讬拽讟 讛讝讛

驻专讜讬拽讟 讛-IAM Users 讻讬住讛 诪住驻专 转讻讜谞讜转 诪转拽讚诪讜转 砖诇 Terraform:


拽讜讘抓 讛住讜驻讬: user-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.tf

terraform {
  required_version = "~> 1.7"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

拽讜讘抓 讛住讜驻讬: users.tf

locals {
  # 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
}

讘讚讬拽讛 住讜驻讬转