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

时间:2017-07-17 15:09:54

标签: amazon-web-services aws-sdk amazon-cloudformation

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

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

那部分有效。如果没有为角色和表提供名称,则会创建它们。

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

是否有某种方法可以设置模板,以便在资源不存在时创建新资源,但如果资源已经存在则不会将其删除?

我没有使用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网关之前手动创建资源,但似乎我应该尝试做的事情。

1 个答案:

答案 0 :(得分:3)

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

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

原因是:

  1. 创建堆栈时,将ShouldCreateTable设置为true,模板将应用它的条件逻辑,并将表创建为自己的托管资源。
  2. 更新堆栈后,您将ShouldCreateTable设置为false,模板会应用它的条件逻辑,并确定您不再需要托管表,因为您提供了你自己现在。应删除该资源。它无法识别表格是否相同。
  3. 使用模板时,如果您提供自己创建的,则只需说出ShouldCreateTable == false