Skip to content

Expert · Infrastructure as code · topic 36 of 50 · 30 min

AWS CloudFormation

Explanation

CloudFormation is the IaC service built into AWS. You write a template in YAML or JSON that lists resources, and AWS creates them together as a stack. Change sets show what will change before it happens, and if something fails, CloudFormation rolls the stack back to the last working state.

Templates and stacks

A template is a YAML or JSON file that describes AWS resources. YAML is easier to read, so most people use it. When you deploy a template, CloudFormation creates a stack: a named group of resources that are created, updated and deleted together. You can deploy the same template many times as different stacks, for example results-bucket-dev and results-bucket-prod.

Think of it like this: A template is the architect's drawing of a flat. A stack is an actual flat built from that drawing. The builder can build the same drawing in Pune and in Indore, and if the drawing changes, the builder works out which walls to move.

The sections of a template

SectionRequired?What it holds
AWSTemplateFormatVersionNoThe template format version. The only value is '2010-09-09'.
DescriptionNoA line of text that says what the template is for.
ParametersNoInputs you pass at deploy time, such as the environment name.
MappingsNoFixed lookup tables, for example an instance size per environment.
ConditionsNoTrue or false rules, for example "create this only in prod".
ResourcesYesThe AWS resources to create. Each has a logical name, a Type and Properties.
OutputsNoValues to show after deploy or share with other stacks, like a bucket name.

A full example: a versioned S3 bucket

yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Versioned S3 bucket for exam result PDFs

Parameters:
  EnvName:
    Type: String
    AllowedValues: [dev, prod]
    Default: dev
    Description: Which environment this stack is for

Resources:
  ResultsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub 'results-${EnvName}-${AWS::AccountId}'
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      Tags:
        - Key: env
          Value: !Ref EnvName

Outputs:
  BucketName:
    Description: Name of the results bucket
    Value: !Ref ResultsBucket
  BucketArn:
    Description: ARN of the results bucket
    Value: !GetAtt ResultsBucket.Arn
bucket.yaml: ResultsBucket is the logical name inside the template; the real bucket name comes from !Sub.

Intrinsic functions

Intrinsic functions are small built-in helpers that compute values when the stack is deployed. In YAML they start with an exclamation mark.

  • !Ref Name: the value of a parameter, or the main identifier of a resource. For an S3 bucket, !Ref gives the bucket name.
  • !GetAtt Resource.Attribute: another attribute of a resource, such as ResultsBucket.Arn. An ARN (Amazon Resource Name) is the full unique ID of a resource.
  • !Sub 'text ${Name}': puts parameter values, resource values or pseudo parameters (built-in values such as AWS::AccountId, AWS::Region and AWS::StackName) into a string.
  • !Join ['-', [a, b, c]]: joins a list of values with a separator, giving a-b-c.

Mappings and Conditions are used less often but are useful. A Mappings table can say "dev uses t3.micro, prod uses t3.large", read with !FindInMap. A condition such as IsProd: !Equals [!Ref EnvName, prod] can be attached to a resource so it is only created in production.

Deploying with the AWS CLI

bash
# Check the template for syntax errors
aws cloudformation validate-template --template-body file://bucket.yaml

# Create the stack, or update it if it exists (uses a change set behind the scenes)
aws cloudformation deploy \
  --template-file bucket.yaml \
  --stack-name results-bucket-dev \
  --parameter-overrides EnvName=dev

# Read the outputs
aws cloudformation describe-stacks --stack-name results-bucket-dev \
  --query "Stacks[0].Outputs"

# Only create the change set, so you can review it before running it
aws cloudformation deploy --template-file bucket.yaml \
  --stack-name results-bucket-prod --parameter-overrides EnvName=prod \
  --no-execute-changeset
If a template creates IAM roles, deploy also needs --capabilities CAPABILITY_IAM (or CAPABILITY_NAMED_IAM).

Change sets, rollback and drift detection

A change set is a preview: "these resources will be added, these modified, and this one replaced". Replacement matters. Some property changes, such as BucketName, cannot be made in place, so CloudFormation creates a new resource and deletes the old one. Always read the change set before running it in production.

Rollback: if any resource fails during create or update, CloudFormation undoes the changes and returns the stack to its last good state. Your stack is never left half-built without you knowing. Drift detection compares each resource with the template and reports properties changed outside CloudFormation (aws cloudformation detect-stack-drift).

Nested stacks and StackSets

A nested stack is a stack created from inside another template (resource type AWS::CloudFormation::Stack). It lets you reuse a standard piece, such as a VPC, in many templates. A StackSet deploys the same template to many AWS accounts and regions at once, for example a security baseline in every account of an organisation.

Careful: Common mistakes: (1) Wrong YAML indentation, which makes a property silently belong to the wrong parent. Fix: use two spaces consistently and run validate-template or cfn-lint. (2) Deleting a stack and losing data: deleting the stack deletes its resources. Fix: add DeletionPolicy: Retain to buckets and databases that hold data (and note CloudFormation cannot delete a bucket that still has objects). (3) Changing a property that forces replacement without reading the change set. Fix: always review the change set first. (4) Editing stack resources in the console. Fix: change the template and deploy again.
Tip: Install cfn-lint (an open-source linter from AWS) in your editor. It catches wrong property names and types before you deploy, which is much faster than waiting for a rollback.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

AWS CloudFormation · 1/8

AWS CloudFormation

CloudFormation is the infrastructure as code service built into AWS. You describe resources in a template, and AWS builds them.

Prefer a video by a teacher? Find free videos on “aws cloudformation tutorial for beginners template stack change set” on YouTube ↗

In real life: A state exam board publishes results with one template

A state school board publishes result PDFs from S3. Its cloud team keeps a CloudFormation template like the one in this lesson in Git: versioning on (so a wrongly overwritten PDF can be recovered), public access blocked (files are served through a CDN with access control), and a tag for the environment. The same template creates results-dev for testing and results-prod for the real results. Before result day, the team deploys a change with --no-execute-changeset, reads the change set in a short review call, and only then executes it.

bash
aws cloudformation deploy --template-file bucket.yaml \
  --stack-name results-bucket-prod \
  --parameter-overrides EnvName=prod --no-execute-changeset
Create the change set first; execute it after review.

Practice (0/3 done)

Task 1 · Question

In the bucket.yaml template of this lesson, what value does the output BucketName (Value: !Ref ResultsBucket) show after deploying with EnvName=dev?

Task 2 · Code · Python

Write validate(template) for a CloudFormation template that has been loaded into a Python dict. Return a list of error strings, sorted alphabetically: - If "Resources" is missing or empty, return ["Resources section is missing"] and nothing else. - For every resource without a "Type", add "<Name>: missing Type". - Look through everything inside Resources and Outputs for dicts like {"Ref": "X"}. If X is not a parameter name, not a resource name and does not start with "AWS::" (a pseudo parameter), add "Unknown Ref: X". A valid template returns [].

Task 3 · Practice

Extend bucket.yaml: add a Number parameter ExpiryDays (default 365), add a lifecycle rule to the bucket that deletes objects after that many days, and add an output with the stack's region. Then write the CLI command that creates a change set for the dev stack with ExpiryDays=30 without executing it.

Quick quiz

Question 1

1.Which section must every CloudFormation template have?

Question 2

2.Which intrinsic function gives you the ARN of a bucket called ResultsBucket?

Question 3

3.An update fails halfway because one resource could not be created. What does CloudFormation do by default?

Question 4

4.You want to see exactly what will change in production before anything happens. What do you use?

Question 5

5.Your company wants the same security baseline stack in 30 accounts and 3 regions. Which feature fits best?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which section must every CloudFormation template have?

Answer: Resources

Resources is the only required section. All the others are optional.

Which intrinsic function gives you the ARN of a bucket called ResultsBucket?

Answer: !GetAtt ResultsBucket.Arn

!GetAtt reads an attribute such as Arn. For a bucket, !Ref gives the name, not the ARN.

An update fails halfway because one resource could not be created. What does CloudFormation do by default?

Answer: Rolls the stack back to its last working state

Automatic rollback returns the stack to the last good state when a create or update fails.

You want to see exactly what will change in production before anything happens. What do you use?

Answer: A change set

A change set is a preview of the additions, modifications and replacements that an update will make.

Your company wants the same security baseline stack in 30 accounts and 3 regions. Which feature fits best?

Answer: StackSets

StackSets deploy one template to many accounts and regions from a single place.

In a plan, a resource is marked -/+. What will happen?Related

Answer: It will be destroyed and a new one created

-/+ means replace: Terraform destroys the old object and creates a new one. Check this carefully for databases.

From Terraform for the cloud

Your company has 200 resources that were created in the console. What is the usual way to bring them under Terraform?Related

Answer: Import them (import blocks or terraform import), then plan until it shows no changes

Importing adds existing resources to the state; a clean plan proves the code matches them.

From IaC in a team: reviews, pipelines and policy

You run the same IaC apply twice in a row with no changes to the files. What should happen the second time?Related

Answer: Nothing changes, because the apply is idempotent

Idempotent means running it again gives the same result, so the second run finds nothing to do.

From Why infrastructure as code

Which ARN means "every object in the bucket named photos"?Related

Answer: arn:aws:s3:::photos/*

Objects are bucket-name/key, so /* covers every object. S3 ARNs have no region or account.

From IAM policy JSON and how AWS decides

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Infrastructure as code” before its exam: Terraform for the cloud, IaC in a team: reviews, pipelines and policy.
  2. Module 9 exam: Infrastructure as code · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, High availability, scaling and disaster recovery. 50 topics left in the course before the final exam.

← Previous: Why infrastructure as code