Infrastructure & Automation

Automating Amazon CloudWatch alarms with an AWS CloudFormation macro

Have you ever wondered if it was possible to automate tasks in AWS CloudFormation? In this post, I will discuss an alternative to the age-old problem of writing out each alarm within your CloudFormation template: a CloudFormation macro that takes your template and appends alarms. By using this macro, you can not only save time when writing out your templates, you can ensure that important resources have appropriate monitoring in place.

The code for this macro is available for you to modify and add your own alarms and resource types. Alarms that are based on custom metrics are not included with this example, but they are supported.

This post will cover:

  • Introduction to the AWS CloudFormation macro feature
  • How the macro works
  • Deploying the macro
  • Using the macro in your CloudFormation templates

Prerequisites

  • Access keys and secret keys with permission to create CloudFormation stacks; the keys are needed to deploy the local artifacts along with the template via the command line. Please refer to the AWS documentation for more information about access keys and secret API keys.
  • AWS Command-Line Interface (AWS CLI). You can find more information on setting up the AWS CLI in the AWS CLI documentation.

The CloudFormation macro feature

Macros enable custom processing on templates before a change set is created. If you have ever deployed a serverless template, you have used a macro to transform it into a compliant CloudFormation template. A macro is comprised of two main components:

  • A CloudFormation macro resource type
  • A corresponding Lambda function that handles the logic

The macro is deployed into each of the AWS Regions in which it is to be used. Note that when macros are used within a template, they are deployed in the order in which they are listed in the template. For more information about using macros to perform custom processing on templates, see the AWS CloudFormation documentation.

How this macro works

Here is a diagram of the process.

cloudwatch cloudformation diagram

You can see that the macro is invoked when CloudFormation detects the transform macro reference in the template. From there, the template is sent to the Lambda function that handles the logic of the macro.

In our case, that Lambda function will take the template fragment, which is the entire template in JSON, and filter each resource by its type. If that type is supported, a set of alarms that are applicable will be appended. The Lambda function then sends the new template back to AWS CloudFormation, which creates a stack.

The table below outlines the alarms that are pre-written into the macro for Amazon Elastic Compute Cloud (Amazon EC2), load balancers, Lambda, and Network address translation (NAT) gateways. Feel free to adjust the macro to meet your needs.

Service Alarm Description
Amazon EC2 CPUUtilization CPU utilization percentage
Amazon EC2 StatusCheckFailed_Instance Health check for the instance
Application Load Balancer HTTPCode_ELB_5XX_Count The number of HTTP 5XX server error codes that originate from the load balancer
Application Load Balancer RejectedConnectionCount Rejected connections due to the load balancer reaching its maximum number of connections
Network Load Balancer UnHealthyHostCount The number of targets that are considered unhealthy
Network Load Balancer HealthyHostCount The number of targets that are considered healthy
Lambda Errors The number of invocations that failed due to errors in the function (response code 4XX)
Lambda Invocations The number of times a function is invoked in response to an event or invocation API call
NAT gateway ErrorPortAllocation The number of times the NAT gateway could not allocate a source port
NAT gateway ActiveConnectionCount The total number of concurrent active TCP connections through the NAT gateway

Deploying the macro

You can view and clone the macro in the GitHub repo.

Use the AWS CLI to set the appropriate profile/API keys. Open a terminal, and from the root of the repository directory, type the following, replacing $BUCKET and $STACKNAME with your own values.

aws cloudformation package --template-file MacroTemplate.yaml --s3-bucket $BUCKET  --output-template-file packaged-template.yaml

aws cloudformation deploy --template-file packaged-template.yaml --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND --stack-name $STACKNAME 

You can find more information on the package and deploy commands in the AWS CLI documentation.

Using the macro

To use the macro, add the following line to the top of your templates:

Transform: AlarmMacro

If you are using a serverless template, you must invoke the serverless macro first:

Transform: ["AWS::Serverless-2016-10-31", "AlarmMacro"]

Conclusion

To conclude, I have discussed the benefits of CloudFormation macros, and I have provided an example of how a particular macro can speed up the development of CloudFormation templates. I hope you enjoyed reading the post. As always, let us know your feedback in the comments below.