AWS CloudFormation – 模板中的自定义variables

有没有什么办法来定义从CloudFormation模板参数派生的经常使用的值的快捷方式?

例如 – 我已经创build了一个脚本,它创build了ELB名称project的多可用区项目堆栈,ELB后面有两个名为project-1project-2实例。 我只将ELBHostNameparameter passing给模板,稍后使用它来构造:

 "Fn::Join": [ ".", [ { "Fn::Join": [ "", [ { "Ref": "ELBHostName" }, "-1" ] ] }, { "Ref": "EnvironmentVersioned" }, { "Ref": "HostedZone" } ] ] 

这种结构或非常相似的模板在整个模板中重复多次 – 创buildEC2主机名,Route53logging等。

而不是一遍又一遍地重复,我想分配的Fn::Join的输出到某种types的variables,只能参考,就像我可以用"Ref":语句。

理想情况下是这样的:

 Var::HostNameFull = "Fn::Join": [ ... ] ... { "Name": { "Ref": "Var::HostNameFull" } } 

或类似的简单的东西。

Amazon CloudFormation可能吗?

我正在寻找相同的function。 SpoonMeiserbuild议使用嵌套堆栈,但后来我意识到,我真正需要的是自定义函数。 幸运的是,CloudFormation允许使用AWS :: CloudFormation :: CustomResource ,通过一些工作,可以做到这一点。 这对于只是variables(我认为本来应该放在CloudFormation中的东西)来说太过于夸张了,但是它完成了工作,而且还考虑到了所有的灵活性(可以selectpython / node / JAVA)。 应该注意的是,lambda函数花钱,但是我们在这里讲便士,除非你每小时多次创build/删除你的栈。

第一步是在这个页面上创build一个lambda函数,它除了获取input值并将其复制到输出之外什么也不做。 我们可以让lambda函数做各种疯狂的东西,但是一旦我们有了身份识别function,其他任何事情都很容易。 或者我们可以在栈中创buildlambda函数。 由于我在1个帐户中使用了很多堆栈,因此我将拥有大量剩余的lambda函数和angular色(并且所有堆栈都需要使用--capabilities=CAPABILITY_IAM创build,因为它也需要一个angular色。

创buildlambda函数

  • 转到lambda主页 ,并select您最喜欢的地区
  • select“空白function”作为模板
  • 点击“下一步”(不要configuration任何触发器)
  • 填写:
    • 名称:CloudFormationIdentity
    • 描述:返回它在Cloud Formation中获得的可变支持
    • 运行时:python2.7
    • 代码inputtypes:内联编辑代码
    • 代码:见下文
    • 处理程序: index.handler
    • angular色:创build自定义angular色。 此时会打开一个popup窗口,允许您创build一个新的angular色。 接受此页面上的所有内容,然后单击“允许”。 它将创build一个有权发布到cloudwatch日志的angular色。
    • 内存:128(这是最低的)
    • 超时:3秒(应该是很多)
    • VPC:没有VPC

然后将以下代码复制粘贴到代码字段中。 函数的顶部是来自cfn-response python模块的代码,出于某种奇怪的原因,如果lambda函数是通过CloudFormation创build的,那么它只会自动安装。 handler函数是不言自明的。

 from __future__ import print_function import json try: from urllib2 import HTTPError, build_opener, HTTPHandler, Request except ImportError: from urllib.error import HTTPError from urllib.request import build_opener, HTTPHandler, Request SUCCESS = "SUCCESS" FAILED = "FAILED" def send(event, context, response_status, reason=None, response_data=None, physical_resource_id=None): response_data = response_data or {} response_body = json.dumps( { 'Status': response_status, 'Reason': reason or "See the details in CloudWatch Log Stream: " + context.log_stream_name, 'PhysicalResourceId': physical_resource_id or context.log_stream_name, 'StackId': event['StackId'], 'RequestId': event['RequestId'], 'LogicalResourceId': event['LogicalResourceId'], 'Data': response_data } ) if event["ResponseURL"] == "http://pre-signed-S3-url-for-response": print("Would send back the following values to Cloud Formation:") print(response_data) return opener = build_opener(HTTPHandler) request = Request(event['ResponseURL'], data=response_body) request.add_header('Content-Type', '') request.add_header('Content-Length', len(response_body)) request.get_method = lambda: 'PUT' try: response = opener.open(request) print("Status code: {}".format(response.getcode())) print("Status message: {}".format(response.msg)) return True except HTTPError as exc: print("Failed executing HTTP request: {}".format(exc.code)) return False def handler(event, context): responseData = event['ResourceProperties'] send(event, context, SUCCESS, None, responseData, "CustomResourcePhysicalID") 
  • 点击下一步”
  • 点击“创buildfunction”

您现在可以通过select“testing”button来testinglambda函数,并select“CloudFormation Create Request”作为示例模板。 你应该在你的日志中看到馈给它的variables被返回。

在你的CloudFormation模板中使用variables

现在我们有了这个lambda函数,我们可以在CloudFormation模板中使用它。 首先记下lambda函数Arn(转到lambda主页 ,单击刚创build的函数,Arn应该在右上angular,类似于arn:aws:lambda:region:12345:function:CloudFormationIdentity )。

现在在你的模板中,在资源部分,指定你的variables,如:

 Identity: Type: "Custom::Variable" Properties: ServiceToken: "arn:aws:lambda:region:12345:function:CloudFormationIdentity" Arn: "arn:aws:lambda:region:12345:function:CloudFormationIdentity" ClientBucketVar: Type: "Custom::Variable" Properties: ServiceToken: !GetAtt [Identity, Arn] Name: !Join ["-", [my-client-bucket, !Ref ClientName]] Arn: !Join [":", [arn, aws, s3, "", "", !Join ["-", [my-client-bucket, !Ref ClientName]]]] ClientBackupBucketVar: Type: "Custom::Variable" Properties: ServiceToken: !GetAtt [Identity, Arn] Name: !Join ["-", [my-client-bucket, !Ref ClientName, backup]] Arn: !Join [":", [arn, aws, s3, "", "", !Join ["-", [my-client-bucket, !Ref ClientName, backup]]]] 

首先,我指定一个包含lambda函数的Arn的Identityvariables。 把这个放在一个variables里,意味着我只需要指定一次。 我使我的所有variables的typesCustom::Variable 。 CloudFormation允许您使用以Custom::开头的任何types名称作为自定义资源。

请注意Identityvariables包含两次lambda函数的Arn。 一次指定要使用的lambda函数。 第二次作为variables的值。

现在我有了Identityvariables,我可以使用ServiceToken: !GetAtt [Identity, Arn]定义新的variablesServiceToken: !GetAtt [Identity, Arn] (我认为JSON代码应该像"ServiceToken": {"Fn::GetAtt": ["Identity", "Arn"]} )。 我创build了2个新的variables,每个variables都有2个字段:Name和Arn。 在我的模板的其余部分,我可以使用!GetAtt [ClientBucketVar, Name]!GetAtt [ClientBucketVar, Arn]只要我需要它。

谨慎的话

在使用自定义资源时,如果lambda函数崩溃,则会停滞1到2个小时,因为在放弃之前,CloudFormation等待从(崩溃)函数回复一个小时。 因此,在开发lambda函数的时候为堆栈指定一个短暂的超时值可能是很好的做法。

不,我试过了,但空了。 对我来说有道理的方法是创build一个名为“CustomVariables”的映射条目,并拥有我所有的variables。 它适用于简单的string,但不能在映射中使用Intrinsics(Refs,Fn :: Joins等) 。

作品:

 "Mappings" : { "CustomVariables" : { "Variable1" : { "Value" : "foo" }, "Variable2" : { "Value" : "bar" } } } 

不会工作:

  "Variable3" : { "Value" : { "Ref" : "AWS::Region" } } 

这只是一个例子。 你不会把一个独立的Ref放入一个variables中。

您可以使用嵌套的模板,在其中“parsing”外部模板中的所有variables并将它们传递给另一个模板。

你可以使用一个嵌套的栈来parsing输出中的所有variables,然后使用Fn::GetAtt从栈中读取输出

我没有答案,但是想要指出的是,通过使用Fn::Sub代替Fn::Join ,可以节省很多痛苦

 { "Fn::Sub": "${ELBHostName"}-1.${EnvironmentVersioned}.${HostedZone}"} 

替代对象

 "Fn::Join": [ ".", [ { "Fn::Join": [ "", [ { "Ref": "ELBHostName" }, "-1" ] ] }, { "Ref": "EnvironmentVersioned" }, { "Ref": "HostedZone" } ] ]