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

如何在CloudFormation栈中引用EC2 User Data创建的AMI ID?

问题

我正在编写CloudFormation模板,用来部署EC2实例。该实例会通过User Data从Git仓库拉取脚本,并用脚本创建AMI。我想在同一个CloudFormation栈的其他资源里,通过!Ref或其他方式引用这个新生成的AMI ID。

目前我在User Data里加了以下代码获取AMI ID:

export AMIID=$(aws ec2 describe-images --filters "Name=name,Values=ami-name" | jq -r ".Images[].ImageId")

还添加了把AMI ID存入SSM参数存储的代码:

aws ssm put-parameter --name aminame --type String --value "$AMIID"

在CloudFormation栈里我定义了如下参数:

AMI:
    Type : 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: aminame

然后在资源块里引用这个AMI:

EC2Instance:
      Type: "AWS::EC2::Instance"
      CreationPolicy:
        ResourceSignal:
          Timeout: PT120M
      Properties:
          ImageId: !Ref AMI
      UserData:
            Fn::Base64: |
              #!/bin/bash
              # 这里是拉取脚本、创建AMI并存入SSM的代码

运行时收到“参数未找到”的错误,这在意料之中,有没有其他可行的实现方式?

可行的实现方式

方法一:用CloudFormation自定义资源传递AMI ID

自定义资源能让EC2实例在生成AMI后主动通知CloudFormation栈,把AMI ID作为资源属性存储,这样栈内其他资源就能直接引用。

步骤&代码示例

  1. 给EC2实例添加必要的IAM权限
    先定义一个IAM角色,允许实例创建AMI、调用CloudFormation的自定义资源回调API:

    EC2Role:
      Type: AWS::IAM::Role
      Properties:
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service: ec2.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: AMICreationAndCFNCallback
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action:
                    - ec2:CreateImage
                    - ec2:DescribeImages
                    - ssm:PutParameter
                  Resource: "*"
                - Effect: Allow
                  Action:
                    - cloudformation:SignalResource
                  Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*"
    
    EC2InstanceProfile:
      Type: AWS::IAM::InstanceProfile
      Properties:
        Roles: [!Ref EC2Role]
    
  2. 定义自定义资源
    在模板里加一个自定义资源,用来接收AMI ID:

    CustomAMIResource:
      Type: Custom::AMIResource
      Properties:
        ServiceToken: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:AWSLambdaBasicExecutionRole"
        # 留空等待EC2实例回调填充数据
    

    注:实际使用时,ServiceToken可以用AWS提供的Lambda函数,或自行编写简单Lambda处理回调,核心是接收AMI ID并返回给CloudFormation。

  3. 修改EC2的User Data,完成AMI创建后回调自定义资源
    在创建AMI并存入SSM的代码后,添加回调逻辑:

    # 创建AMI的代码...
    # 获取AMI ID后,发送信号给自定义资源
    aws cloudformation signal-resource \
      --stack-name ${AWS::StackName} \
      --logical-resource-id CustomAMIResource \
      --region ${AWS::Region} \
      --status SUCCESS \
      --data "$AMIID"
    
  4. 在其他资源中引用
    之后其他资源就可以通过!GetAtt CustomAMIResource.Data获取AMI ID:

    NewEC2Instance:
      Type: AWS::EC2::Instance
      Properties:
        ImageId: !GetAtt CustomAMIResource.Data
        # 其他属性...
    

方法二:用等待条件+SSM动态引用

先让EC2实例生成AMI并存入SSM,然后用WaitCondition等待参数创建完成,再通过动态引用获取AMI ID。

步骤&代码示例

  1. 添加WaitCondition和WaitConditionHandle

    AMIWaitConditionHandle:
      Type: AWS::CloudFormation::WaitConditionHandle
    
    AMIWaitCondition:
      Type: AWS::CloudFormation::WaitCondition
      DependsOn: EC2Instance
      Properties:
        Handle: !Ref AMIWaitConditionHandle
        Timeout: PT120M
    
  2. 修改EC2的User Data,存入SSM后发送等待信号

    # 创建AMI、获取AMI ID并存入SSM的代码...
    # 发送信号通知等待条件完成
    aws cloudformation signal-resource \
      --stack-name ${AWS::StackName} \
      --logical-resource-id AMIWaitCondition \
      --region ${AWS::Region} \
      --status SUCCESS
    
  3. 引用AMI ID
    在需要使用AMI的资源里,用动态引用{{resolve:ssm:aminame}},并确保依赖WaitCondition:

    NewEC2Instance:
      Type: AWS::EC2::Instance
      DependsOn: AMIWaitCondition
      Properties:
        ImageId: "{{resolve:ssm:aminame}}"
        # 其他属性...
    

方法三:拆分CloudFormation栈

把流程拆成两个独立的栈,第一个栈负责生成AMI,第二个栈使用生成的AMI。

步骤

  1. 第一个栈(AMI生成栈)
    只部署用来生成AMI的EC2实例,在User Data里完成AMI创建并存入SSM参数,不需要其他资源。

  2. 第二个栈(业务资源栈)
    通过SSM参数引用AMI ID,部署需要使用该AMI的资源,比如新的EC2实例。可以设置第二个栈依赖第一个栈,或等第一个栈执行完成后手动创建第二个栈。

代码示例(第二个栈的参数)

Parameters:
  AMIId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: aminame

Resources:
  BusinessEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AMIId
      # 其他属性...

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 12:36:19