AWS for DevOps — Stacks & Nested Stacks

3 minute read

8.3 — Stacks & Nested Stacks

A stack is a collection of AWS resources managed as a single unit. Create, update, or delete a stack — all its resources follow.

Stack Lifecycle

  template.yml
       │
       ▼
  aws cloudformation create-stack
       │
       ▼
  CREATE_IN_PROGRESS
       │
       ▼  (all resources created successfully)
  CREATE_COMPLETE
       │
       ▼  (update the template)
  aws cloudformation update-stack
       │
       ▼
  UPDATE_IN_PROGRESS
       │
       ▼
  UPDATE_COMPLETE
       │
       ▼  (done with the stack)
  aws cloudformation delete-stack
       │
       ▼
  DELETE_IN_PROGRESS → DELETE_COMPLETE

Creating a Stack

$ aws cloudformation create-stack \
    --stack-name my-app-prod \
    --template-body file://template.yml \
    --parameters \
        ParameterKey=EnvironmentType,ParameterValue=production \
        ParameterKey=InstanceType,ParameterValue=t3.small \
    --capabilities CAPABILITY_IAM \
    --tags Key=Team,Value=DevOps Key=Project,Value=MyApp
{
    "StackId": "arn:aws:cloudformation:ap-south-1:123456789012:stack/my-app-prod/abc-123"
}

# Wait for creation to complete
$ aws cloudformation wait stack-create-complete --stack-name my-app-prod

# Check stack status
$ aws cloudformation describe-stacks --stack-name my-app-prod \
    --query 'Stacks[0].{Status:StackStatus,Outputs:Outputs}'
{
    "Status": "CREATE_COMPLETE",
    "Outputs": [
        { "OutputKey": "InstanceId", "OutputValue": "i-0abc123def456" },
        { "OutputKey": "PublicIP", "OutputValue": "54.123.45.67" }
    ]
}

--capabilities CAPABILITY_IAM is required when your template creates IAM resources (roles, policies, users).

Updating a Stack

# Update with a modified template
$ aws cloudformation update-stack \
    --stack-name my-app-prod \
    --template-body file://template-v2.yml \
    --parameters \
        ParameterKey=EnvironmentType,UsePreviousValue=true \
        ParameterKey=InstanceType,ParameterValue=t3.medium

# Watch events during update
$ aws cloudformation describe-stack-events \
    --stack-name my-app-prod \
    --query 'StackEvents[].[Timestamp,ResourceType,LogicalResourceId,ResourceStatus]' \
    --output table

Update Behaviours

When you change a resource property, CloudFormation determines the impact:

Update Type What Happens Example
No interruption Resource updated in-place Changing a tag
Some interruption Brief disruption Changing EC2 instance type (stop/start)
Replacement Old resource deleted, new one created Changing EC2 AMI, RDS engine

Always use Change Sets (section 8.4) to preview update behaviour before applying.

Deleting a Stack

$ aws cloudformation delete-stack --stack-name my-app-prod

# Watch deletion
$ aws cloudformation wait stack-delete-complete --stack-name my-app-prod

Deletion Protection

# Enable protection
$ aws cloudformation update-termination-protection \
    --stack-name my-app-prod \
    --enable-termination-protection

# Now delete will fail:
$ aws cloudformation delete-stack --stack-name my-app-prod
# Error: Stack [my-app-prod] cannot be deleted while TerminationProtection is enabled

Stack Policies

Prevent accidental updates to critical resources:

$ aws cloudformation set-stack-policy \
    --stack-name my-app-prod \
    --stack-policy-body '{
        "Statement": [
            {
                "Effect": "Deny",
                "Action": "Update:Replace",
                "Principal": "*",
                "Resource": "LogicalResourceId/ProductionDatabase"
            },
            {
                "Effect": "Allow",
                "Action": "Update:*",
                "Principal": "*",
                "Resource": "*"
            }
        ]
    }'

This prevents the database from being replaced (which deletes data!) while allowing all other updates.

Nested Stacks

Split large templates into smaller, reusable components:

  Root Stack (main.yml)
  ├── Nested Stack: VPC (vpc.yml)
  │   └── VPC, Subnets, Route Tables
  ├── Nested Stack: Security (security.yml)
  │   └── Security Groups, NACLs
  └── Nested Stack: Application (app.yml)
      └── EC2, ALB, Auto Scaling

Root template:

Resources:
  VPCStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3.amazonaws.com/my-templates/vpc.yml
      Parameters:
        VpcCidr: "10.0.0.0/16"
        EnvironmentType: !Ref EnvironmentType

  SecurityStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: VPCStack
    Properties:
      TemplateURL: https://s3.amazonaws.com/my-templates/security.yml
      Parameters:
        VpcId: !GetAtt VPCStack.Outputs.VpcId

  AppStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: SecurityStack
    Properties:
      TemplateURL: https://s3.amazonaws.com/my-templates/app.yml
      Parameters:
        SubnetId: !GetAtt VPCStack.Outputs.PublicSubnetId
        SecurityGroupId: !GetAtt SecurityStack.Outputs.AppSGId

Cross-Stack References

Alternative to nested stacks — independent stacks that share values:

Stack A (VPC):

Outputs:
  VpcId:
    Value: !Ref MyVPC
    Export:
      Name: "shared-vpc-id"
  PublicSubnet:
    Value: !Ref PublicSubnet
    Export:
      Name: "shared-public-subnet"

Stack B (Application):

Resources:
  WebServer:
    Type: AWS::EC2::Instance
    Properties:
      SubnetId: !ImportValue "shared-public-subnet"

Nested Stacks vs Cross-Stack References

Aspect Nested Stacks Cross-Stack References
Lifecycle All managed by root stack Independent lifecycles
Coupling Tight — root controls everything Loose — stacks are independent
Reuse Template reuse via TemplateURL Value sharing via Export/Import
Best for Components of a single application Shared infrastructure (VPC, DNS)

Common Mistakes

Mistake Why It Hurts Fix
No termination protection on prod stacks Accidental deletion destroys everything Enable --enable-termination-protection
Deleting stack that exports values Dependent stacks break Check !ImportValue references first
Huge monolithic templates Hard to debug, slow updates Split into nested or cross-stack
No DependsOn for implicit dependencies Resources created out of order Use DependsOn or reference with !Ref

Quick Reference

Task Command
Create stack aws cloudformation create-stack --stack-name NAME --template-body file://template.yml
Update stack aws cloudformation update-stack --stack-name NAME --template-body file://template.yml
Delete stack aws cloudformation delete-stack --stack-name NAME
List stacks aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE
View events aws cloudformation describe-stack-events --stack-name NAME
Get outputs aws cloudformation describe-stacks --stack-name NAME --query 'Stacks[0].Outputs'

Next → Change Sets & Drift Detection

Updated: