← All posts
CloudFormation

How to validate a CloudFormation template (CLI and cfn-lint)

Validate a CloudFormation template before you deploy, with the AWS CLI and cfn-lint

Validating an IaC template before you hand it to CloudFormation is one of the cheapest ways to avoid the dreaded UPDATE_ROLLBACK_IN_PROGRESS. You can catch a lot without ever creating a stack - but only if you understand what the tool you use actually checks. Here are the two we usually use, and where one stops and the other takes over.

Updated June 2026 This post was first published in 2016 - it has been refreshed for YAML, current AWS CLI output, and cfn-lint.

Structural validation with the AWS CLI

The quickest check is the validate-template command. It does not create anything - it just asks CloudFormation whether the template is well-formed. It works the same way for JSON and YAML:

aws cloudformation validate-template --template-body file://template.yaml

If the template lives in an S3 bucket, point at it with --template-url instead:

aws cloudformation validate-template --template-url https://s3.amazonaws.com/my-bucket/template.yaml

When the template is valid, you get back the basic information about the stack template - the parameters, the description, and any capabilities (like CAPABILITY_IAM) the stack will need. If your stack template is not correct, you’ll receive an error message specifying what is wrong, for example:

An error occurred (ValidationError) when calling the ValidateTemplate operation: Template format error: Every Outputs member must contain a Value object

What validate-template does not catch

Here is the part that trips people up. validate-template only checks the structure of the template - that the JSON or YAML parses, that the sections are in the right shape, and that the parameters line up. It does not check your resources against the CloudFormation resource specification.

So a template can pass validate-template cleanly and still fail the moment you deploy it, because the command never looked at whether Bucketname should have been BucketName, whether a property accepts the value you gave it, or whether a required property is missing. If validate-template fails, you can be sure the stack will not be created - but if it passes, that is not a promise that it will. AWS make the same point in their template best practices.

To catch those mistakes before you deploy, you need a linter.

Catch the real errors with cfn-lint

cfn-lint is the linter built by the CloudFormation team. Unlike validate-template, it checks your resources against the resource specification and against a long list of best practices, so it finds the property typos, invalid values and missing required properties that the CLI waves through.

Install it from pip and point it at your template:

pip install cfn-lint
cfn-lint template.yaml

It tells you exactly which line and which property is wrong, which is a much better experience than reading a rollback event in the Console twenty minutes later.

If you would rather not install anything locally - which is usually the case in a build pipeline - you can run cfn-lint from a container. There is a snag, though: the CloudFormation team has never shipped an official, up-to-date Docker image for cfn-lint, and most of the community ones drift out of date or quietly stop being maintained. We needed a current one for our own pipelines, so rather than keep it to ourselves we made it public and set it to rebuild itself the day each new cfn-lint release lands. You can use it like this:

docker run --rm -v "$(pwd):/data" mysteriouscode/cfn-lint cfn-lint /data/template.yaml

If you want the details - how the automatic rebuild works, and copy-and-paste examples for Jenkins and GitLab - they are in our write-up of the cfn-lint Docker image, and each change is tracked in the cfn-lint series.

Where to run validation

Both tools earn their keep at different points:

  • While you write the template - run cfn-lint from the command line or your editor for instant feedback.
  • On every commit - a git pre-commit hook stops a broken template before it ever reaches the branch.
  • In your pipeline - running validate-template and cfn-lint in continuous integration makes valid, lint-clean templates a standard the whole team has to meet, not something each person remembers to check.

That last point is the one that matters most on a real codebase. A check that relies on someone remembering to run it will be skipped sooner or later, so the fix is to automate it: run validate-template and cfn-lint in your pipeline, and a broken template fails the build instead of failing a deployment. It is one of the cheapest checks you can add, and it saves you from the painful way of finding the same mistakes - watching a stack roll back in the Console.

Let's talk

Start with a free second opinion: 30 minutes with our founder. No account access needed, and you keep a short written read.

Schedule a meeting: our calendar