首页 > 解决方案 > 如何使 CloudFormation 模板中的整个对象成为可选?

问题描述

我正在通过 CloudFormation 模板创建一个 Lambda 函数,并且我想让它成为可选的,以输入 VpcConfig 属性的信息。我发现了这样一篇关于如何使参数可选的文章:

https://cloudonaut.io/optional-parameter-in-cloudformation/

这对于查找语法以使具有单个值的属性成为可选(如单个字符串值)非常有帮助。

但我需要弄清楚的是如何使整个 VpcConfig OBJECT 成为可选的。

这有点棘手,因为 VpcConfig 对象有两个属性:SecurityGroupIds 和 SubnetIds。两者都是必需的。所以用户需要输入这两个或一个都不输入。如果两者都没有输入,那么整个 VpcConfig 对象应该是空的,或者不存在(这应该没问题,因为 VpConfig 对象本身是可选的)。

这是我现在所拥有的截断版本,但这还不够,因为我仍然收到错误说 SecurityGroupIds 和 SubnetIds 不能有空值,因为只要对象存在于 VpcConfig 属性中,它们就是必需的:

"Conditions": {
        "HasSecurityGroups": {"Fn::Not": [{"Fn::Equals": [{"Fn::Join": ["", {"Ref": "SecurityGroupIds"}]}, ""]}]},
        "HasSubnetIds": {"Fn::Not": [{"Fn::Equals": [{"Fn::Join": ["", {"Ref": "SubnetIds"}]}, ""]}]}
      },

然后在 Lambda 资源中:

"VpcConfig" : {
          "SecurityGroupIds" : {"Fn::If": ["HasSecurityGroups", {"Ref": "SecurityGroupIds"}, {"Ref": "AWS::NoValue"}]},
          "SubnetIds" : {"Fn::If": ["HasSubnetIds", {"Ref": "SubnetIds"}, {"Ref": "AWS::NoValue"}]}
        },

这是整个模板,如果有帮助的话:

 {
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "Lambda template for testing purposes",

  "Parameters" : {
    "S3BucketName" : {
      "Type" : "String",
      "Description" : "The name of the Amazon S3 bucket where the .zip file that contains your deployment package is stored."
    },
    "S3FileLocation" : {
      "Type" : "String",
      "Description" : "The location and name of the .zip file that contains your source code."
    },
    "S3ObjectVersion" : {
      "Type" : "String",
      "Description" : "If you have S3 versioning enabled, the version ID of the zip file that contains your source code."
    },
    "DeadLetterArn" : {
      "Type" : "String",
      "Description" : "ARN that specifies a Dead Letter Queue (DLQ) that Lambda sends events to when it can't process them. For example, you can send unprocessed events to an Amazon Simple Notification Service (Amazon SNS) topic, where you can take further action."
    },
    "EnvironmentVariable" : {
      "Type" : "String",
      "Default" : "test",
      "Description" : "Environment Variable"
    },
    "KmsKeyArn" : {
      "Type" : "String",
      "Description" : "KMS Key ARN if environment variables are encrypted"
    },
    "HandlerFunctionName" : {
      "Type" : "String",
      "Description" : "Name of function to initiate the Lambda"
    },
    "MemorySize" : {
      "Type" : "Number",
      "Description" : "Number of MB to allocate to the Lambda function",
      "ConstraintDescription" : "Multiples of 64, between 128 and 3008",
      "MinValue" : "128",
      "Default" : "128"
    },
    "SecurityGroupIds" : {
      "Type" : "CommaDelimitedList",
      "Description" : "A list of one or more security groups IDs in the VPC that includes the resources to which your Lambda function requires access."
    },
    "SubnetIds" : {
      "Type" : "CommaDelimitedList",
      "Description" : "A list of one or more subnet IDs in the VPC that includes the resources to which your Lambda function requires access."
    },
    "Role" : {
      "Type" : "String",
      "Description" : "ARN of Lambda Role"
    },
    "FuncName" : {
      "Type" : "String",
      "Description" : "Name of the the new Lambda function?"
    }
  },

  "Conditions": {
    "HasSecurityGroups": {"Fn::Not": [{"Fn::Equals": [{"Fn::Join": ["", {"Ref": "SecurityGroupIds"}]}, ""]}]},
    "HasSubnetIds": {"Fn::Not": [{"Fn::Equals": [{"Fn::Join": ["", {"Ref": "SubnetIds"}]}, ""]}]}
  },

  "Resources" : {

    "LambdaFunction" : {
      "Type" : "AWS::Lambda::Function",
      "Properties" : {
        "Code" : {
          "S3Bucket" : { "Ref" : "S3BucketName" },
          "S3Key" : { "Ref" : "S3FileLocation" },
          "S3ObjectVersion" : { "Ref" : "S3ObjectVersion" }
        },
        "DeadLetterConfig" : {
          "TargetArn" : { "Ref" : "DeadLetterArn" }
        },
        "Description" : "Lambda",
        "Environment" : {
          "Variables" : {
            "SomeVariable": {
              "Ref" : "EnvironmentVariable"
            }  
          }
        },
        "FunctionName" : { "Ref" : "FuncName" },
        "Handler" : { "Ref" : "HandlerFunctionName" },
        "KmsKeyArn" : { "Ref" : "KmsKeyArn" },
        "MemorySize" : { "Ref" : "MemorySize" },
        "Role" : { "Ref" : "Role" },
        "Runtime" : "python3.6",
        "VpcConfig" : {
          "SecurityGroupIds" : {"Fn::If": ["HasSecurityGroups", {"Ref": "SecurityGroupIds"}, {"Ref": "AWS::NoValue"}]},
          "SubnetIds" : {"Fn::If": ["HasSubnetIds", {"Ref": "SubnetIds"}, {"Ref": "AWS::NoValue"}]}
        },
        "Tags" : [ {
          "Key" : "test",
          "Value" : "true"
        } ]
      }
    }
  },
  "Outputs" : {
    "LambdaFunction" : {
      "Description" : "Lambda function",
      "Value" : { "Ref" : "LambdaFunction" },
      "Export" : {
        "Name" : {"Fn::Sub": "${AWS::StackName}-Lambda" }
      }
    }
  }
}

更新:cementblocks 下面发布的答案适用于 AWS GUI 控制台,但不幸的是,如果您尝试使用 CLI 部署模板,它仍然会遇到同样的问题。我在下面的链接中创建了一个新问题来单独解决这个问题,因为我想他的回答会对大多数参考这篇文章的人有用。

使用 AWS CLI 启动 CloudFormation 模板时的可选参数

标签: jsonaws-lambdaamazon-cloudformation

解决方案


创建第三个条件HasVPC,当 HasSecurityGroups 和 HasSubnetIds 都为真时,它应该为真,使用Fn::And. 您将不得不复制条件语句;你不能在另一个条件下引用。

然后使用设置 vpcConfig 属性Fn::If

"VpcConfig": {
  "Fn::If": [
    "HasVPC",
    {
      "SecurityGroupIds" : {"Ref": "SecurityGroupIds"},
      "SubnetIds" : {"Ref": "SubnetIds"}
    },
    { "Ref":"AWS::NoValue" }
  ]
}

然后,您可以删除HasSecurityGroupsandHasSubnetIds条件,除非您在其他地方使用它们。


推荐阅读