Dygerati Dygerati - 5 months ago 49
Node.js Question

Using Cloudformation to Deploy Lamba, Including a Parameter that the function will have access to

We have an API that will be used to provision certain resources in AWS using Cloud Formation. This includes a Lambda function that will send events to S3, with the bucket being configurable. The thing is, we will know the bucket name when we provision the lambda, not within the lambda code itself.

As far as I can tell, there is no way to inject the S3 bucket name at the time of provisioning, in the Cloud Formation Template itself. Is that true?

The only solution I can see is to generate the function code on the fly, and embed that into the Cloud Formation template. This would make us unable to use any NPM dependencies along with the function code. Is there a better option?

Answer

So, I realized I had never updated this question with my eventual solution. I ended up embedding a proxy lambda function into the cloudformation template, which enabled me to inject template parameters.

Example:

{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Creates a function to relay messages from a Kinesis instance to S3",
"Parameters": {
    "S3Bucket" : {
        "Type": "String",
        "Description": "The name of the S3 bucket where the data will be stored"
    },
    "S3Key": {
        "Type": "String",
        "Description": "The key of the directory where the data will be stored"
    }
},

"Resources": {
    "mainLambda": {
        "Type" : "AWS::Lambda::Function",
        "Properties" : {
            "Handler" : "index.handler",
            "Description" : "Writes events to S3",
            "Role" : { "Ref": "LambdaRoleARN" },
            "Runtime" : "nodejs4.3",
            "Code" : {
                "S3Bucket": "streams-resources",
                "S3Key": "astro-bass/${GIT_COMMIT}/lambda/astro-bass.zip"
            }
        }
    },

    "lambdaProxy": {
        "Type" : "AWS::Lambda::Function",
        "Properties" : {
            "Handler" : "index.handler",
            "Runtime" : "nodejs",
            "Code" : {
                "ZipFile": { "Fn::Join": ["", [
                    "var AWS = require('aws-sdk');",
                    "var lambda = new AWS.Lambda();",
                    "exports.handler = function(event, context) {",
                        "event.bundledParams = ['",
                            { "Ref": "S3Bucket" },
                            "','",
                            { "Ref": "S3Key" },
                        "'];",
                        "lambda.invoke({",
                            "FunctionName: '",
                            { "Ref": "mainLambda" },
                            "',",
                            "Payload: JSON.stringify(event, null, 2),",
                            "InvocationType: 'Event'",
                        "}, function(err, data) {",
                            "if(err) {",
                                "context.fail(err);",
                            "}",
                            "context.done();",
                        "});",
                    "};"
                ]]}
            }
        }
    },
},

...
}

The proxy function had the parameters injected into its code (s3bucket/key), and then it invokes the main lambda with a modified event object. It's a little unorthodox but struck me as much cleaner than the other available solutions, such as parse stacknames/etc. Worked well thus far.

Note that this solution only works currently with the legacy node environment. Not an issue, but worrisome in terms of the longevity of this solution.