verified_tinker
verified_tinker

Reputation: 665

What's the right way to update a CloudFormation stack from within a Lambda function?

So far, my code looks like this:

const AWS = require('aws-sdk');
AWS.config.update({ region: 'eu-west-1' });
const CF = new AWS.CloudFormation({ apiVersion: '2010-05-15' });

const updateParams = {
  StackName: myStackName,
  UsePreviousTemplate: true,
  Parameters: [
    {
    ParameterKey: "StackOffline",
    ParameterValue: "Online"
    }
  ],
  Capabilities: [
    "CAPABILITY_IAM",
    "CAPABILITY_AUTO_EXPAND"
  ]
};

exports.handler = async (event) => {    
    CF.updateStack(updateParams, (err, data) => {
      if (err)
        console.log(err, err.stack);
      else
        console.log(data);
    });
    
    // ...
};

The problem is... it doesn't do anything. When it's run, it doesn't actually log anything; not data, nor an error message.

I tried using both the stack name and the unique stack ID, but neither worked.

Finally, I saved the return value (Request, I think) of updateStack() and here's its response property:

response: Response {
  request: [Circular *1],
  data: null,
  error: null,
  retryCount: 0,
  redirectCount: 0,
  httpResponse: HttpResponse {
    statusCode: undefined,
    headers: {},
    body: undefined,
    streaming: false,
    stream: null,
    _abortCallback: [Function: callNextListener]
  },
  maxRetries: 3,
  maxRedirects: 10
}

(I can also post the entire Request, if needed. I didn't because it's enormous, and I don't know if it contains any sensitive information.)

I assume those null values are a problem. Where am I going wrong?

Upvotes: 2

Views: 488

Answers (1)

Jason Wadsworth
Jason Wadsworth

Reputation: 8887

You've run into the age old async/await combined with callback problem. TLDR; don't combine them.

AWS Lambda function handler in Node.js

For non-async handlers, function execution continues until the event loop is empty or the function times out. The response isn't sent to the invoker until all event loop tasks are finished. If the function times out, an error is returned instead. You can configure the runtime to send the response immediately by setting context.callbackWaitsForEmptyEventLoop to false.

Change your code to something like this instead:

const AWS = require('aws-sdk');
AWS.config.update({ region: 'eu-west-1' });
const CF = new AWS.CloudFormation({ apiVersion: '2010-05-15' });

const updateParams = {
  StackName: myStackName,
  UsePreviousTemplate: true,
  Parameters: [
    {
    ParameterKey: "StackOffline",
    ParameterValue: "Online"
    }
  ],
  Capabilities: [
    "CAPABILITY_IAM",
    "CAPABILITY_AUTO_EXPAND"
  ]
};

exports.handler = async (event) => {    
    try {
        const data = await CF.updateStack(updateParams).promise();
        console.log(data);

    // ...

    }
    catch (err) {
        console.log(err, err.stack);
    }    
};

Upvotes: 2

Related Questions