Automatic CloudFormation Template Validation with Guard::CloudFormation

One of the nice little conveniences I’ve started to use in my daily work with Amazon Webservices CloudFormation is the Guard::CloudFormation ruby gem.

The Guard gem "is a command line tool to easily handle events on file system modifications" which, simply put, means “run a command when a file changes”. While I’ve used a number of different little tools to do this in the past, Guard presents a promising base to build more specific test executors on so I’ve started to integrate it in to more aspects of my work flow. In this example I’m going to show you how to validate a CloudFormation template each time you save a change to it.

The example below assumes that you already have the AWS CloudFormation command line tools installed, configured and available on your path.

# our example, one resource, template.

$ cat example-sns-email-topic.json 
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description" : "Creates an example SNS topic",

  "Parameters" : {
    "AutoScaleSNSTopic": {
      "Description": "Email address for notifications.",
      "Type": "String",
      "Default": "testymctest@example.org"
    }
  },

  "Resources" : {
    "EmailSNSTopic": {
      "Type" : "AWS::SNS::Topic",
      "Properties" : {
        "DisplayName" : "Autoscaling notifications for Location service",
        "Subscription": [ {
          "Endpoint" : { "Ref" : "AutoScaleSNSTopic" },
          "Protocol" : "email"
        } ]
      }
    }
  }
}

# now, do a manual run to ensure all the basics are working

$ cfn-validate-template --template-file example-sns-email-topic.json
PARAMETERS  AutoScaleSNSTopic  testymctest@example.org  false  Email address for notifications.

Now that we have confirmed our CloudFormation tools are working we can sprinkle some automation all over it.

# install the required gems

gem install guard guard-cloudformation

# and then create a basic Guardfile
# in this case we watch all .json files in the current directory

cat << 'EOC' > Guardfile

guard "cloudformation", :templates_path => ".", :all_on_start => false do
  watch(%r{.+\.json$})
end

EOC

# run guard

$ guard 
10:07:49 - INFO - Guard is using NotifySend to send notifications.
10:07:49 - INFO - Guard is using TerminalTitle to send notifications.
10:07:49 - INFO - Guard is now watching at '/home/cfntest/.../cloudformations/location'
[1] guard(main)> 

Now that guard is up and running open up a second terminal to the directory you’ve been working in. We’ll now make a couple of changes and watch Guard in action. First we’ll make a small change to the text that shouldn’t break anything.

# run the sed command to change the email address - shouldn't break

$ sed -i -e 's/testymctest@example.org/test@example.org/' example-sns-email-topic.json

# in the term running Guard we see -

10:12:31 - INFO - Validating: example-sns-email-topic.json
Validating example-sns-email-topic.json...
PARAMETERS  AutoScaleSNSTopic  test@example.org  false  Email address for notifications.

On my desktop the validation output is a lovely terminal green and I also get a little pop-up in the corner telling me the validate was successful. Leaving Guard open, we’ll run a breaking change.

# run the sed command to remove needed quotes - this will not end well

$ sed -i -e 's/"Ref" :/Ref :/' example-sns-email-topic.json

# in the term running Guard we see -

10:13:51 - INFO - Validating: example-sns-email-topic.json
Validating example-sns-email-topic.json...
cfn-validate-template:  Malformed input-Template format error: JSON not well-formed. (line 19, column
 27)
Usage:
cfn-validate-template
       [--template-file  value ] [--template-url  value ]  [General Options]
For more information and a full list of options, run "cfn-validate-template --help"
FAILED: example-sns-email-topic.json

The ‘FAILED: example-sns-email-topic.json’ line is displayed in less welcome red, the dialog box pops up again and we know that our last change was incorrect. While this isn’t quite as nice as having vim running the validate in the background and taking you directly to the erroring line it’s a lot easier to plumb in to your tool chain and gives you 80% of the benefit for very little effort. For completeness we’ll reverse our last edit to fix the template.

# sed command to fix the template

$ sed -i -e 's/Ref :/"Ref" :/' example-sns-email-topic.json

# in the term running Guard we see -

10:22:42 - INFO - Validating: example-sns-email-topic.json
Validating example-sns-email-topic.json...
PARAMETERS  AutoScaleSNSTopic  test@example.org  false  Email address for notifications.

One last config option that’s worth noting is ':all_on_start => false' from the Guardfile. If this is set to true then, as you’d expect by the name, all CloudFormation templates that match the watch will be validated when Guard starts. I find the validates to be quite slow and I often only dip in to a couple of templates so I set this to off. If you spend more focused time working on nothing but templates then having this set to ‘true’ gives you a nice early warning in case someone checked in a broken template. Although your git hooks shouldn’t allow this anyway. But that’s a different post.

After reading through the validate errors of a couple of days work it seems my most common issue is from continuation commas. It’s just a shame that CloudFormation doesn’t allow trailing commas everywhere.