【问题标题】:CloudFormation - Access Parameter from Lambda CodeCloudFormation - 来自 Lambda 代码的访问参数
【发布时间】:2016-12-07 01:30:29
【问题描述】:

我有一个看起来像这样的CloudFormation 模板:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "This template will deploy stuff",
    "Parameters":{
    "myParamToLambdaFunction" : {
        "Description" : "Please enter the the value",
        "Type" : "String",
        "ConstraintDescription" : "must have some value."
    }
},
"Resources": {
    "SecGrpValidatorFromCFTemplate": {
        "Type": "AWS::Lambda::Function",
        "Properties": {
            "FunctionName": "mylambdafunctionname",
            "Handler": "myfile.lambda_handler",
            "Role": {
                "Fn::GetAtt": ["somerole", "Arn"]
            },
            "Timeout": "30",
            "Runtime": "python2.7",
            "Code": {
                "S3Bucket":"mybucket",
                "S3Key":"mylambdafunction.zip"
            }
        }
    }
}

我需要将myParamToLambdaFunction 的值传递给 Lambda 函数。

有办法吗?

【问题讨论】:

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


    【解决方案1】:

    18 Nov 2016 开始,AWS Lambda 现在支持 environment variables,CloudFormation 通过 AWS::Lambda::Function 资源上的 Environment 属性支持。

    像这样将Environment 属性添加到您的资源中:

    {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Description": "This template will deploy stuff",
        "Parameters":{
        "myParamToLambdaFunction" : {
            "Description" : "Please enter the the value",
            "Type" : "String",
            "ConstraintDescription" : "must have some value."
        }
    },
    "Resources": {
        "SecGrpValidatorFromCFTemplate": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "FunctionName": "mylambdafunctionname",
                "Handler": "myfile.lambda_handler",
                "Role": {
                    "Fn::GetAtt": ["somerole", "Arn"]
                },
                "Timeout": "30",
                "Runtime": "python2.7",
                "Code": {
                    "S3Bucket":"mybucket",
                    "S3Key":"mylambdafunction.zip"
                },
                "Environment": {
                    "Variables": {
                        "myParam": {
                            "Ref": "myParamToLambdaFunction"
                        }
                    }
                }
            }
        }
    }
    

    然后根据您的运行时平台从您部署的 Lambda 函数中引用环境变量(例如,Python 中的os.environ['myParam'])。

    【讨论】:

      【解决方案2】:

      按照您在模板中所做的那样创建 lambda 函数后,您可以定义一个支持 Lambda 的自定义资源来使用自定义参数调用该函数,并且您还可以处理来自 lambda 的响应。有关更多信息和示例,请参阅https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html

      【讨论】:

      • 不是我真正想要的。我需要将 CloudFormation 的输入参数的值注入 Lambda 中的变量之一。我也将问题发布到 AWS 论坛,并提供了更多细节:Link to the Question on AWS Forum
      • 您可以在 node.js 中执行此操作,因为您可以在 CF 中内联提供 lambda 代码,但在 python 中我认为没有办法。
      【解决方案3】:

      如果您的 lambda 不是太复杂,您可以将其内联到您的模板中,而不是依赖上传的 zip 文件。

      这是我使用输入参数做的一个:

      "CopyLogsStreamToFirehose": {
        "Type": "AWS::Lambda::Function",
        "Properties": {
          "Code": {
            "ZipFile": {
              "Fn::Join": [
                "",
                [
                  "import base64\n",
                  "import boto3\n",
                  "def on_new_kinesis_data(event, context):\n",
                  "   client = boto3.client('firehose')\n",
                  "   records = [ {'Data': base64.b64decode(r['kinesis']['data']) + '\\n' } for r in event['Records']]\n",
                  "   client.put_record_batch(DeliveryStreamName='",
                  {
                    "Ref": "LogsDeliveryStream"
                  },
                  "', Records=records)\n",
                  "   print 'Successfully processed {} records.'.format(len(event['Records']))\n"
                ]
              ]
            }
          },
          "Description": "Kinesis Stream to Firehose Stream",
          "Handler": "index.on_new_kinesis_data",
          "Role": {
            "Fn::GetAtt": [
              "CopyLogsStreamToFirehoseRole",
              "Arn"
            ]
          },
          "Runtime": "python2.7",
          "Timeout": 5
        }
      

      【讨论】:

      • 我的 lambda 函数超过 4096 个字符,这是对内联函数的限制,我在我的 lambda 函数中使用了多个库。
      • 如果您的 lambda 与其他一些资源(例如:EC2 实例)相关,您可以将参数设置为 CloudFormation 中的 EC2 标签,然后在 lambda 中检索它。我以前做过一次:我从 AutoScaling Group 读取标签,然后使用此信息更新 route53 中的记录。
      【解决方案4】:

      这是 Lambda 需要环境变量的地方。这对我的工作来说是个大问题。我尝试了两种方法。

      1 - 您的 lambda 函数名称将包含您的堆栈名称。使用一些拆分,提取该值,例如var stackName = context.functionName.split('-')[0];,然后调用 describeStacks 来检索您的堆栈。从那里从输出/参数中提取所需的数据。这很好用,但请注意,查询 CloudFormation 堆栈的限制相当有限。如果您的 lambda 快速触发,选项 2 将更适合您。

      2 - 手动将您的终端节点输出到 DynamoDB 表中,其中堆栈名称作为键(S3 中的文件也可以工作,具体取决于您的延迟要求)并查询您的环境数据。如果您真的愿意,您可以使用自定义 cloudformation 资源自动执行此堆栈数据输出。

      【讨论】:

        【解决方案5】:

        调用您的 lambda 函数的任何内容都可以在调用 lambda 函数时将元数据传递给它。

        例如,对于自动缩放组,您可能希望在实例启动或终止时调用 lambda 函数。在这种情况下,AWS::AutoScaling::LifecycleHook 资源包括 NotificationMetadata,其中包含一个带有一项的字典,Route53ZoneId

        "MyLifecycleHook": {
          "Type": "AWS::AutoScaling::LifecycleHook",
          "Properties": {
            "AutoScalingGroupName": { "Ref": "MyASG" },
            "LifecycleTransition": "autoscaling:EC2_INSTANCE_LAUNCHING",
            "NotificationMetadata": { "Fn::Join": [ "", [
              "{",
              "  \"Route53ZoneId\": \"YOUR_ROUTE53_ZONE_IDENTIFIER_HERE\"",
              "}"
            ] ] },
            "NotificationTargetARN": { "Ref": "MyTopic" },
            "RoleARN": { "Fn::GetAtt": [ "MyLifecycleHookRole", "Arn" ] }
          }
        }
        

        然后在您的 lambda 处理程序中,假设您使用 Python 编写它,您可以访问该字典中的变量,例如:

        def handler(event, context):
            message = json.loads(event[u'Records'][0][u'Sns'][u'Message'])
            metadata = json.loads(message['NotificationMetadata'])
        
            logger.info("Route53 Zone Identifier {0}".format(metadata['Route53ZoneId']))
        

        【讨论】:

        • 是的,我们在生产中使用它并且效果很好。请注意,上例中需要一个 SNS 主题,因为自动缩放生命周期挂钩不能直接调用 lambda,只能发布到 SNS,而 SNS 会调用 lambda。
        • 我的 lambda 将被 Cloudwatch 事件调用。我希望他们支持 NotificationMetadata。
        【解决方案6】:

        如果您的 Lambda 函数位于 API Gateway 之后,您可以使用RequestTemplates 在请求到达函数之前对其进行修改。下面的配置将发送原始请求正文以及定义为YourCloudformationParameter的参数:

        "RequestTemplates": {
          "application/json": {
            "Fn::Join": [
              "",
              [
                "{",
                  "\"body\": $input.json('$'),",
                  "\"env\": {",
                    "\"yourCloudformationParameter\": \"", { "Ref": "YourCloudformationParameter" }, "\"",
                  "}",
                "}"
              ]
            ]
          }
        },
        

        【讨论】:

          【解决方案7】:

          在互联网上搜索了一个很好的答案,但找不到任何答案。这是我自己想出来的,它比我迄今为止看到的任何东西都容易。使用自定义资源:

          Resources:
            TriggerAccountLambda:
              Type: "Custom::TriggerAccountLambda"
              DeletionPolicy: Retain
              Properties:
                ServiceToken: "arn:aws:lambda:us-east-1:123436875680:function:your-function"
                var1: !Ref var1
                var2: !Ref var2
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-07-19
            • 2018-04-11
            • 2017-11-15
            • 2016-04-08
            • 2016-02-29
            • 1970-01-01
            • 2023-04-06
            • 2019-01-21
            相关资源
            最近更新 更多