如何检查特定资源是否已存在于 CloudFormation 脚本中

How to check if specific resource already exists in CloudFormation script

我正在使用 cloudformation 创建一个堆栈,其中包含一个自动缩放的 ec2 实例和一个 S3 存储桶。对于 S3 存储桶,我将 DeletionPolicy 设置为 Retain,效果很好,直到我想再次重新运行我的 cloudformation 脚本。由于在之前的运行中,脚本创建了 S3 存储桶,因此在后续运行中失败,说我的 S3 存储桶已经存在。 None 当然也会创建其他资源。我的问题是如何检查我的 S3 存储桶是否首先存在于 cloudformation 脚本中,如果存在,则跳过创建该资源。我查看了 AWS 中的条件,但它似乎都是基于参数的,我还没有找到一个从现有资源中检查的函数。

没有明显的方法可以做到这一点,除非您通过显式检查动态创建模板。同一个模板创建的栈是独立的实体,如果你创建一个包含桶的栈,在保留桶的情况下删除栈,然后创建一个新的栈(即使是同名的栈),这之间没有任何联系新堆栈和作为先前堆栈的一部分创建的存储桶。

如果您想对多个堆栈使用相同的 S3 存储桶(即使一次只存在其中一个),该存储桶并不真正属于该堆栈 - 在中创建存储桶更有意义一个单独的堆栈,使用单独的模板(将存储桶 URL 放在 "Outputs" 部分),然后使用参数从原始堆栈中引用它。

2019 年 11 月更新:

现在有一个可能的替代方案。 11 月 13 日,AWS launched CloudFormation 资源导入。使用该功能,您现在可以从现有资源创建堆栈。目前此功能支持的资源类型不多,但支持 S3 存储桶。

在您的情况下,您必须分两步完成:

  1. 使用 "Create stack" > "With existing resources (import resources)" 创建一个 包含预先存在的 S3 存储桶的模板(这是 --change-set-type IMPORT 标志CLI) (see docs)
  2. 更新模板以包含所有尚不存在的资源。

正如他们在文档中指出的那样;此功能非常通用。所以它开辟了很多可能性。有关详细信息,请参阅 docs

只需向 CloudFormation 模板添加一个输入参数以指示应使用现有存储桶....除非您当时还不知道何时要使用该模板?然后你可以根据参数值添加或不添加新资源。

如果您进行更新(可能是堆栈中的堆栈,也称为嵌套堆栈),则不会更新未更改的部分。 https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html?icmpid=docs_cfn_console_designer

然后您可以按照上述设置策略以防止删除。 [记住回滚的 'cancel update' 权限] 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

这意味着可以使用网络堆栈名称参数。

不幸的是,当您在条件中尝试它们时,您会遇到这样的错误。

Template validation error: Template error: Cannot use Fn::ImportValue in Conditions.

或者在参数中?

Template validation error: Template format error: Every Default member must be a string.

尝试时也可能发生这种情况...

Template format error: Output ExportOut is malformed. The Name field of Export must not depend on any resources, imported values, or 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"
      }
    }
  }
}

你会得到

No export named Exported-GroupName found. Rollback requested by user.

if 运行 未找到组的用户已导出。

然后您可以使用嵌套堆栈方法。

{
  "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 创建的,但使用删除策略和其他可能会有所帮助。

然后你只是更新它创建的各种堆栈,如果你正在重用一个桶,你就不会做多堆栈。

否则您将看到各种风格的 API 和脚本。

使用 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"

如果您试图将一些现有资源合并到 CF 中,很遗憾,这是不可能的。如果您只是想让一组资源成为您模板的一部分,或者不根据某些参数的值,您可以使用 Conditions。但他们并没有改变CF本身的性质,只是判断需要哪些资源,而不是要采取什么行动,无法事前判断资源是否存在。

有些事情没有明确说明。如果您的第一次部署失败,除非您有保留政策,否则资源将被删除。在这种情况下,手动删除相关资源是安全的。下一次部署将重新创建它而不会产生资源已存在的错误。