如何检查 CloudFormation 脚本中是不是已存在特定资源
Posted
技术标签:
【中文标题】如何检查 CloudFormation 脚本中是不是已存在特定资源【英文标题】:How to check if specific resource already exists in CloudFormation script如何检查 CloudFormation 脚本中是否已存在特定资源 【发布时间】:2015-03-22 00:51:10 【问题描述】:我正在使用 cloudformation 创建一个堆栈,其中包括一个自动缩放的 ec2 实例和一个 S3 存储桶。对于 S3 存储桶,我将 DeletionPolicy 设置为 Retain,它工作正常,直到我想再次重新运行我的 cloudformation 脚本。由于在之前的运行中,脚本创建了 S3 存储桶,因此在后续运行中失败,说我的 S3 存储桶已经存在。当然也没有创建其他资源。我的问题是如何检查我的 S3 存储桶是否首先存在于 cloudformation 脚本中,如果存在,则跳过创建该资源。我查看了 AWS 中的条件,但似乎都是基于参数的,我还没有找到一个从现有资源中检查的函数。
【问题讨论】:
是的,这在 Terraform 中不是这样的问题。 【参考方案1】:没有明显的方法可以做到这一点,除非您使用显式检查动态创建模板。同一个模板创建的栈是独立的实体,如果创建一个包含bucket的stack,在保留bucket的同时删除stack,然后再创建一个新的stack(即使是同名的),这两者之间没有联系新堆栈和作为先前堆栈的一部分创建的存储桶。
如果您想为多个堆栈使用同一个 S3 存储桶(即使一次只存在一个),该存储桶并不真正属于堆栈 - 在单独的存储桶中创建存储桶会更有意义堆栈,使用单独的模板(将存储桶 URL 放在“输出”部分),然后使用参数从原始堆栈中引用它。
2019 年 11 月更新:
现在有一个可能的替代方案。 11 月 13 日 AWS launched CloudFormation 资源导入。使用该功能,您现在可以从现有资源创建堆栈。目前,此功能支持的资源类型并不多,但 S3 存储桶支持。
在您的情况下,您必须分两步完成:
-
使用“创建堆栈”>“使用现有资源(导入资源)”创建仅包含预先存在的 S3 存储桶的模板(这是 CLI 中的
--change-set-type IMPORT
标志)(@ 987654322@)
更新模板以包含所有不存在的资源。
正如他们在文档中指出的那样;这个功能非常通用。所以它开辟了很多可能性。请参阅docs 了解更多信息。
【讨论】:
目标是为 Docker 注册表创建一个堆栈 - 存储桶用作后端来存储图像,ec2 主机用作 docker 注册表本身,以及所有必要的 IAM 策略和安全组。在我看来,它是一个实体,因此我希望创建 1 个足够灵活的脚本。我想我会在运行它时使用 Amazon CLI 脚本创建一个条件并输入一个参数,这是我了解如何使 cloudfomration 脚本完全自动化和可重新运行的唯一方法。【参考方案2】:使用 cloudformation,您可以使用 Conditions 我创建了一个输入参数“ShouldCreateBucketInputParameter”,然后使用 CLI 你只需要设置“true”或“false”
Cloudformation json 文件:
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Description": "",
"Parameters":
"ShouldCreateBucketInputParameter":
"Type": "String",
"AllowedValues": [
"true",
"false"
],
"Description": "If true then the S3 bucket that will be proxied will be created with the CloudFormation stack."
,
"Conditions":
"CreateS3Bucket":
"Fn::Equals": [
"Ref": "ShouldCreateBucketInputParameter"
,
"true"
]
,
"Resources":
"SerialNumberBucketResource":
"Type": "AWS::S3::Bucket",
"Condition": "CreateS3Bucket",
"Properties":
"AccessControl": "Private"
,
"Outputs":
然后(我正在使用 CLI 部署堆栈)
aws cloudformation deploy --template ./s3BucketWithCondition.json --stack-name bucket-stack --parameter-overrides ShouldCreateBucketInputParameter="true" S3BucketNameInputParameter="BucketName22211"
【讨论】:
干得好,非常感谢您的贡献 Jorge。 这应该是公认的答案【参考方案3】:只需向 CloudFormation 模板添加一个输入参数以指示应使用现有存储桶....除非您当时还不知道何时使用该模板?然后您可以根据参数值添加或不添加新资源。
【讨论】:
这个参数是什么?抱歉很新:) 是的,我要和@VincentBuscarello 一起去,你为什么不至少扔掉一些代码。不知道我的项目经理对 SO 提出了建议【参考方案4】:如果您进行更新(可能是堆栈内的堆栈,也就是嵌套堆栈),未更改的部分不会得到更新。 https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html?icmpid=docs_cfn_console_designer
然后,您可以设置上述政策以防止删除。 [记住回滚的“取消更新”权限] https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html
还可以通过将导出名称添加到堆栈输出来了解跨堆栈输出。 http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html 演练... http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/walkthrough-crossstackref.html
那么你需要使用 Fn::ImportValue ... http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html
这意味着可以使用网络堆栈名称参数。
不幸的是,当您在条件中尝试它们时,您会遇到这样的错误。
模板验证错误:模板错误:无法使用 Fn::ImportValue 在条件中。
还是在参数中?
模板验证错误:模板格式错误:每个默认成员 必须是字符串。
这也可能在尝试时发生...
模板格式错误:输出 ExportOut 格式错误。名称字段 出口不得依赖于任何资源、进口值或 Fn::GetAZs。
因此,您无法阻止它再次从同一个文件中创建现有资源。仅当将其放入另一个堆栈并使用导出导入引用时。
但是如果你将两者分开,那么会有一个依赖项将停止并回滚,例如依赖项的删除,这要归功于 ImportValue 函数的引用。
这里给出的例子是:
首先制作组模板
"AWSTemplateFormatVersion": "2010-09-09",
"Metadata":
"AWS::CloudFormation::Designer":
"6927bf3d-85ec-449d-8ee1-f3e1804d78f7":
"size":
"width": 60,
"height": 60
,
"position":
"x": -390,
"y": 130
,
"z": 0,
"embeds": []
,
"6fe3a2b8-16a1-4ce0-b412-4d4f87e9c54c":
"source":
"id": "ac295134-9e38-4425-8d20-2c50ef0d51b3"
,
"target":
"id": "6927bf3d-85ec-449d-8ee1-f3e1804d78f7"
,
"z": 1
,
"Resources":
"TestGroup":
"Type": "AWS::IAM::Group",
"Properties": ,
"Metadata":
"AWS::CloudFormation::Designer":
"id": "6927bf3d-85ec-449d-8ee1-f3e1804d78f7"
,
"Condition": ""
,
"Parameters": ,
"Outputs":
"GroupNameOut":
"Description": "The Group Name",
"Value":
"Ref": "TestGroup"
,
"Export":
"Name": "Exported-GroupName"
然后制作一个需要该组的用户模板。
"AWSTemplateFormatVersion": "2010-09-09",
"Metadata":
"AWS::CloudFormation::Designer":
"ac295134-9e38-4425-8d20-2c50ef0d51b3":
"size":
"width": 60,
"height": 60
,
"position":
"x": -450,
"y": 130
,
"z": 0,
"embeds": [],
"isrelatedto": [
"6927bf3d-85ec-449d-8ee1-f3e1804d78f7"
]
,
"6fe3a2b8-16a1-4ce0-b412-4d4f87e9c54c":
"source":
"id": "ac295134-9e38-4425-8d20-2c50ef0d51b3"
,
"target":
"id": "6927bf3d-85ec-449d-8ee1-f3e1804d78f7"
,
"z": 1
,
"Resources":
"TestUser":
"Type": "AWS::IAM::User",
"Properties":
"UserName":
"Ref": "UserNameParam"
,
"Groups": [
"Fn::ImportValue": "Exported-GroupName"
]
,
"Metadata":
"AWS::CloudFormation::Designer":
"id": "ac295134-9e38-4425-8d20-2c50ef0d51b3"
,
"Parameters":
"UserNameParam":
"Default": "testerUser",
"Description": "Username For Test",
"Type": "String",
"MinLength": "1",
"MaxLength": "16",
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
"ConstraintDescription": "must begin with a letter and contain only alphanumeric characters."
,
"Outputs":
"UserNameOut":
"Description": "The User Name",
"Value":
"Ref": "TestUser"
你会得到
找不到名为 Exported-GroupName 的导出。用户请求回滚。
如果运行用户未找到已导出的组。
然后您可以使用嵌套堆栈方法。
"AWSTemplateFormatVersion": "2010-09-09",
"Metadata":
"AWS::CloudFormation::Designer":
"66470873-b2bd-4a5a-af19-5d54b11f48ef":
"size":
"width": 60,
"height": 60
,
"position":
"x": -815,
"y": 169
,
"z": 0,
"embeds": []
,
"ed1de011-f1bb-4788-b63e-dcf5494d10d1":
"size":
"width": 60,
"height": 60
,
"position":
"x": -710,
"y": 170
,
"z": 0,
"dependson": [
"66470873-b2bd-4a5a-af19-5d54b11f48ef"
]
,
"c978f2d9-3fb2-4420-b255-74941f10a28a":
"source":
"id": "ed1de011-f1bb-4788-b63e-dcf5494d10d1"
,
"target":
"id": "66470873-b2bd-4a5a-af19-5d54b11f48ef"
,
"z": 1
,
"Resources":
"GroupStack":
"Type": "AWS::CloudFormation::Stack",
"Properties":
"TemplateURL": "https://s3-us-west-2.amazonaws.com/cf-templates-x-TestGroup.json"
,
"Metadata":
"AWS::CloudFormation::Designer":
"id": "66470873-b2bd-4a5a-af19-5d54b11f48ef"
,
"UserStack":
"Type": "AWS::CloudFormation::Stack",
"Properties":
"TemplateURL": "https://s3-us-west-2.amazonaws.com/cf-templates-x-TestUserFindsGroup.json"
,
"Metadata":
"AWS::CloudFormation::Designer":
"id": "ed1de011-f1bb-4788-b63e-dcf5494d10d1"
,
"DependsOn": [
"GroupStack"
]
不幸的是,即使在此示例中它是由 MultiStack 创建的,您仍然可以删除用户堆栈,但使用删除策略和其他东西可能会有所帮助。
那么您只是更新它创建的各种堆栈,如果您要重用 Bucket,则不会执行 Multi Stack。
否则,您将看到各种风格的 API 和脚本。
【讨论】:
看起来 Cloudformation 的目标是一个 API 集,以使 AWS 资源初始化更容易,而不是一个状态管理工具。 也许可以试试 Hashicorp 的 Terraform。至少这是我今天可能会尝试的。【参考方案5】:如果您尝试将一些现有资源合并到 CF 中,很遗憾这是不可能的。 如果您只是希望一组资源成为模板的一部分,则取决于一些参数,你可以使用Conditions。但它们并没有改变 CF 本身的性质,只是确定需要哪些资源,而不是要采取什么行动,并且无法事先查看资源是否存在。
【讨论】:
【参考方案6】:没有明确说明的事情。如果您的第一次部署失败,除非您有保留策略,否则资源将被删除。在这种情况下,手动删除相关资源是安全的。下一次部署将重新创建它,而不会生成资源已存在的错误。
【讨论】:
以上是关于如何检查 CloudFormation 脚本中是不是已存在特定资源的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Cloudformation 脚本执行期间将 Cognito UserPoolID、客户端密码传递给 AWS Lambda?
如何检查 Linux shell 脚本是不是由 cronjob 执行?