SynfraCore
Synfracore
Start Learning
Navigation

Academies

Platform

RoadmapsLabsCertificationsInterviewPYQsAI AssistantCareer
Start Learning Free🗺️ Learning Roadmaps

CloudFormationFundamentals

Core concepts and commands — hands-on from the start

✍️
Written by senior engineers. Reviewed for technical accuracy.· Updated 2025 · SynfraCore CloudFormation Team
Expert Content

AWS CloudFormation — Fundamentals

The Hook

Every resource in a CloudFormation template is declared, not scripted — you never write "first create the VPC, then create the subnet inside it." You just write both resource blocks, one referencing the other with !Ref, and CloudFormation works out the create order from those references (and the reverse order for deletion) on its own.

Analogy — Think of Resources: as a family tree, not a to-do list. You don't say "do step 1, then step 2." You say "this subnet's parent is this VPC" (VpcId: !Ref VPC), and CloudFormation walks the tree to figure out who has to exist before whom — the same way you'd know a grandparent has to exist before a grandchild can, without anyone telling you the order explicitly.

Template Structure

yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'My Application Stack'

Parameters:                      # User inputs at deploy time
  Environment:
    Type: String
    Default: staging
    AllowedValues: [dev, staging, prod]
  
  InstanceType:
    Type: String
    Default: t3.medium

Mappings:                        # Static lookup tables
  InstanceTypes:
    dev:     { Type: t3.micro }
    staging: { Type: t3.medium }
    prod:    { Type: m5.large }

Conditions:                      # Conditional resource creation
  IsProd: !Equals [!Ref Environment, prod]

Resources:                       # Required — what to create
  WebServer:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !FindInMap [InstanceTypes, !Ref Environment, Type]
      ImageId: !Sub '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}'
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-web-server'

  Database:
    Type: AWS::RDS::DBInstance
    Condition: IsProd              # Only create in prod
    DeletionPolicy: Snapshot       # Take snapshot before delete
    Properties:
      DBInstanceClass: db.r6g.large
      Engine: postgres
      MasterUsername: !Sub '{{resolve:secretsmanager:${Environment}/db:username}}'
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${Environment}/db:password}}'

Outputs:                          # Values to export
  WebServerIP:
    Value: !GetAtt WebServer.PublicIp
    Export:
      Name: !Sub '${AWS::StackName}-WebIP'

Common Intrinsic Functions

yaml
!Ref Resource           # Reference a resource's default value
!GetAtt Resource.Attr   # Get specific attribute
!Sub 'prefix-${Param}'  # String substitution
!Join ['-', [a, b, c]]  # Join array: a-b-c
!Select [0, [a,b,c]]    # Select by index: a
!If [Condition, yes, no] # Conditional value
!FindInMap [Map,Key1,Key2] # Lookup in Mappings
!ImportValue ExportName  # Cross-stack reference

# Condition functions
!Equals [!Ref Env, prod]
!And [!Condition C1, !Condition C2]
!Or  [!Condition C1, !Condition C2]
!Not [!Condition C1]

CloudFormation CLI + Cheatsheet

bash
# Deploy/update stack
aws cloudformation deploy \
    --stack-name my-app-prod \
    --template-file template.yaml \
    --parameter-overrides Environment=prod InstanceType=m5.large \
    --capabilities CAPABILITY_IAM \
    --region ap-south-1

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

# Describe stack and outputs
aws cloudformation describe-stacks --stack-name my-app-prod
# There is no separate "describe-stack-outputs" subcommand — outputs are a
# field on the stack description above; extract them with --query:
aws cloudformation describe-stacks --stack-name my-app-prod \
    --query 'Stacks[0].Outputs'

# List events (debug failures)
aws cloudformation describe-stack-events --stack-name my-app-prod \
    --query 'StackEvents[?ResourceStatus==`CREATE_FAILED`]'

# Delete stack (careful!)
aws cloudformation delete-stack --stack-name my-app-dev

# Change sets (preview changes before applying)
aws cloudformation create-change-set \
    --stack-name my-app-prod \
    --change-set-name my-changes \
    --template-body file://template.yaml
aws cloudformation describe-change-set --stack-name my-app-prod --change-set-name my-changes
aws cloudformation execute-change-set --stack-name my-app-prod --change-set-name my-changes

Try It (2 Minutes)

If you have AWS CLI access to a sandbox account, create a change set WITHOUT executing it — this previews what would happen with zero risk of actually changing anything:

bash
aws cloudformation create-change-set \
    --stack-name my-app-dev \
    --change-set-name preview-only \
    --template-body file://template.yaml \
    --parameters ParameterKey=Environment,ParameterValue=dev
aws cloudformation describe-change-set --stack-name my-app-dev --change-set-name preview-only
# Read the "Changes" list — each entry shows Action (Add/Modify/Remove) and
# whether it requires replacement (destroys and recreates the resource)

No AWS access handy? Reason through it instead: InstanceType in the template above is read via !FindInMap [InstanceTypes, !Ref Environment, Type] — it depends entirely on the Environment parameter, not on any hardcoded value. If you re-deploy with Environment=prod instead of dev, does the EC2 instance update in place, or does it have to be destroyed and recreated? (Hint: changing InstanceType on AWS::EC2::Instance is generally an in-place update, not a replacement — unlike changing something like a VPC's CIDR block.)

Share:
Join our Community
Daily tips, job alerts, interview help — join engineers learning together
Up Next
CloudFormationIntermediate
Real-world patterns, best practices, and deeper topics
Also Worth Exploring
← Back to all CloudFormation modules
InstallationIntermediate