Invoking Functions on Deploy
Overview
Within a serverless infrastructure, Functions can be invoked in response to an event emitted by other cloud resources. However, there are situations where a Function needs to be invoked on the very first deployment, or every deployment in the development process.
Trigger on First Deploy
Stackery provides the option to trigger a Function on the first stack deployment. These are examples of actions that only need to be performed once, during the creation of the stack.
Use cases include:
- Initializing the schema or populating a database
- Inviting administrator users on the application's initial deploy
- Registering the application inside another service discovery framework
Trigger on Every Deploy
Trigger on Every Deploy is only available after Trigger on First Deploy has been enabled
Stackery provides the option to trigger a Function on every stack deployment. By design, this option only becomes available once Trigger on First Deploy
has been enabled.
Use cases include:
- Database migrations
- Updating users on new app releases
- Single-page application packaging/deployment
In some single-page applications, there is a Function responsible for packaging and publishing it to an S3 bucket to be hosted. This Function needs to be triggered after any updates are made to an application in order for the S3 bucket (or any resources within the stack) to reflect the changes. With Trigger on Every Deploy
enabled for this Function, deploying the stack in Stackery also deploys the newest version of the application.
Read on to learn how Stackery implements these features and additional requirements to consider.
Implementation
Trigger on First Deploy
adds a resource similar to this in template.yaml
:
MyFunctionDeployTrigger:
Type: Custom::FunctionDeployTrigger
Properties:
ServiceToken: !GetAtt MyFunction.Arn
This CloudFormation custom resource uses the Function's ARN, for its ServiceToken
property. When it's created in CloudFormation as a part of the stack deployment process, it will trigger the Function.
This custom resource will remain in your template.yaml
file but will not trigger the assigned function on any subsequent stack deployments. CloudFormation does not recreate the custom resource and trigger the function unless it detects changes to this resource within template.yaml
.
Trigger on Every Deploy
adds a resource similar to this in template.yaml
:
MyFunctionDeployTrigger:
Type: Custom::FunctionDeployTrigger
Properties:
ServiceToken: !GetAtt MyFunction.Arn
StackeryDeploymentTimestamp: !Ref StackeryDeploymentTimestamp
The only addition to the custom resource is the StackeryDeploymentTimestamp
property. This value gets injected by Stackery at deployment time. No two timestamps will be the same, which means this value will update after each deployment.
Whenever a stack is deployed, CloudFormation will detect this change in the custom resource's properties, and update it. Following the same chain of events as the Trigger on First Deploy
feature, this custom resource will trigger the Function.
The purpose of
StackeryDeploymentTimestamp
is to be detected by CloudFormation as an update, resulting in a "refresh" of the custom resource and invocation of the Function
Function Requirements
The CloudFormation custom resource is responsible for invoking it's assigned Function, so the only requirement is configuring the Function to respond to the custom resource's event.
This includes importing a module similar to cfn-custom-resource
for Node and sending back an appropriate response at the end of your Function. The end of this Function walkthrough, from one of our tutorials, has a completed Function that meets these requirements. Refer to the cfn-custom-resource documentation for more info.
Troubleshooting
If all of the following apply to a stack deployment, it may be due to a Lambda function timeout/failure:
- Stack deployment is taking longer than 10-15 minutes
- Maintains the CREATE_IN_PROGRESS event for
Custom::FunctionDeployTrigger
You can confirm this by accessing the CloudWatch logs for your Lambda function.
CloudFormation will continue this deployment waiting for a success response from the custom resource indicating that the Lambda function has been triggered. If a success response is not received after 60 minutes, it will fail to create the resource and begin a DELETE_IN_PROGRESS event that may take an additional 60 minutes to complete. This resulting delete event for the custom resource could also fail, and CloudFormation will retry this deletion up to two more times.
Use the following to "escape" this process by running a curl response indicating a FAILED
status while the custom resource is in the CREATE_IN_PROGRESS event.
In the AWS Console, navigate to your Lambda function's CloudWatch Logs
Access the most recent Log Stream
Locate the log with a message that consists of
START
The log directly below it holds the values we need for our curl request (message will have RequestType, ServiceToken, etc)
Copy and save the following values from this log
ResponseURL
StackId
RequestId
LogicalResourceId
Use the following to construct the curl command by replacing the placeholders with the values from your CloudWatch log
curl -X PUT '{RESPONSE_URL}' -H 'Content-Type:' -d '{"Status":"FAILED","Reason":"Manual Success","PhysicalResourceId":"resource","RequestId":"{REQUEST_ID}","LogicalResourceId":"{LOGICAL_RESOURCE_ID}","StackId":"{STACK_ID}"}'
- Run your curl command in a terminal window
- Head back to your stack's CloudFormation console to verify the failed message has been received and a CREATE_FAILED event displays for
Custom::FunctionDeployTrigger
. - A rollback event will begin on your stack following this create failed event.