[Webinar] Don't let your IaC platform become another system to maintain |

Sign up ➡️

AWS

What is an AWS CloudFormation Template? [Examples]

cloudformation templates

A CloudFormation template is a JSON or YAML file that describes the AWS resources you want and how they should be configured. Get the structure right and CloudFormation creates them in dependency order, rolls back cleanly when something fails, and gives you a definition your team can review in a pull request. Get it wrong and you find out partway through a stack creation that you misspelled a property name.

In this article, we will explore what AWS CloudFormation template is, what each of the ten sections does, a complete example you can deploy, and the practices that keep templates maintainable as your stack count grows.

What we will cover:

  1. What is AWS CloudFormation?
  2. What are AWS CloudFormation templates?
  3. Key CloudFormation parameters
  4. Benefits of using CloudFormation templates
  5. AWS CloudFormation template example
  6. CloudFormation templates best practices

What is AWS CloudFormation?

aws cloudformation template

CloudFormation is an IaC AWS-native service that helps you model and configure your resources declaratively. Using CloudFormation, you can manage and operate your AWS infrastructure efficiently, so you can spend less time managing infrastructure. 

AWS CloudFormation handles provisioning, configuration, and automatic rollbacks. And it has features that help you manage resources at scale, such as templates and stacks

Learn more: What is AWS CloudFormation? Key Concepts & Tutorial

What is an AWS CloudFormation template?

Templates are a core component of CloudFormation used to define the desired state of your infrastructure declaratively. They are text files written in JSON or YAML format that describe a set of AWS resources and their configurations, serving as blueprints for creating and managing AWS resources. 

Templates should be version-controlled in a repository and go through CI/CD pipeline flows for infrastructure changes. 

What is the difference between the AWS CloudFormation template and the stack?

 

diagram showing what is the difference between cloudformation stack and template

With CloudFormation, you can create templates that describe all the AWS resources needed for your environments, enabling you to create, update, and delete them in a controlled and predictable manner. Templates can be parameterized, allowing you to reuse the same template across different environments (dev, staging, production) by changing input parameters.

A stack is a collection of AWS resources that can be operated as a single unit. CloudFormation automatically manages the order of resource creation, updates, and deletion based on dependencies defined in your template. This ensures that resources are created in the correct order and prevents conflicts. 

Before applying changes to a stack, CloudFormation allows you to preview the changes using Change Sets. This feature helps you understand the impact of your changes before actually implementing them.

If errors occur during stack creation or update, CloudFormation can automatically roll back to the last known stable state, helping maintain the integrity of your infrastructure.

To recap, the CloudFormation template is a JSON or YAML file that defines AWS resources and configurations. It’s the blueprint for the infrastructure. A stack is the actual set of AWS resources created and managed based on the template.

 

When you are instantiating a template, you basically create a stack. The template defines what resources should exist and how they should be configured, whereas the stack represents the actual implementation of those resources in your AWS account.

What are the sections of a CloudFormation template?

CloudFormation templates are composed of one or more sections, including:

  • Resources (the only required section)
  • Parameters
  • Mappings
  • Outputs
  • Metadata
  • Conditions
  • Transform
  • Rules
  • Description
  • Format version

1. Resources

The resources section is always required in every CloudFormation template you define. It includes the stack resources and their configuration properties and is considered the main component of the template. Each resource is defined with a unique logical name (or ID), type, and specific configuration details.

For example:

Resources:
  LogicalResourceName:
    Type: AWS::ProductIdentifier::ResourceType
    Properties:
      PropertyName: PropertyValue

Each resource must have a Type attribute to identify it. 

For example, an Amazon S3 bucket has AWS::S3::Bucket as its resource type. You can use the Properties attribute to define additional configuration options for each specific resource type. 

For details on the properties supported for each resource type, see the topics in AWS resource and property types reference. Property values can be literal strings, lists of strings, Booleans, dynamic references, parameter references, pseudo references, or the value returned by a function. 

Here’s an S3 bucket with a few properties set, plus two resource attributes that control what happens to the bucket when the stack changes:

Resources:
  MyS3Bucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: RetainExceptOnCreate
    UpdateReplacePolicy: Retain
    Properties:
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256

Sometimes, you might need to reference or define a resource based on previously defined resources. CloudFormation provides several built-in functions that let you create dependencies between them and pass values from one to another. Two of the most common are Ref and Fn::GetAtt.

The Ref function is commonly used to retrieve an identifying property of resources defined within the same CloudFormation template. 

Here’s an example of referencing a Security Group for our EC2 instance configuration.

Resources:
  Ec2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      SecurityGroupIds:
        - !Ref InstanceSecurityGroup
      KeyName: MyKey
  Parameters:
  SshCidr:
    Description: CIDR range allowed to reach port 22
    Type: String
    Default: 10.0.0.0/8
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
    ConstraintDescription: Must be a valid CIDR range, for example 10.0.0.0/8

Resources:
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH from an approved CIDR range
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SshCidr

To get a specific attribute of a resource, use the Fn::GetAtt function, written as !GetAtt in YAML shorthand.

For a detailed reference of all the available functions, see the topics in the Intrinsic function reference

Here’s an example of using this function to get the S3 bucket’s domain name attribute:

Resources:
  MyS3Bucket:
    Type: AWS::S3::Bucket

Outputs:
  BucketDomainName:
    Description: IPv4 DNS name of the bucket
    Value: !GetAtt MyS3Bucket.DomainName
  BucketRegionalDomainName:
    Description: Regional domain name, required for CloudFront origins
    Value: !GetAtt MyS3Bucket.RegionalDomainName

2. Parameters

Parameters in AWS CloudFormation templates allow you to customize your templates by providing a mechanism to input custom values. Effectively using parameters is the main method of making your templates reusable across different use cases, scenarios, and environments.

The only required attribute of a parameter is Type. CloudFormation supports String, Number, List<Number>, and CommaDelimitedList, plus two families of CloudFormation-supplied types:

  • AWS-specific types such as AWS::EC2::VPC::Id or AWS::EC2::KeyPair::KeyName. CloudFormation validates the value against resources that exist in the account, and the console renders a dropdown instead of a text box.
  • SSM parameter types such as AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>. You pass a Parameter Store key, and CloudFormation fetches the current value when the stack is created or updated.

The full template later in this article uses an SSM parameter type to resolve the latest Amazon Linux 2023 AMI without hardcoding an AMI ID.

As a best practice, you can add a Description attribute and a default value where applicable. For a full list, check out Parameter Properties.

Here’s an example of defining a parameter for an EC2 instance type:

Parameters:
  InstanceType:
    Description: The instance type of the EC2 instance. Allowed values are t3.micro, t3.small, t3.medium, or t3.large. Default is t3.micro.
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
      - t3.small
      - t3.medium
      - t3.large

Another useful type of parameter is pseudo parameters. Pseudo parameters are predefined by AWS CloudFormation for you and are available to use in your templates without declaring them. Common examples of pseudo parameters are:

  • AWS::AccountId
  • AWS::Region
  • AWS::StackName

3. Mappings

CloudFormation template mappings can help create key-value pairs based on dependencies or conditions. A common use case for mappings is to define a different set of values depending on the AWS region where the stack is deployed. 

Here’s an example of defining a different AMI for EC2 instances based on the region, as AMIs are region-specific resources:

Mappings:
  RegionMap:
    us-east-2:
      AmiId: ami-123abc
    us-west-2:
      AmiId: ami-456def
    eu-west-2:
      AmiId: ami-789ghi

To retrieve values in a map, you can use the FindInMap intrinsic function within the Resources section of your template. Here’s an example of fetching the respective value from our RegionMap based on the actual value of AWS::Region pseudo parameter.

Resources: 
  myEC2: 
    Type: "AWS::EC2::Instance"
    Properties: 
      ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AmiId]
      InstanceType: t3.small

4. Outputs

With the Outputs section of templates, you can declare output values for your stack. These values can be passed to other stacks to cross-reference created resources or capture information you can pass to other systems. 

Here’s an example of declaring a few outputs:

Outputs:
  InstanceID:
    Description: The EC2 Instance ID
    Value: !Ref MyEC2Instance
  VPCID:
    Description: The VPC ID
    Value: !Ref MyVPC

Referencing outputs from another stack

Fn::ImportValue reads a value that another stack explicitly exported, and it only works inside a single account and Region. Fn::GetStackOutput, added in May 2026, reads an output from another stack without that stack declaring an Export, and it works across accounts and Regions in the same partition.

Resources:
  AppInstance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.micro
      SubnetId:
        Fn::GetStackOutput:
          StackName: NetworkStack
          OutputName: PublicSubnetId

For a stack in another account, add a RoleArn pointing at a role with cloudformation:DescribeStacks on the referenced stack, assumable by the consuming stack’s execution role. Add Region for a cross-Region reference.

      SubnetId:
        Fn::GetStackOutput:
          StackName: NetworkStack
          OutputName: PublicSubnetId
          RoleArn: arn:aws:iam::111111111111:role/GetStackOutputRole
          Region: us-west-2

Two constraints worth knowing before you reach for it. First, this is a weak reference: the value resolves at create or update time, and if the referenced stack or output later changes or is deleted, the consuming stack is not updated or notified.

Fn::ImportValue gives you referential integrity and blocks deletion of the exporting stack; Fn::GetStackOutput does not. Second, Fn::GetStackOutput cannot be used as a direct value in the Outputs section. Use it as a resource property value, or wrap it in Fn::Join, Fn::If, or Fn::Select.

5. Metadata

You can leverage the Metadata section to include further details about your template in JSON or YAML. For example, you can add further descriptions or implementation details about specific components of your templates.

Metadata:
  Networking:
    Description: "Information about the system’s networking"

6. Conditions

With CloudFormation templates Conditions, you can define statements to control entity creation and configuration based on something. For example, with Conditions, you can specify a resource creation only if a condition results in true. Similarly, you can define a resource conditionally properly. 

A use case for conditions is to reuse templates with different contexts, such as test, staging, dev, and production environments. Based on an input parameter that defines the environment type, you can create the resources and respective configurations for the specific environment.

Before creating or updating any resources, conditions are evaluated during stack creation or update. 

Here’s an example based on the environment type:

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  EnvironmentType:
    Description: Environment type.
    Default: test
    Type: String
    AllowedValues:
      - prod
      - preprod
      - staging
      - dev
      - test
    ConstraintDescription: must specify prod, preprod, staging, dev, or test.
Conditions:
  CreateProdResources: !Equals 
    - !Ref EnvironmentType
    - prod
Resources:
  EC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: ami-08d8ac128e0a1b91c
      InstanceType: !If [ CreateProdResources, c5.xlarge, t3.small]

7. Transform

The optional Transform section names one or more macros that CloudFormation runs against your template before provisioning. Macros range from simple find-and-replace to rewriting the whole template. AWS::Serverless is the transform behind AWS SAM, and AWS::Include pulls in template snippets stored separately.

The one most template authors want is AWS::LanguageExtensions, which unlocks Fn::ForEach and lets you use intrinsic functions in positions where they are normally rejected, such as inside Ref and Fn::GetAtt. Fn::ForEach removes the copy-paste block that makes long templates hard to review:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::LanguageExtensions

Resources:
  'Fn::ForEach::LogGroups':
    - Environment
    - [dev, staging, prod]
    - 'LogGroup${Environment}':
        Type: AWS::Logs::LogGroup
        Properties:
          LogGroupName: !Sub '/aws/app/${Environment}'
          RetentionInDays: 30

That produces three log groups with logical IDs LogGroupdev, LogGroupstaging, and LogGroupprod. The collection can also be a Ref to a CommaDelimitedList parameter, so the list becomes a deployment-time input.

Three limits to know. Fn::ForEach works in the Resources, Conditions, and Outputs sections and inside resource properties, and nowhere else. It does not raise CloudFormation’s quotas, which apply to the expanded template rather than the source. And the AWS SAM CLI does not support it, so if you are writing SAM templates, check before you commit.

8. Rules

Rules allow you to validate a combination of parameters during stack creation or update before actually creating or updating resources.

Let’s check an example that validates the value of the InstanceType parameter for an EC2 instance depending on the environment type:

Rules:
  stagingInstanceType:
    RuleCondition: !Equals 
      - !Ref EnvironmentType
      - staging
    Assertions:
      - Assert:
          'Fn::Contains':
            - - t3.small
            - !Ref InstanceType
        AssertDescription: 'For a staging environment, the instance type must be t3.small'
  productionInstanceType:
    RuleCondition: !Equals 
      - !Ref EnvironmentType
      - prod
    Assertions:
      - Assert:
          'Fn::Contains':
            - - c5.xlarge
            - !Ref InstanceType
        AssertDescription: 'For a production environment, the instance type must be c5.xlarge'

This example defines two rules that check the environment type. Staging allows only t3.small, and prod allows only c5.xlarge.

If we try to deploy a template that breaks these rules, we will get an error:

 

cloudformation template parameters

9. Description

The Description section of the CloudFormation templates allows for a textual description of the template and its resources.

10. Format Version

The optional AWSTemplateFormatVersion section identifies the current template format version. The latest template format version is 2010-09-09.

Benefits of using CloudFormation templates

Leveraging CloudFormation templates to provision and manage AWS infrastructure can offer numerous advantages. Here are some benefits of CloudFormation adoption and template usage:

Version control and auditability

By defining your infrastructure as CloudFormation templates, you can store their definitions in Git, allowing you to track changes and collaborate effectively with other team members. All changes to environments pass through Git, maintaining a complete history.

Automated deployments

CloudFormation enables automated infrastructure deployments, offering a consistent and repeatable approach to change management. This simplifies tasks like performing upgrades, rolling back changes, and auditing modifications. By automating these processes, your team can work more efficiently, rapidly provisioning resources using best practices and reusable templates for common patterns.

Security, compliance, and disaster recovery

By adopting CloudFormation, you can enforce security best practices and compliance requirements baked into your reusable templates. This ensures that deployments, operations, and configurations comply with organizations’ policies and standards, reducing the risk of misconfigurations and vulnerabilities. 

Furthermore, because the desired state of all our environments is defined in CloudFormation templates, we can recreate our environments from scratch, making disaster recovery scenarios much more manageable.

Deep integration with other AWS services

CloudFormation integrates with different AWS services, such as AWS Organizations, which allows you to manage complex multi-account and multi-region deployments.

AWS CloudFormation template example

Now that we understand the basics and benefits of CloudFormation templates let’s examine an end-to-end example of creating, validating, and deploying one.

How to create a CloudFormation template

To create a CloudFormation template, use the sections explained earlier and write a YAML manifest with all the resources you want to create. 

Here’s a complete example that creates a VPC, a public subnet, a security group, and an EC2 instance:

example_template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS CloudFormation template to create a VPC, EC2 instance, and Security Group'

Parameters:
  EnvironmentName:
    Description: An environment name that is prefixed to resource names
    Type: String
    Default: Dev

  VpcCIDR:
    Description: Please enter the IP range (CIDR notation) for this VPC
    Type: String
    Default: 10.0.0.0/16

  PublicSubnetCIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet
    Type: String
    Default: 10.0.1.0/24

  InstanceType:
    Description: EC2 instance type
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
      - t3.small
      - t3.medium
    ConstraintDescription: Must be one of the allowed t3 instance types.

  SshCidr:
    Description: CIDR range allowed to reach port 22
    Type: String
    Default: 10.0.0.0/8
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
    ConstraintDescription: Must be a valid CIDR range, for example 10.0.0.0/8

  LatestAmiId:
    Description: SSM public parameter resolving to the latest Amazon Linux 2023 AMI
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64'

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} VPC

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} IGW

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [ 0, !GetAZs '' ]
      CidrBlock: !Ref PublicSubnetCIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Public Subnet

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Public Routes

  DefaultPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet

EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: "Allow HTTP and SSH"
      GroupDescription: "Allow HTTP from anywhere and SSH from an approved CIDR range"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SshCidr
      VpcId: !Ref VPC

  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref InstanceType
      SecurityGroupIds:
        - !Ref EC2SecurityGroup
      SubnetId: !Ref PublicSubnet
      ImageId: !Ref LatestAmiId
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} EC2 Instance

Outputs:
  VPC:
    Description: A reference to the created VPC
    Value: !Ref VPC
    Export:
      Name: !Sub ${EnvironmentName}-VPCID

  PublicSubnet:
    Description: A reference to the public subnet
    Value: !Ref PublicSubnet
    Export:
      Name: !Sub ${EnvironmentName}-PUBLIC-SUBNET

  EC2InstancePublicDNS:
    Description: Public DNS of EC2 instance
    Value: !GetAtt EC2Instance.PublicDnsName

The LatestAmiId parameter uses an SSM public parameter rather than a hardcoded AMI ID, so the same template resolves the current Amazon Linux 2023 image in any Region. Amazon Linux 2 reached end of life on June 30, 2026, so amzn2-* parameters no longer resolve to a supported image.

The security group has no SecurityGroupEgress block. When you create a security group without egress rules, EC2 adds rules allowing all outbound IPv4 and IPv6 traffic. As soon as you declare a single egress rule, that default is not added, so an IPv4-only rule quietly drops outbound IPv6.

Let’s break down the template and the different sections we used:

  1. As a first step, we defined the Template Version and Description.
  2. Next, we define different parameters such as EnvironmentName and InstanceType, among others.
  3. Right after, we define our actual resources along with their configuration, including VPC, EC2, Public Subnet, Internet Gateway, and Security Group.
  4. Finally, we define a few template outputs to be exported and possibly used or references in other stacks or for easy access.

This template creates basic networking components such as a VPC with a public subnet and an EC2 instance accessible via SSH and HTTP.

Apart from manually authoring CloudFormation templates, you can also use several tools to fast-track your development:

 

  • Use AWS Infrastructure Composer, a visual canvas for designing templates. You drag, drop, configure, and connect resources as cards, and Infrastructure Composer produces the template. In CloudFormation console mode it is the tool AWS recommends for working with templates visually, and it replaces the older CloudFormation Designer.
  • For existing resources created manually that CloudFormation doesn’t manage, you can use the IaC Generator tool to generate the templates and bring entire applications under CloudFormation management.
  • Another popular way to define infrastructure as code is using the AWS Cloud Development Kit (CDK) and programming languages such as Python or Java. CDK synthesizes CloudFormation templates from your code, and many users prefer this approach due to the advanced code reuse and abstractions it offers. 

How to validate a CloudFormation template

CloudFormation now validates before it provisions. Pre-deployment validation runs automatically on Create Stack, Update Stack, and Create Change Set operations, so a misspelled property name fails in seconds instead of ten minutes into a rollback. You do not enable it and you do not configure it.

Six validation types run. Two run on all three operations in FAIL mode, which stops the operation before any resource is provisioned:

  • Property syntax validation against the resource schemas
  • Resource name conflict detection

Four more run during change set creation only, in WARN mode, so the change set is still created and you decide what to do:

  • S3 bucket emptiness, when a bucket is targeted for deletion
  • Service quota checks
  • Config recorder conflict detection
  • ECR repository delete readiness

That difference matters in practice. If you want the quota and recorder warnings, you have to go through a change set. A direct update-stack will not surface them.

To read the results, use describe-events with the operation ID. Each error carries the logical resource ID and the property path:

aws cloudformation describe-events --stack-name MyStack

In the console, open the stack’s Events tab and choose the operation ID to land on the Deployment validations tab.

You can skip validation for a single operation with --disable-validation, which is worth doing only when you have already validated another way or a known false positive is blocking you:

aws cloudformation create-stack \
  --stack-name MyStack \
  --template-body file://template.yaml \
  --disable-validation

Pre-deployment validation is not a substitute for linting. It checks your template against resource schemas and your account, not against your own conventions. Keep cfn-lint in CI for that, and run it locally as a pre-commit hook so problems surface before the push. rain fmt handles formatting, and the CloudFormation IDE extensions give you schema validation while you write.

A caveat: some resource types are excluded from pre-deployment validation, including AWS::EC2::SecurityGroup, AWS::IAM::Role, and AWS::CloudFormation::Stack. The full list is in the AWS documentation.

How to deploy a CloudFormation template

There are several ways to deploy CloudFormation templates for different scenarios and preferences. The most straightforward way is to use the AWS Management Console, navigate to the CloudFormation service, and create a stack to deploy the resources based on a template.

A standard method to deploy CloudFormation templates is to integrate deployments into CI/CD pipelines. For example, you can define AWS CodePipeline flows to automate deployments based on source control changes.

Another way is to use the AWS CLI either manually, via scripts, or in an automated flow (e.g., as part of a CI/CD pipeline). For such cases, check out the aws cloudformation create-stack command.

Let’s take the template example we created above and deploy it via the AWS Management Console and the CloudFormation Service. 

Select the `Create stack` option:

aws cloudformation template example

On the next screen, select `Choose an existing template` and `Upload a template file`. Then, select `Choose file` to upload the YAML file we created previously and hit `Next.`

cloudformation template reference

Enter a stack name and optionally modify any of the input parameters. You should already see the default values we configured in the template. Click `Next` to continue.

sample cloudformation template

On the next page, you can add Tags, use a specific IAM role while deploying, and define stack failure options. Possible options include rolling back stack resources or preserving successfully provisioned resources. Other options you can configure include adding a stack policy, rollback configuration, and notification options. 

For this example, we will use the defaults. Click `Next` to continue. 

On the `Review and create` page, you can review your configuration options, parameter values, and other options. To proceed with deployment, click `Submit` at the bottom of the page.

cloudformation template options

Your stack moves in `CREATE_IN_PROGRESS` state:

cloudformation template state

On the `Events` tab, you can see the actual events of the deployment as they happen:

aws cloudformation stack events

After the successful deployment of our stack, select the `Resources` tab to get an overview of all the created resources:

aws cloudformation stack resources

Finally, check out the `Outputs` tab to check all the exported values from the newly created resources:

aws cloudformation stack output

CloudFormation templates best practices

Here are some best practices for creating and using CloudFormation templates.

  1. Store your CloudFormation templates in Git repositories or S3.
  2. Use YAML for better readability. While CloudFormation supports JSON, YAML is typically a more human-readable format that supports comments and is less error-prone to syntax errors.
  3. Make your templates reusable and dynamic using parameters, intrinsic functions, and conditionals.
  4. Implement an elaborate naming strategy. Use logical names for your resources that clearly state their purpose.
  5. Because your resources will depend on each other, leverage the DependsOn attribute to ensure resources are created in the correct order.
  6. For complex architectures and environments, explore advanced features and functionalities such as nested stacks and StackSets.
  7. Use linting and validation tools such as cfn-lint.
  8. When a stack outgrows its shape, use stack refactoring rather than the old retain-remove-import sequence. Stack refactoring moves resources between stacks, splits a monolithic stack, and renames resource logical IDs while preserving resource properties and data. You supply the templates describing the structure you want, review a generated preview of the actions, then execute.

Can I use CloudFormation with Spacelift?

Spacelift is an infrastructure orchestration platform that increases your infrastructure deployment speed without sacrificing control. With Spacelift, you can provision, configure, and govern with one or more automated workflows that orchestrate Terraform, OpenTofu, Terragrunt, Pulumi, CloudFormation, Ansible, and Kubernetes. 

Spacelift also detects drift on CloudFormation stacks using AWS’s native DetectStackDrift API. Worth being precise about the boundary: CloudFormation stacks are excluded from Spacelift’s reconciliation workflows, because CloudFormation detects drift without offering built-in reconciliation. So Spacelift tells you when something changed outside CloudFormation, and you fix it through your normal deployment path rather than having it reconciled automatically.

If you are not writing YAML or JSON by hand, Spacelift supports AWS CDK, AWS SAM, and the Serverless Framework on CloudFormation stacks.

You don’t need to define all the prerequisite steps for installing and configuring the infrastructure tool you are using, nor the deployment and security steps, as they are all available in the default workflow.

Spacelift can be configured with AWS CloudFormation as a backend. See the image below:

spacelift cloudformation backend

When you select the CloudFormation backend, you need to provide a little information — the AWS Region where the stack should be created, the stack name, the template file name, and the S3 bucket where the template will be uploaded and then executed.

All runs of the CloudFormation stack are completed with change sets.

Let’s take a look at the successful execution log in Spacelift:

deploy cloudformation spacelift

You can see the Unconfirmed status on this image. We used this manual step here to show the moment when some review of the change set can be made.

If you want to learn more about what you can do with Spacelift, check out this article.

Key points

This blog post delved into CloudFormation templates for defining and configuring IaC manifests. We looked into the benefits of using templates to create and manage multiple AWS resources and provided a detailed explanation of their key sections. 

Next, we reviewed a complete example of authoring, validating, and deploying a template and discussed alternatives. Finally, we listed several best practices to consider when authoring your templates.

Would you like to improve IaC management in your organization? Book a demo with our engineering team to discuss your options in more detail. 

Solve your infrastructure challenges

Spacelift is a flexible orchestration solution for IaC development. It delivers enhanced collaboration, automation, and controls to simplify and accelerate the provisioning of cloud-based infrastructures.

Learn more

Frequently asked questions

  • What best practices secure AWS CloudFormation templates at scale?

    To secure AWS CloudFormation templates at scale, use parameter constraints, IAM scoping, and automated validation across environments. Security should be embedded both in template design and in the deployment pipeline.

  • How do AWS CloudFormation templates handle parameters and conditions?

    AWS CloudFormation templates handle parameters by allowing users to input custom values at stack creation time, and use conditions to control resource creation based on those values or other logical rules.

  • Should you write CloudFormation templates in YAML or JSON?

    Use YAML unless something in your toolchain requires JSON. CloudFormation supports both formats completely, and YAML templates follow the same anatomy and support the same features as JSON ones, but YAML lets you comment your templates, gives you the shorthand function syntax (!Ref, !GetAtt, !Sub), and produces a smaller file, which matters against the 51,200-byte ceiling on a template body passed directly to the API. Choose JSON when a tool generates or consumes your templates programmatically, because almost every language parses JSON without an extra dependency.

The Practitioner’s Guide to Scaling Infrastructure as Code

Transform your IaC management to scale

securely, efficiently, and productively

into the future.

ebook global banner
Share your data and download the guide