5

I have a CloudFormation template that looks like this:

{
    "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"
            }
        }
    }
}

I need to pass the value of myParamToLambdaFunction to the Lambda function.

Is there a way to do so?

7 Answers 7

4

As of 18 Nov 2016, AWS Lambda now supports environment variables, which CloudFormation supports through the Environment property on the AWS::Lambda::Function resource.

Add an Environment property to your resource like this:

{
    "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"
                    }
                }
            }
        }
    }
}

Then reference the environment variable from your deployed Lambda function according to your runtime platform (e.g., os.environ['myParam'] in Python).

Sign up to request clarification or add additional context in comments.

Comments

0

Once you create the lambda function as you did in your template, you can define a Lambda-backed custom resource to invoke the function with custom parameters and also you can process the response coming from lambda. See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html for more information and examples.

2 Comments

not really what i am looking for. I need to inject the value of CloudFormation's input parameter into one of the variables in Lambda. I have posted the question to AWS forum as well with little more details: Link to the Question on AWS Forum
You could do it in node.js because you could provide lambda code inlinely in CF but in python I don't think there is a way to do it.
0

If your lambda is not too complex, you can inline it in your template instead of relying on an uploaded zip file.

Here's one that I did that uses an input parameter:

"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
  }

2 Comments

my lambda function is more than 4096 characters which is a limitation on inline function and i am using multiple libraries in my lambda function.
If your lambda is related to some other resources (ex: an EC2 instance) you could set the parameter as an EC2 tag in CloudFormation, and then retrieve it in lambda. I've done this once before: I read a tag from an AutoScaling Group and then use this information to update a record in route53.
0

This is where Lambda needs environment variables. It's been a huge issue for my work. I've tried two approaches.

1 - Your lambda function name will contain your stack name. Use some splitting, extract that value e.g var stackName = context.functionName.split('-')[0];, then call describeStacks to retrieve your stack. Pull out your required data from outputs/parameters from there. This works fine, but note that querying CloudFormation stacks has a fairly modest limit. If your lambda is firing rapidily, option 2 will work better for you.

2 - Manually output your endpoints into a DynamoDB table with the stack name as the key (a file in S3 could work too, depending on your latency requirements) and query that for your environment data. If you really wanted to you could automate this stack data output using a custom cloudformation resource.

Comments

0

Whatever calls your lambda function can pass metadata to a lambda function when it is invoked.

For example, with an auto-scaling group, perhaps you want to call a lambda function when an instance launches or is terminated. In this case, the AWS::AutoScaling::LifecycleHook resource includes NotificationMetadata which contains a dictionary with one item, 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" ] }
  }
}

Then in your lambda handler, assuming you're writing it in Python, you can access variables in that dictionary, for example:

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']))

2 Comments

Yup, we use it in production and it works well. Note you'll need an SNS topic in the example above as the auto-scaling lifecycle hook can't call lambda directly, can only publish to SNS, and SNS calls lambda.
My lambda will be invoked by Cloudwatch event. I hope they support NotificationMetadata.
0

If your Lambda function is behind API Gateway, you can use RequestTemplates to modify the request before it reaches the function. The configuration below would send the original request body along with the parameter defined as YourCloudformationParameter:

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

Comments

0

Scoured the internet for a good answer to this and couldn't find any. Figured this out on my own and it was easier than anything I've seen thus so far. Use a custom resource:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.