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

Sou*_*eAK 5 amazon-web-services aws-cloudformation aws-sdk

我有以下CloudFormation模板。(它基于默认模板而创建,该模板是为在AWS Lambda中运行C#Web API而创建的,但这可能无关紧要。)

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

那部分起作用。如果没有为角色和表提供名称,则将创建它们。

当我第二次运行模板以执行更新时,问题就存在了:此时,我的角色和表已经存在,因此我将名称作为参数提供。但是,当CloudFormation第二次运行时,它第一次创建的资源(角色和表)将被删除。

是否可以通过某种方式设置模板,以便在不存在新资源的情况下创建新资源,而在已经存在的情况下不删除它们呢?

我对CloudFormation并没有做很多事情,但确实浏览了文档。我发现最接近的是设置堆栈策略,但它似乎不是模板的一部分。事实看来,我似乎必须在管理控制台中执行此操作。

{
  "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"} ] }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我可以删除创建步骤并在API网关之前手动创建资源,但是似乎我想做的事情应该可行。

Mat*_*ser 3

当您更新现有堆栈时,请勿更改参数。因此,即使您正在更新堆栈,也请设置ShouldCreateTabletrue.

是的,当表已经存在时,更新堆栈时说“创建表”似乎是违反直觉的,但您需要这样做。

原因是这样的:

  1. 创建堆栈时,您设置ShouldCreateTabletrue,模板将应用其条件逻辑并创建表作为其自己的托管资源。
  2. 当堆栈更新时,您设置ShouldCreateTablefalse,模板将应用其条件逻辑并确定您不再需要托管表,因为您现在正在提供自己的表。应删除该资源。它无法识别该表是相同的。

使用模板时,仅当您提供自己创建的ShouldCreateTable表时才说== 。false