.CloudFormation Best Practices

May 7th 2021-5 min read

🚨 - Update added Tagging 🚀

There are plenty of tools to provision AWS resources, I prefer to use CloudFormation for my daily work. Here are my recommendations that can help you to use CloudFormation more effectively and securely throughout its entire workflow.

Do not hardcode names in your templates

When using Continuous Integration and Continuous Deployment patterns for your service, you might want to spin up your CloudFormation Stack more than once in a region or account. Sooner or later you might end up with resources using the same name (eg. IAM Roles, etc.) since these resources are global.
❗️ Try to avoid using names in general and use tags or add a prefix to all your resources.

AWSTemplateFormatVersion: 2010-09-09
    Type: String
    Type: AWS::IAM::Role
      RoleName: !Sub ${Prefix}-role

Validate Templates Before Using Them

Validate your templates against the AWS CloudFormation Resource Specification and check your templates for insecure infrastructure - you can use the following tools to do that automatically.


cfn-lint is an open-source command-line tool that validates CloudFormation YAML/JSON templates against the AWS CloudFormation Resource Specification and additional checks. It includes checking valid values for resource properties and best practices.


cfn_nag is an open source command-line tool that performs static analysis of CloudFormation templates. It will search for insecure infrastructure like:

  • IAM rules that are too permissive (wildcards)
  • Security group rules that are too permissive (wildcards)
  • Access logs that aren't enabled
  • Encryption that isn't enabled
  • Password literals

⚠️ If you want to suspend certain findings you can do that by adjusting the metadata of the resource of the template eg:

AWSTemplateFormatVersion: 2010-09-09
    Type: 'AWS::IAM::Role'
          - id: F3
            reason: "Complies with own Coding Guidelines"
          - id: W11
            reason: "Complies with own Coding Guidelines"
          - id: W28
            reason: "Should never be replaced"

Automate CloudFormation testing with taskcat

taskcat is a tool that tests AWS CloudFormation templates. It deploys your CloudFormation template in multiple AWS Regions and generates a report with a pass/fail grade for each region.

Use AWS-Specific Parameter Types or use Allowed Patterns / Values

To avoid false statements and the following errors in deployments use AWS-Specific Parameter Types for existing resources (such as existing Virtual Private Cloud IDs or an EC2 key-pair name), for not existing resources define allowed patterns or allowed values for the parameter.

AWSTemplateFormatVersion: 2010-09-09
    Description: Frequency of the Backup
    ConstraintDescription: Must be a valid selection
    Type: String
    Default: Daily
      - Daily
      - Weekly
      - None
    Type: String
    Description: >-
      The account number.
    ConstraintDescription: Must be a valid Account number
    Default: '123456789101'
    MinLength: 12
    MaxLength: 12
    AllowedPattern: '^\d{12}$'

Split up Stacks

Group resources by their technical context, not the AWS service name that will help you keep the overview of your template(s) and it will help you to make changes to a particular set of resources by using their own process and schedule without affecting other resources.

Nested Stacks

Use one Master stack and trigger all other stacks as nested stacks. That way you can mix and match different templates but use nested stacks to create a single, unified stack.

Short-form Syntax

Try to use the short-form syntax for all intrinsic functions - there are just a few cases where it's not possible to use short-form syntax. It is way much easier to read the short-form syntax instead of reading the syntax for the full function name.

🚨 Wrong:
     - CostReporterLambda 
     - Arn 
✅ Right: 
Arn: !GetAtt CostReporterLambda.Arn

💡 If you use the short form and immediately include another function in the valueToEncode parameter, use the full function name for at least one of the functions eg:

        Fn::Base64: !Sub |

If you have more than two intrinsic functions successively you can do something like that (😁 yes I know that is not the normal case but someday it might be your case) eg:

      UserData: !Base64
        - UseWindows
        - Fn::Join:
          - ''
          - - |

For readability of code, it is better to use !GetAtt in short notation within !Sub.

    🚨 Wrong: !Join ['', ['arn:aws:s3:::', !GetAtt [Example, ParameterValue], /*]]
    ✅ Right: !Sub arn:aws:s3:::${Example.ParameterValue}/*

Always pay attention to the tab stops

If you need to deal with several interconnected Ifs pay attention to the tab stops, otherwise your template will not be invoked. In the following example we have four interconnected if conditions:

    - UseAZ1
    - - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetAID
    - !If
        - UseAZ2
        - - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetAID
          - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetBID
        - !If
        - UseAZ3
        - - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetAID
          - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetBID
          - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetCID
        - !If
            - UseAZ4
            - - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetAID
              - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetBID
              - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetCID
              - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnetDID
            - !Ref AWS::NoValue


You can use the Resource Tags property to apply tags to resources, which can help you identify and categorize those resources. 🚨 There are two different notations to tag your aws resources within the template. I think thats because multiple teams are working on providing automations for cloudformation. In the following you are see the two different notations how tags need to be assigned in your template.

Notation 1
- Need to be used for example in Ec2 or DynamoDB.
- This property type is an array of key-value pairs.

  - Key: YOURKEY
    Value: YOURVALUE

Notation 2 
- Need to be used for example in Signer or SecurityHub.
- This property type is processed as a map of key-value pairs.



Parameters vs. Parameter Store

If you need to use settings/parameters which you need more than once, I for example, always store them in the AWS Parameter Store and query them in my other templates via the AWS::SSM::Parameter::Value<String> Parametertype in other templates. For Parameters which I just need to use once, I just handover them via input.

globaldatanetCloud Development, Optimization & Automation



  • follow globaldatanet on instagram
  • follow globaldatanet on facebook
  • follow globaldatanet on twitter
  • follow globaldatanet on linkendin
  • follow globaldatanet on twitch
  •  listen to our serverless world podcast
  • follow globaldatanet's tech rss feed
  • follow globaldatanet at github
© 2024 by globaldatanet. All Right Reserved
Your privacy is important to us!

We use cookies on our website. Some of them are essential,while others help us to improve our online offer.
You can find more information in our Privacy policy