如何检查 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?

如何检查命令是不是存在于 shell 脚本中? [复制]

如何检查 Linux shell 脚本是不是由 cronjob 执行?

如何检查传递给 shell 脚本的参数是不是不是整数?

具有资源属性 CloudFormation 的 UserData 脚本

如何检查 csh 脚本中是不是存在任何文件?