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

如何让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:20:04