AWS for DevOps — Stacks & Nested Stacks
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_IAMis 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' |