CloudFormation: cfn-lint Pre-Commit Git Hook


There are a few ways to validate CloudFormation templates, but I mostly use cfn-lint. I run it in my build pipelines, but I also like to run it locally just so I don’t have to wait for a build to find out if I did a typo. I work on a lot of templates, though, and it’s easy to forget. I still end up finding out about simple errors after builds fail. Good news! Git can remember to do it for me.

I keep all my templates in git repos, and git has hooks to run scripts on actions like commits. They’re covered in the core docs and Atlassian also has a pretty good guide.

My goal is to keep bad code out of the git history, so I want commits to fail if there are linting errors. For that we need .git/hooks/pre-commit file:

#!/usr/bin/env bash

find . -type f -name "*\.yaml" | xargs cfn-lint

It must be executable: chmod u+x .git/hooks/pre-commit

I bodged together a repo with some bad templates in it so you can see the output:

└── templates
    ├── app1
    │   └── working.yaml
    └── app2
        └── broken.yaml

There’s a deliberate syntax error in broken.yaml. If I try to commit:

git commit -m "Test cfn-lint pre-commit hook."
E0000 Template needs to be an object.

… and there’s no commit. My changes are still just staged:

git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   templates/app1/working.yaml
	new file:   templates/app2/broken.yaml

A few notes:

  • You can ignore hook errors with the --no-verify flag. I use this when I need to break down my work into iterations of partially-working commits, then I go back and squash them in an interactive rebase.
  • These hooks are unique to your clone, they don’t automatically copy to others. That’s fine for my cases because this is just my personal development process; the build jobs are the real gateway.
  • cfn-lint doesn’t automatically descend into subdirectories, which is why I do a find.
  • This only works if the hook script exits non-zero when there are errors. My find/xargs pattern does, but there are lots of patterns that don’t. If you use a for/do/done loop, for example, you can end up masking the return codes and the hook won’t block commits. If you modify the pattern for searching, make sure you test the failure cases.
  • I always use the .yaml extension for my templates, and typically they’re the only YAML files in my repo. If your case is more complex than that, you’ll need to modify the pattern for searching.

Happy automating!


Need more than just this article? I’m available to consult.

You might also want to check out these related articles: