如何在更新参数时强制 CloudFormation 堆栈更新?

Posted

技术标签:

【中文标题】如何在更新参数时强制 CloudFormation 堆栈更新?【英文标题】:How do I force a CloudFormation stack to update when the parameter is updated? 【发布时间】:2018-03-31 03:41:20 【问题描述】:

我正在运行一个 AWS CloudFormation 堆栈,该堆栈接受一些参数并启动 EC2 实例以及其他 AWS 资源。这些参数被输入到 EC2 实例的用户数据中,并在此基础上动态地对驻留在 EC2 实例上的 Web 应用程序进行更改。

UserData: 
      Fn::Base64: 
        Fn::Join: 
          - ""
          - 
            - "#!/bin/bash \n"
            - "sh website-conf/website_mysql_config.sh "
            - " -c \""
            - 
              Ref: "CompanyName"

如上例所示,CompanyName 是传递给 userdata 脚本的众多参数之一。问题是,当更新任何一个或多个参数时,CloudFormation 不会检测到这一点,而是会引发此错误。

因此,为了更新堆栈,我必须编辑堆栈并对 ASG 进行更改,以便 CloudFormation '看到'更改并执行堆栈更新。

有没有办法在参数更新时强制CFN更新栈?

【问题讨论】:

【参考方案1】:

CloudFormation 不会更新堆栈,除非堆栈中已创建资源的属性发生更改

例如: 考虑我有一个简单的模板来创建一个需要传递 2 个参数的数据库:

    数据库名称 地区

假设我使用db-name 将其作为值传递给DBInstanceIdentifier

还假设我没有出于任何目的以任何方式使用输入参数region 创建堆栈的资源(或其属性)。它更多的是我为可读性目的而保留的虚拟参数。

我将(TEST-DB1, us-east-1) 作为输入参数传递给 CloudFormation 模板并成功创建了资源。

Scenario-1: 现在,如果我更新堆栈(仍然使用现有模板)并将输入参数更改为(TEST-DB2, us-east-1)。即:仅更改数据库名称而不更改区域。然后 CloudFormation 将检测到,此参数更新会导致堆栈的运行资源的属性发生变化,并将这些修改计算并显示为更改集。

Scenario-2: 假设我进行了另一个更新(仍然使用现有模板)属性并将输入参数更改为(TEST-DB1, us-east-2)。即:仅更改区域而不更改数据库名称。然后 CloudFormation 将检测到,此参数更新导致堆栈的运行资源的属性没有变化将显示Error creating change set

底线: 您对输入参数的更改必须更新/替换堆栈的任何资源(或其属性,如安全组、端口等)。然后 AWS CloudFormation 会将它们显示为 Change Sets 以供您查看。此外,AWS CloudFormation 使用的方法(更新或替换)取决于您为给定资源类型更新的属性。

您的参数“CompanyName”没有对正在运行的 堆栈的资源。因此它报告为Error creating change set。您需要使用它来创建堆栈的任何资源/资源属性。然后 CloudFormation 将在您修改它时检测更改集。这同样适用于您使用的任何其他输入参数。

【讨论】:

【参考方案2】:

使用 AWS CLI 更新堆栈命令。如果您使用 AWS CLI,您可以将参数注入您的堆栈,因此对任何参数的任何更改都会生成一个新堆栈。我自己这样做是为了将 Git/版本提交 ID 注入到 UserData 中,因此只需将堆栈的 JSON/Yaml 更改提交到 Git 即可允许堆栈更新。对参数文件的任何更改都将允许堆栈更新,即使只是一个注释。我在 UserData 中引用我的 Git 提交 ID 的方式与引用 Ref:CompanyName 的方式相同,因此当我更改 Git 提交 ID 时,userData 部分会在堆栈更新时更新。

更新堆栈命令

aws cloudformation update-stack --stack-name MyStack --template-body file:///Users/Documents/Git/project/cloudformation/stack.json --parameters file:///Users/Documents/Git/project/cloudformation/parameters/stack-parameters.dev.json --capabilities CAPABILITY_IAM

流程

通过这种方法,您可以对参数 json 或 yaml 文件进行参数更改,然后将其检入版本控制。现在,如果您使用构建服务器,您可以通过签出 master 并运行上面的那一行来更新您的堆栈。使用 AWS CodeBuild 让这一切变得简单,因此您不需要 jenkins。

【讨论】:

当您说“对任何参数的任何更改都会导致新堆栈”时,您的意思是堆栈已更新还是完全形成了新堆栈? 对我的 parameters.json 文件中的任何参数的任何更改都将允许堆栈更新。堆栈保持不变,但 update-stack 命令将被接受,并且您当前遇到的错误不会发生。 查看我的最后评论。我做了一些编辑来澄清。【参考方案3】:

您的问题的答案已经在此状态下得到解答,除非堆栈中已创建的资源的属性发生变化,否则 CloudFormation 不会更新堆栈。

关于您的问题的答案,请查看下面的解释。

有一种方法可以强制 Cloudformation 使用 AWS::CloudFormation::Init 更新堆栈。 通过使用 cfn-init,每个实例可以在检测到 AWS::CloudFormation::Init 对元数据所做的更改时自行更新。

有一个概念我们要先搞清楚,那就是UserData和元数据的区别,至少在AWS::CloudFormation::Init的情况下是这样。

Userdata:只会在实例第一次启动时调用一次(这包括需要替换实例的更新)。所以,如果你更新堆栈(不是创建一个新堆栈),即使你改变了参数值,如果你调用UserData下的参数,它也不会改变任何东西。 Metadata:随时更新。要使其正常工作,您必须确保检测元数据更改的守护进程正在运行(该守护进程称为 cfn-hup)

如果您已经使用MetadataAWS::CloudFormation::Init,则不会立即更新数据。据我所知,这是更改Metadata 值后要更改的数据的条件。

重启实例 使用参数再次运行cfn-init 命令 等待大约 15 分钟,因为检查 Metadata 更改的守护进程每 15 分钟检查一次更改。

【讨论】:

以上是关于如何在更新参数时强制 CloudFormation 堆栈更新?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 CloudFormation 上将 DynamoDB 读/写容量模式设置为按需

CloudFormation yaml - 如何强制数字类型?

CloudFormation 不会在更新时部署到 API 网关阶段

将对象上传到 S3 存储桶时如何触发 AWS Cloudformation 堆栈的更新?

如何一次从CloudFormation中删除多个全局二级索引?

如何在存储桶名称中使用变量 AWS Cloudformation