Skip to content

Latest commit

 

History

History
342 lines (262 loc) · 9.93 KB

File metadata and controls

342 lines (262 loc) · 9.93 KB

CloudFormation — Infrastructure as Code

What Is It?

CloudFormation lets you define AWS infrastructure in YAML or JSON templates. You describe what you want; CloudFormation figures out how to create it in the right order, handles dependencies, and rolls back on failure.

Real-World: Your startup needs to spin up identical environments for dev, staging, and production. With CloudFormation, one template creates all three — VPC, subnets, RDS, ElastiCache, EC2 ASG, ALB, Lambda. Run the same template 3 times with different parameter values.


Template Structure

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

Parameters:          # Input values at deploy time
  Environment:
    Type: String
    AllowedValues: [dev, staging, prod]
    Default: dev
  InstanceType:
    Type: String
    Default: t3.micro

Mappings:            # Static lookup tables
  EnvConfig:
    dev:
      MinSize: 1
      MaxSize: 2
    prod:
      MinSize: 3
      MaxSize: 10

Conditions:          # Boolean expressions
  IsProd: !Equals [!Ref Environment, prod]

Resources:           # REQUIRED - the actual AWS resources
  MyBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub 'my-app-${Environment}-${AWS::AccountId}'
      VersioningConfiguration:
        Status: !If [IsProd, Enabled, Suspended]

  MyDB:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceClass: !If [IsProd, db.t3.large, db.t3.micro]
      MasterUsername: admin
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${Environment}/db-password}}'

Outputs:             # Values to export or display
  BucketName:
    Value: !Ref MyBucket
    Export:
      Name: !Sub '${AWS::StackName}-BucketName'
  DBURL:
    Value: !GetAtt MyDB.Endpoint.Address

Intrinsic Functions

Function Purpose Example
!Ref Reference parameter or resource !Ref MyBucket
!GetAtt Get attribute of resource !GetAtt MyLB.DNSName
!Sub String substitution !Sub 'bucket-${AWS::AccountId}'
!If Conditional value !If [IsProd, big, small]
!ImportValue Import output from another stack !ImportValue VPC-SubnetId
!Join Join values !Join [',', [a, b, c]]
!Select Select from list !Select [0, !GetAZs '']
!FindInMap Lookup in Mappings !FindInMap [EnvConfig, !Ref Env, MinSize]
!Cidr Generate CIDR blocks Used in VPC subnet design
!Base64 Encode to Base64 EC2 UserData

Pseudo Parameters (Built-in)

AWS::AccountId     # 123456789012
AWS::Region        # us-east-1
AWS::StackName     # my-app-stack
AWS::StackId       # arn:aws:cloudformation:...
AWS::NoValue       # Remove property (used with Conditions)
AWS::URLSuffix     # amazonaws.com (or cn for China)

Change Sets — Preview Before Apply

Developer updates template → creates Change Set → reviews proposed changes
                           → executes Change Set → stack updated

Never apply directly to production without reviewing the Change Set. A Change Set shows:

  • Resources being added/modified/deleted
  • Resources that will be replaced (risky!)
  • No changes until you execute
# Create change set
aws cloudformation create-change-set \
  --stack-name my-stack \
  --template-body file://template.yaml \
  --change-set-name my-update

# Review
aws cloudformation describe-change-set --change-set-name my-update

# Apply
aws cloudformation execute-change-set --change-set-name my-update

Stack Policies

Prevent accidental update/deletion of critical resources:

{
  "Statement": [
    {
      "Effect": "Deny",
      "Action": "Update:Replace",
      "Principal": "*",
      "Resource": "LogicalResourceId/ProductionDatabase"
    },
    {
      "Effect": "Allow",
      "Action": "Update:*",
      "Principal": "*",
      "Resource": "*"
    }
  ]
}

This prevents the RDS instance from being replaced during stack updates.


Rollback Behavior

Default: If any resource fails → rollback ALL resources.

Rollback options:

  • ROLLBACK_ALL_FAILURES: rollback on any failure (default)
  • DO_NOTHING: keep partially created resources (useful for debugging)
  • Specific resources can have DeletionPolicy

DeletionPolicy

MyDB:
  Type: AWS::RDS::DBInstance
  DeletionPolicy: Retain    # Keep RDS even if stack deleted
  # Options: Delete (default), Retain, Snapshot

MyBucket:
  Type: AWS::S3::Bucket
  DeletionPolicy: Snapshot  # Take snapshot before delete (not valid for S3)

Critical: S3 buckets with contents will fail to delete. Use DeletionPolicy: Retain or custom resource to empty bucket first.


CloudFormation Custom Resources

When you need to do something CloudFormation doesn't natively support:

Stack create/update/delete → SNS/Lambda → your custom code → return response to CloudFormation

Use cases:

  • Populate DynamoDB table with initial data
  • Create Route 53 records in external DNS
  • Rotate secrets during deployment
  • Empty S3 bucket before deletion
EmptyBucketResource:
  Type: Custom::EmptyBucket
  Properties:
    ServiceToken: !GetAtt EmptyBucketFunction.Arn
    BucketName: !Ref MyBucket

Lambda receives RequestType: Create/Update/Delete and must call CloudFormation callback URL.


Nested Stacks

Break large templates into smaller, reusable ones:

RootStack
├── NetworkStack (VPC, subnets, IGW)
├── SecurityStack (Security groups, NACLs)
├── DatabaseStack (RDS, parameter group)
└── AppStack (EC2 ASG, ALB)
NetworkStack:
  Type: AWS::CloudFormation::Stack
  Properties:
    TemplateURL: https://s3.amazonaws.com/templates/network.yaml
    Parameters:
      VpcCidr: 10.0.0.0/16

Cross-stack references: Parent stack passes outputs to child stacks via Parameters. Child stacks export outputs that parent can reference.


Stack Sets — Multi-Account/Multi-Region

Deploy same template to multiple AWS accounts and regions:

Management Account
└── StackSet: security-baseline
    ├── Deploy to: Account A (us-east-1, eu-west-1)
    ├── Deploy to: Account B (us-east-1)
    └── Deploy to: Account C (ap-southeast-1, us-east-1)

Use cases:

  • Deploy security baseline (CloudTrail, Config, GuardDuty) to all accounts
  • Standardize networking across org

CloudFormation Drift Detection

Detect if someone manually changed a resource outside CloudFormation:

Template says: S3 bucket versioning = Enabled
Someone went to console and disabled versioning
Drift detection: "MODIFIED - BucketVersioningConfiguration"

Fix: Update template OR use aws cloudformation detect-stack-resource-drift + remediate.


cfn-init and EC2 UserData

For configuring EC2 instances:

MyEC2:
  Type: AWS::EC2::Instance
  Metadata:
    AWS::CloudFormation::Init:
      config:
        packages:
          yum:
            httpd: []
        files:
          /var/www/html/index.html:
            content: '<h1>Hello from CloudFormation</h1>'
        services:
          sysvinit:
            httpd:
              enabled: true
              ensureRunning: true
  Properties:
    UserData: !Base64
      !Sub |
        #!/bin/bash
        /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyEC2 --region ${AWS::Region}
        /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource MyEC2 --region ${AWS::Region}

cfn-signal tells CloudFormation the instance is ready (required for WaitCondition).


Good Practices

Practice Reason
Review Change Sets before applying Know exactly what's changing
Use DeletionPolicy: Retain on critical resources Prevent accidental data loss
Use nested stacks for large templates Modularity, reusability
Use Stack Policies Protect critical resources from updates
Parameterize environment-specific values Same template for dev/prod
Use !Sub over !Join for readability Cleaner string substitution
Store templates in S3 with versioning Rollback to previous template

Bad Practices

Anti-Pattern Impact Fix
Hardcoding account IDs/region Template not portable Use AWS::AccountId, AWS::Region
No DeletionPolicy on RDS/S3 Data loss on stack delete Add DeletionPolicy: Retain or Snapshot
Directly editing resources in console Drift, CloudFormation loses sync Always change through CloudFormation
Putting secrets in template Parameters Secrets visible in console/logs Use NoEcho: true + reference Secrets Manager
One huge 5000-line template Hard to maintain, update errors cascade Use nested stacks

Exam Tips

  1. !Ref on a resource returns the resource ID/name. !Ref on a parameter returns the value.
  2. !GetAtt gets a specific attribute (like the endpoint, ARN, etc.)
  3. Change Sets don't apply until you execute them.
  4. DeletionPolicy options: Delete (default), Retain, Snapshot (only DB/EFS/ElastiCache).
  5. cfn-signal is needed when using CreationPolicy on EC2 — tells CloudFormation instance bootstrapped successfully.
  6. Stack Sets require: either service-managed (AWS Organizations) or self-managed (create execution roles in each account).
  7. CloudFormation limits: 200 stacks per account per region (default), 500 resources per template.

Common Exam Scenarios

Q: Stack update failing because RDS is being replaced — prevent it? → Use Stack Policy to deny Replace on that resource.

Q: Delete stack but keep the RDS instance? → Set DeletionPolicy: Retain on the RDS resource.

Q: Deploy same infrastructure across 20 AWS accounts?StackSets with AWS Organizations integration.

Q: CloudFormation resource that runs custom code?Custom Resource backed by Lambda.

Q: Preview impact of template change before applying? → Create a Change Set and review before executing.