如何让Terraform创建/修改AWS EC2实例时不自动启动?
我有多台AWS EC2实例,配置为启动时运行重处理工作负载(已通过多种机制确保在合适时间启动)。但当这些实例被重建或修改时,生成的实例会立即启动,导致必须手动停止以避免误运行工作负载。
这些实例通过Terraform模块中的aws_instance资源创建:
resource "aws_instance" "processing_instance" { ami = var.ami_id instance_type = var.instance_type key_name = var.ec2_key_pair subnet_id = var.aws_subnet_id user_data = "some heavy processing tasks" vpc_security_group_ids = [var.aws_security_group_id] iam_instance_profile = aws_iam_instance_profile.instance_profile.name associate_public_ip_address = true root_block_device { volume_size = var.volume_size } lifecycle { ignore_changes = [associate_public_ip_address] } }
实例运行计算任务后将结果写入S3存储桶,触发EventBridge事件关闭当前实例并启动下一个,相关规则配置如下:
resource "aws_cloudwatch_event_rule" "transition-rule" { for_each = local.steps # map with fields containing what key prefix to trigger on name = "${each.key}-event-rule" description = "Trigger next step in data flow" event_pattern = jsonencode({ "source" : ["aws.s3"], "detail-type" : ["Object Created"], "detail" : { "bucket": { "name": [aws_s3_bucket.bucket.bucket] }, "object": { "key": [{ "prefix": "${each.value.prefix}/" }] } } }) }
整个流程由Scheduler事件触发。我希望Terraform创建这些实例时不自动启动,避免手动停止的麻烦并防止误启动,请问最佳实现方式是什么?
以下是几种可行的实现方式,按推荐优先级排序:
方法1:利用标签+EventBridge自动停止新实例
给Terraform创建的实例添加专属标签,再配置EventBridge规则监听实例启动事件,匹配标签后自动触发停止操作,全程无需手动干预。
步骤1:给EC2实例添加标签
修改aws_instance资源,添加用于识别的标签:
resource "aws_instance" "processing_instance" { # 原有配置保持不变 tags = { Name = "Processing-Instance" AutoStopOnCreation = "true" } }
步骤2:配置EventBridge规则与执行权限
添加EventBridge规则监听实例启动事件,调用SSM文档停止匹配标签的实例:
# 监听EC2实例进入运行状态的事件 resource "aws_cloudwatch_event_rule" "stop-new-processing-instances" { name = "stop-new-processing-instances" description = "自动停止刚创建的重处理实例" event_pattern = jsonencode({ source = ["aws.ec2"], detail-type = ["EC2 Instance State-change Notification"], detail = { state = ["running"], "instance-id" = [{ "tag:AutoStopOnCreation" = ["true"] }] } }) } # 配置事件目标:调用SSM停止实例的文档 resource "aws_cloudwatch_event_target" "stop-instances-target" { rule = aws_cloudwatch_event_rule.stop-new-processing-instances.name target_id = "StopEC2Instance" arn = "arn:aws:ssm:${var.region}:${data.aws_caller_identity.current.account_id}:document/AWS-StopEC2Instance" input = jsonencode({ InstanceIds = ["<aws.instance-id>"] }) role_arn = aws_iam_role.eventbridge_ssm_role.arn } # 给EventBridge创建执行SSM操作的IAM角色 resource "aws_iam_role" "eventbridge_ssm_role" { name = "EventBridge-SSM-StopEC2-Role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "events.amazonaws.com" } }] }) } resource "aws_iam_role_policy_attachment" "eventbridge_ssm_policy" { role = aws_iam_role.eventbridge_ssm_role.name policy_arn = "arn:aws:iam::aws:policy/AmazonSSMAutomationRole" } # 获取当前账号ID(用于SSM文档ARN) data "aws_caller_identity" "current" {}
这种方式的优势是完全依赖AWS原生服务,不修改实例启动逻辑,也不影响原有Scheduler触发流程,稳定性高。
方法2:修改User Data控制启动行为
在实例的User Data中添加判断逻辑,只有满足特定条件时才执行重处理任务,否则直接停止实例。比如通过标签控制是否允许启动任务。
修改User Data示例
#!/bin/bash # 获取当前实例ID INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) # 查询是否允许执行任务的标签值 ALLOW_PROCESSING=$(aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=AllowProcessing" --query "Tags[0].Value" --output text) # 若不允许则停止实例 if [ "$ALLOW_PROCESSING" != "true" ]; then aws ec2 stop-instances --instance-ids $INSTANCE_ID exit 0 fi # 执行原有重处理任务 some heavy processing tasks
Terraform中配置默认标签与User Data
resource "aws_instance" "processing_instance" { # 原有配置不变 user_data = <<-EOF #!/bin/bash INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) ALLOW_PROCESSING=$(aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=AllowProcessing" --query "Tags[0].Value" --output text) if [ "$ALLOW_PROCESSING" != "true" ]; then aws ec2 stop-instances --instance-ids $INSTANCE_ID exit 0 fi some heavy processing tasks EOF tags = { Name = "Processing-Instance" AllowProcessing = "false" } }
当Scheduler需要启动任务时,只需将实例的AllowProcessing标签改为true,再启动实例即可触发任务执行。这种方式逻辑内聚在实例本身,无需额外的EventBridge规则。
方法3:Terraform创建后立即停止实例
利用null_resource配合local-exec provisioner,在实例创建完成后直接调用AWS CLI停止实例,适合小型测试环境。
Terraform配置修改
resource "aws_instance" "processing_instance" { # 原有配置不变 } # 实例创建完成后执行停止命令 resource "null_resource" "stop_instance_after_creation" { triggers = { instance_id = aws_instance.processing_instance.id } provisioner "local-exec" { command = "aws ec2 stop-instances --instance-ids ${aws_instance.processing_instance.id}" } }
注意:这种方式依赖运行Terraform的环境已配置AWS CLI且具备EC2停止权限,若环境无网络或权限问题,可能导致停止失败。
内容的提问来源于stack exchange,提问作者elvieLynx

