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
相关产品推荐
相关产品推荐

