6
votes

A team member and I have a CloudFormation stack with a nodejs Lambda backed custom resource.

Upon updating the lambda/parameters/trigger, we would like the Lambda to first delete the 3rd party resources it made and then create new ones based on the new parameters.

Here is our exports.handler for the lambda.

if (event.RequestType == "Delete") {
    console.log("Request type == Delete")
    var successCallback = function(event, context) {
        sendResponse(event, context, "SUCCESS");
    }
    doDeleteThings(event, context, successCallback);
} else if (event.RequestType == "Create") {
    console.log("request type == create")
    doCreateThings(event, context);
} else if (event.RequestType == "Update") {
    console.log("request type == update")
    var successCallback = function(event, context) {
        doCreateThings(event, context);
    }
    doDeleteThings(event, context, successCallback);
} else {
    sendResponse(event, context, "SUCCESS");
}

We have tested the code and it works for both create and delete in CloudFormation, and create, delete and update in stackless-mode (where we set: event.RequestType = process.env.RequestType and sendResponse doesn't do the usual CloudFormation response POSTing, but instead just does context.done()), but we cannot seem to make it work on update in CloudFormation. I'm beginning to think that we are misunderstanding what 'update' on a Lambda is supposed to do.

It doesn't help that we've never been able to see CloudWatch logs for Lambda functions created by CloudFormation before.

Here is the relative portion of the CloudFormation template:

   "ManageThirdPartyResources": {
        "Type": "AWS::Lambda::Function",
        "Properties": {
            "Code": {
                "S3Bucket": "<bucketname>",
                "S3Key": "<zipname>.zip"
            },
            "Description": { "Fn::Join": ["", ["Use cloudformation to automatically create third party resources for the ", { "Ref": "ENV" }, "-", { "Ref": "AWS::StackName" }, " environment"]] },
            "Environment": {
                "Variables": {
                    <environment variables that will probably be the things changing.>
                }
            },
            "FunctionName": {
                "Fn::Join": ["_", [{ "Ref": "AWS::StackName" }, "ManageThirdPartyResources"]]
            },
            "Handler": "index.handler",
            "Role": "<role>",
            "Runtime": "nodejs4.3",
            "Timeout": 30
        }
    },
    "ThirdPartyResourcesTrigger": {
        "Type": "Custom::ThirdPartyResourcesTrigger",
        "Properties": {
            "ServiceToken": { "Fn::GetAtt": ["ManageThirdPartyResources", "Arn"] }
        }
    },

Thanks!

1

1 Answers

6
votes

Updates will be triggered on your Custom::ThirdPartyResourcesTrigger if one of its properties change. If properties on the Lambda function change, it will not trigger an update on the Custom::ThirdPartyResourcesTrigger.

So if you want to trigger updates on Custom::ThirdPartyResourcesTrigger, you must modify its properties. For example, you can add a property to ThirdPartyResourcesTrigger called ThingName, and whenever you change the value of ThingName, your Lambda will be called with the Update request type:

"ThirdPartyResourcesTrigger": {
    "Type": "Custom::ThirdPartyResourcesTrigger",
    "Properties": {
        "ServiceToken": { "Fn::GetAtt": ["ManageThirdPartyResources", "Arn"] },
        "ThingName": "some value"
    }
},

As for logging, make sure the IAM role assumed by your Lambda function has the required permissions for CloudWatch logs:

"Effect": "Allow"
"Action": "logs:*"
"Resource": "arn:aws:logs:*:*:*"