CloudFormation:如果资源不存在则创建资源,但不删除它们

CloudFormation: Create resources if they do not exist, but do not delete them

我有以下 CloudFormation 模板。 (它基于默认为 AWS Lambda 中的 运行ning C# Web API 创建的模板,但这可能不相关。)

它创建一个 AWS Lambda 函数。如果现有资源的名称未作为参数提供,该模板还会创建一个 IAM 角色和一个 DynamoDB table。

这部分有效。如果没有为角色提供名称并且 table,则创建它们。

我运行第二次对模板执行更新时存在问题:此时,我的角色和table存在,所以我提供名称作为参数。然而,当 CloudFormation 运行 第二次时,它第一次创建的资源(角色和 table)被删除。

有没有什么方法可以设置模板,使其在资源不存在时创建新资源,但在资源已经存在时不删除它们?

我对 CloudFormation 的了解不多,但我确实浏览了文档。我找到的最接近的是 setting a stack policy,但它似乎不是模板的一部分。看来事后我必须在管理控制台中执行此操作。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Transform" : "AWS::Serverless-2016-10-31",
  "Description" : "...",

  "Parameters" : {
    "ShouldCreateTable" : {
      "Type" : "String",        
      "AllowedValues" : ["true", "false"],
      "Description" : "If true then the underlying DynamoDB table will be created with the CloudFormation stack."
    },  
    "TableName" : {
        "Type" : "String",
        "Description" : "Name of DynamoDB table to be used for underlying data store. If left blank a new table will be created.",
        "MinLength" : "0"
    },
    "ShouldCreateRole" : {
      "Type" : "String",        
      "AllowedValues" : ["true", "false"],
      "Description" : "If true then the role for the Lambda function will be created with the CloudFormation stack."
    },  
    "RoleARN" : {
        "Type" : "String",
        "Description" : "ARN of the IAM Role used to run the Lambda function. If left blank a new role will be created.",
        "MinLength" : "0"
    }
  },

  "Conditions" : {
    "CreateDynamoTable" : {"Fn::Equals" : [{"Ref" : "ShouldCreateTable"}, "true"]},
    "TableNameGenerated" : {"Fn::Equals" : [{"Ref" : "TableName"}, ""]},
    "CreateRole":{"Fn::Equals" : [{"Ref" : "ShouldCreateRole"}, "true"]},
    "RoleGenerated" : {"Fn::Equals" : [{"Ref" : "RoleARN"}, ""]}
  },

  "Resources" : {

    "Get" : {
      "Type" : "AWS::Serverless::Function",
      "Properties": {
        ...
        "Role": {"Fn::If" : ["CreateRole", {"Fn::GetAtt":["LambdaRole", "Arn"]}, {"Ref":"RoleARN"}]},
        "Environment" : {
          "Variables" : {
            "AppDynamoTable" : { "Fn::If" : ["CreateDynamoTable", {"Ref":"DynamoTable"}, { "Ref" : "TableName" } ] }
          }
        },
        ...
      }
    },

    "LambdaRole":{
        "Type":"AWS::IAM::Role",
        "Condition":"CreateRole",
        "Properties":{
            "ManagedPolicyArns":["arn:aws:iam::aws:policy/AWSLambdaFullAccess"],
            "AssumeRolePolicyDocument": {
               "Version" : "2012-10-17",
               "Statement": [ {
                  "Effect": "Allow",
                  "Principal": {
                     "Service": [ "lambda.amazonaws.com" ]
                  },
                  "Action": [ "sts:AssumeRole" ]
               } ]
            },
            "Policies": [  {
                "PolicyName": "root",
                "PolicyDocument": {
                        "Version": "2012-10-17",
                        "Statement": [
                            {
                                "Effect": "Allow",
                                "Action": [
                                    "dynamodb:Query",
                                    "dynamodb:Scan",
                                    "dynamodb:PutItem",
                                    "dynamodb:GetItem",
                                    "dynamodb:UpdateItem",
                                    "dynamodb:DeleteItem",
                                    "logs:CreateLogGroup",
                                    "logs:CreateLogStream",
                                    "logs:PutLogEvents"
                                ],
                                "Resource": [
                                    "*"
                                ]
                            }
                        ]
                    }
                }
            ]
        }
    },

    "DynamoTable" : {
        "Type" : "AWS::DynamoDB::Table",
        "Condition" : "CreateDynamoTable",
        "Properties" : {
            "TableName" : { "Fn::If" : ["TableNameGenerated", {"Ref" : "AWS::NoValue" }, { "Ref" : "TableName" } ] },
            "AttributeDefinitions": [
                { "AttributeName" : "id", "AttributeType" : "S" }
            ],
            "KeySchema" : [
                { "AttributeName" : "id", "KeyType" : "HASH"}
            ],          
            "ProvisionedThroughput" : { "ReadCapacityUnits" : "5", "WriteCapacityUnits" : "5" }
        }
    }
  },

  "Outputs" : {
    "UnderlyingDynamoTable" : {
        "Value" : { "Fn::If" : ["CreateDynamoTable", {"Ref":"DynamoTable"}, { "Ref" : "TableName" } ] }
    },
    "LambdaRole" : {
        "Value" : {"Fn::If" : ["CreateRole", {"Fn::GetAtt":["LambdaRole", "Arn"]}, {"Ref":"RoleARN"} ] }
    }
  }
}

我可以删除创建步骤并在 API 网关之前手动创建资源,但看起来我正在尝试做的事情应该是可能的。

更新现有堆栈时,请勿更改参数。因此,即使在更新堆栈时,也请将 ShouldCreateTable 设置为 true

是的,当 table 已经存在时,将堆栈更新为 "Create a table" 似乎违反直觉,但您需要这样做。

原因是这样的:

  1. 创建堆栈时,将 ShouldCreateTable 设置为 true,模板应用它的条件逻辑并创建 table 作为它自己的托管资源。
  2. 更新堆栈时,您将 ShouldCreateTable 设置为 false,模板应用它的条件逻辑并确定您不再需要托管 table,因为您提供你自己的现在。应删除该资源。它不承认 table 是相同的。

使用您的模板时,如果您要提供自己创建的table ,请只​​说 ShouldCreateTable == false