You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform多EC2实例部署:避免重复生成IAM角色的方案咨询

问题描述

使用terraform-aws-ec2-instance模块创建多台EC2实例时,通过for_each批量调用模块会导致模块内的IAM角色、实例配置文件等资源重复生成,但实际需求是多台实例共用一套IAM资源,且不想修改第三方模块。当前代码示例如下:

main.tf

locals {
  name   = "my-app-server"
  multiple_instances = var.multiple_instances_params
}

module "ec2_multiple" {
  source = "../../"
  for_each = local.multiple_instances

  name = "${local.name}-${each.key}"
  ami_ssm_parameter      = var.ami_ssm_parameter
  instance_type          = each.value.instance_type
  availability_zone      = each.value.availability_zone
  subnet_id              = each.value.subnet_id
  private_ip             = each.value.private_ip
  vpc_security_group_ids = [module.App-server-Security-Group.security_group_id]

  enable_volume_tags = false
  root_block_device  = lookup(each.value, "root_block_device", [])
  user_data          = var.app_server_user_data
  key_name           = var.app_server_keyName
  tags               = var.mandatory_tags

  iam_role_name               = "${local.name}-ec2-iam-role"
  iam_role_use_name_prefix    = false
  create_iam_instance_profile = true
  iam_role_description        = "xxxx"
  iam_role_permissions_boundary      = var.permissions_boundary
  iam_role_policies       = {
    "ALM_policy" = aws_iam_policy.ALM_policy.arn
    "CW_agent_Policy" = aws_iam_policy.Cloud_Watch_Agent_Server_Policy.arn
  }
}

terraform.tfvars

multiple_instances_params = {
    server-1 = {
      instance_type     = "t3.micro"
      availability_zone = "eu-central-1a"
      subnet_id         = "subnet-xxxa3fxx"
      private_ip        = "10.xx.xx.xxx"
      root_block_device = [
        {
          delete_on_termination  = false
          encrypted   = true
          kms_key_id  = "xxx"
          volume_type = "gp3"
          throughput  = 200
          volume_size = 100
          tags = {
            Name = "my-root-block"
          }
        }
      ]
    }
    server-2 = {
      instance_type     = "t3.small"
      availability_zone = "eu-central-1a"
      subnet_id         = "subnet-xxx16daxxxe"
      private_ip        = "10.xx.xx.xxx"
      root_block_device = [
        {
          delete_on_termination  = false
          encrypted   = true
          kms_key_id  = "xxx"
          volume_type = "gp3"
          throughput  = 200
          volume_size = 100
          tags = {
            Name = "my-root-block"
          }
        }
      ]
    }
    # 省略其他实例配置
}

解决方案

方案1:单独创建IAM资源,传递给EC2模块

这是最直接的实现方式,无需修改第三方模块,只需在根模块中单独创建一套IAM角色和实例配置文件,再通过模块参数传递给所有EC2实例。

步骤1:创建共用的IAM资源

在main.tf中添加以下代码,单独生成IAM角色、策略绑定和实例配置文件:

# 创建共用IAM角色
resource "aws_iam_role" "ec2_common_role" {
  name                  = "${local.name}-ec2-iam-role"
  description           = "xxxx"
  permissions_boundary  = var.permissions_boundary

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      }
    ]
  })
}

# 绑定所需策略到角色
resource "aws_iam_role_policy_attachment" "ec2_common_policy_attach" {
  for_each = {
    "ALM_policy" = aws_iam_policy.ALM_policy.arn
    "CW_agent_Policy" = aws_iam_policy.Cloud_Watch_Agent_Server_Policy.arn
  }

  role       = aws_iam_role.ec2_common_role.name
  policy_arn = each.value
}

# 创建共用实例配置文件
resource "aws_iam_instance_profile" "ec2_common_profile" {
  name = "${local.name}-ec2-instance-profile"
  role = aws_iam_role.ec2_common_role.name
}

步骤2:修改EC2模块调用

关闭模块内的IAM资源创建逻辑,传入已生成的实例配置文件ARN:

module "ec2_multiple" {
  source = "../../"
  for_each = local.multiple_instances

  name = "${local.name}-${each.key}"
  ami_ssm_parameter      = var.ami_ssm_parameter
  instance_type          = each.value.instance_type
  availability_zone      = each.value.availability_zone
  subnet_id              = each.value.subnet_id
  private_ip             = each.value.private_ip
  vpc_security_group_ids = [module.App-server-Security-Group.security_group_id]

  enable_volume_tags = false
  root_block_device  = lookup(each.value, "root_block_device", [])
  user_data          = var.app_server_user_data
  key_name           = var.app_server_keyName
  tags               = var.mandatory_tags

  # 关闭模块内IAM资源创建
  create_iam_role              = false
  create_iam_instance_profile = false
  # 传入共用的实例配置文件ARN
  iam_instance_profile_arn     = aws_iam_instance_profile.ec2_common_profile.arn
}

方案2:关于count vs for_each的选择

count和for_each都是批量调用模块的方式,核心问题不在于使用哪个元参数,而在于是否让模块重复创建IAM资源。只要模块内开启了create_iam_role或create_iam_instance_profile,无论用count还是for_each都会重复生成IAM资源。

如果你的实例配置是列表格式,用count更合适;如果是键值对(如示例中的server-1、server-2),for_each的可读性和维护性更好。

方案3:自定义封装模块(适合重复使用场景)

如果需要多次实现“多EC2实例共用IAM”的模式,可以封装一个自定义模块,内部先创建一次IAM资源,再批量调用原EC2模块。

自定义模块示例

创建modules/ec2-with-shared-iam模块,目录结构:

modules/
  ec2-with-shared-iam/
    main.tf
    variables.tf
    outputs.tf

main.tf(模块内部)

# 创建共用IAM角色
resource "aws_iam_role" "ec2_role" {
  name                  = var.iam_role_name
  description           = var.iam_role_description
  permissions_boundary  = var.iam_role_permissions_boundary

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = { Service = "ec2.amazonaws.com" }
    }]
  })
}

# 绑定策略
resource "aws_iam_role_policy_attachment" "policy_attach" {
  for_each = var.iam_role_policies
  role     = aws_iam_role.ec2_role.name
  policy_arn = each.value
}

# 创建实例配置文件
resource "aws_iam_instance_profile" "ec2_profile" {
  name = "${var.iam_role_name}-instance-profile"
  role = aws_iam_role.ec2_role.name
}

# 批量创建EC2实例
module "ec2_instances" {
  source = "terraform-aws-modules/ec2-instance/aws"
  version = "~> 5.0" # 替换为你使用的模块版本

  for_each = var.multiple_instances

  name = "${var.base_name}-${each.key}"
  ami_ssm_parameter      = var.ami_ssm_parameter
  instance_type          = each.value.instance_type
  availability_zone      = each.value.availability_zone
  subnet_id              = each.value.subnet_id
  private_ip             = each.value.private_ip
  vpc_security_group_ids = var.vpc_security_group_ids

  enable_volume_tags = var.enable_volume_tags
  root_block_device  = lookup(each.value, "root_block_device", [])
  user_data          = var.user_data
  key_name           = var.key_name
  tags               = var.tags

  # 关闭原模块IAM创建逻辑
  create_iam_role              = false
  create_iam_instance_profile = false
  iam_instance_profile_arn     = aws_iam_instance_profile.ec2_profile.arn
}

# 输出实例ID
output "instance_ids" {
  type = map(string)
  value = { for k, v in module.ec2_instances : k => v.id }
}

根模块调用自定义模块

module "app_servers" {
  source = "./modules/ec2-with-shared-iam"

  base_name = "my-app-server"
  multiple_instances = var.multiple_instances_params

  ami_ssm_parameter      = var.ami_ssm_parameter
  vpc_security_group_ids = [module.App-server-Security-Group.security_group_id]
  enable_volume_tags = false
  user_data          = var.app_server_user_data
  key_name           = var.app_server_keyName
  tags               = var.mandatory_tags

  iam_role_name               = "${local.name}-ec2-iam-role"
  iam_role_description        = "xxxx"
  iam_role_permissions_boundary = var.permissions_boundary
  iam_role_policies       = {
    "ALM_policy" = aws_iam_policy.ALM_policy.arn
    "CW_agent_Policy" = aws_iam_policy.Cloud_Watch_Agent_Server_Policy.arn
  }
}

内容的提问来源于stack exchange,提问作者Saurabh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 13:16:58