Deploy a CloudFormation Stack with AWS CLI
Learn how to deploy a CloudFormation stack using the AWS CLI in this hands-on tutorial. This lesson covers the core concept, step-by-step commands, troubleshooting tips, and what to study next in the AWS Cloud & DevOps with Python track.
Focus: deploy a cloudformation stack with aws cli
Have you ever found yourself clicking through the AWS Management Console, manually creating EC2 instances, security groups, and load balancers — only to realize you have no idea how to reproduce that setup in another region or account? This is the exact pain that Infrastructure as Code (IaC) eliminates, and in this lesson you will learn to deploy a CloudFormation stack with AWS CLI — the fastest, most reproducible way to bring your cloud infrastructure to life. By the end, you will be able to define your resources in a YAML template, push it to AWS with a single command, and update or delete that entire environment on demand.
The problem this lesson solves
Manually creating AWS resources through the console is slow, error-prone, and impossible to version. You might click Create instance, choose the right AMI, configure a security group, and attach a key pair — but by the time you've done it for the fifth time, you're bound to miss a setting. Worse, sharing that configuration with a teammate means sending screenshots or writing a Word doc that someone will misread.
Deploying a CloudFormation stack with AWS CLI solves all of that. Instead of clicking, you write a text file that describes every resource your application needs. That file becomes your single source of truth. It lives in Git, it gets reviewed in pull requests, and it can be deployed to any account or region in minutes. When you need to tear everything down, you issue one command and AWS removes the resources in the correct order — no orphaned VPCs or forgotten elastic IPs.
The problem this lesson solves is simple: manual cloud provisioning is a bottleneck for any serious DevOps workflow. If you want to ship faster, reduce human error, and work collaboratively on infrastructure, you need to automate the deploy. And the AWS CLI is the most direct, scriptable way to do it — especially when you're already using Python for automation elsewhere in your pipeline.
Why not just use the console? The console is great for exploring, but it's not reproducible. Imagine trying to replicate a 20-resource environment by hand — you'd spend hours and still miss something. CloudFormation with the CLI gives you deterministic, repeatable deployments every time.
Core concept / mental model
Think of a CloudFormation stack as a box that AWS uses to group all the resources your application needs. The box itself has a name, and inside it, every resource is created, updated, or deleted together as a unit. The template is the blueprint for that box — a YAML (or JSON) file that lists every resource and its properties.
Here's a simple mental model:
- Template = the recipe (YAML file)
- Stack = the final dish (the deployed resources)
- AWS CLI = your cooking tool that hands the recipe to AWS
When you run aws cloudformation create-stack, you're telling AWS: Here's the recipe — go make the dish. AWS parses the template, validates it, and provisions the resources in the correct order (for example, a security group before an EC2 instance that references it). If anything fails, CloudFormation rolls back the stack, so you don't end up with a half-built environment.
You'll almost always pair this with Python in this track. For example, you might write a Python script that calls the AWS CLI (or the boto3 library) to deploy the stack as part of an automated pipeline. But the core skill — the one that gets you out of the console — is mastering the CLI commands.
Key terms you'll see everywhere
- Stack: a collection of AWS resources managed as a single unit
- Template: a YAML/JSON file that defines the resources
- Change set: a preview of what will change before you apply it
- Stack status: e.g.,
CREATE_COMPLETE,UPDATE_COMPLETE,ROLLBACK_COMPLETE
How it works step by step
Deploying a CloudFormation stack with the AWS CLI follows a predictable sequence. You'll repeat this flow for almost every stack you ever create.
- Write your template — create a YAML file that defines your resources. Start small: maybe an S3 bucket or an EC2 instance.
- Validate the template locally — use
aws cloudformation validate-templateto catch syntax errors before you deploy. - Create the stack — run
aws cloudformation create-stack, passing the template file and a stack name. - Wait for completion — use
aws cloudformation wait stack-create-completeto block until the stack is ready. - Verify the resources — query the stack outputs or list resources to confirm everything is as expected.
- Update (when needed) — modify the template and run
aws cloudformation update-stack. - Delete (when done) — run
aws cloudformation delete-stackto remove everything.
Each command sends a request to the CloudFormation API. The CLI waits and returns a response, but the actual provisioning happens asynchronously. That's why you use the wait command — it polls the stack status until it reaches a terminal state, which is perfect for scripting.
Pro tip: Always validate before you create. It's a 2-second command that saves you from a 10-minute failed deploy.
Hands-on walkthrough
Let's deploy a real stack. We'll create an S3 bucket, which is one of the simplest resources to start with. This example assumes you have the AWS CLI installed and configured with credentials that have permission to create S3 buckets and CloudFormation stacks.
Step 1: Write a minimal template
Create a file named bucket.yaml:
AWSTemplateFormatVersion: "2010-09-09"
Description: "A simple S3 bucket created via CloudFormation"
Resources:
MyBucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Sub "${AWS::StackName}-unique-suffix"
Note: S3 bucket names must be globally unique, so the !Sub trick appends the stack name to keep it unique. In a real project, you'd probably let CloudFormation generate a random name by omitting BucketName.
Step 2: Validate the template
Run this command in your terminal:
aws cloudformation validate-template --template-body file://bucket.yaml
If it's valid, you'll see a JSON response describing parameters, resources, and capabilities. If there's a syntax error, you'll get a clear error message pointing to the line.
Step 3: Create the stack
Now deploy it:
aws cloudformation create-stack --stack-name my-s3-stack --template-body file://bucket.yaml
The output will include the stack ID, which looks like arn:aws:cloudformation:us-east-1:123456789012:stack/my-s3-stack/.... This is your unique identifier.
Step 4: Wait for completion
Run:
aws cloudformation wait stack-create-complete --stack-name my-s3-stack
This command returns nothing when successful. If it fails, it exits with an error code. You can then check the stack events to see what went wrong.
Step 5: Verify your resources
List the resources in the stack:
aws cloudformation list-stack-resources --stack-name my-s3-stack
You should see your bucket in the output. To prove the bucket exists, you can also run:
aws s3 ls | grep my-s3-stack
Full Python + CLI example
Because this track is about using Python for DevOps, here's a complete script that deploys the stack and waits for it, all from Python:
import subprocess
import sys
import time
def run_command(cmd: list[str]) -> None:
"""Run a shell command and raise on failure."""
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error: {result.stderr}")
sys.exit(1)
print(result.stdout)
def deploy_stack(stack_name: str, template_file: str) -> None:
print("Validating template...")
run_command(["aws", "cloudformation", "validate-template",
"--template-body", f"file://{template_file}"])
print("Creating stack...")
run_command(["aws", "cloudformation", "create-stack",
"--stack-name", stack_name,
"--template-body", f"file://{template_file}"])
print("Waiting for stack creation...")
run_command(["aws", "cloudformation", "wait", "stack-create-complete",
"--stack-name", stack_name])
print("Stack deployed successfully!")
if __name__ == "__main__":
deploy_stack("my-s3-stack", "bucket.yaml")
Run it with:
python deploy_stack.py
Expected output (trimmed):
Validating template...
Creating stack...
Waiting for stack creation...
Stack deployed successfully!
This is exactly the kind of script you'd embed in a CI/CD pipeline. You could even use boto3 instead of the CLI, but the CLI approach is simpler and more readable for this task.
Compare options / when to choose what
Now that you can deploy a stack with the CLI, let's compare the main ways to manage CloudFormation stacks. Each has its place.
| Method | Best for | Pros | Cons |
|---|---|---|---|
| AWS CLI | Quick local deploys, scripting in shell or Python | Lightweight, no extra tools, easy to automate | You handle rollbacks and updates manually |
| Management Console | Visual debugging, one-off explorations | See resources and events graphically | Not reproducible, hard to scale |
| AWS SDK (boto3) | Python-native automation, complex logic | Full programmatic control, integrates with Python apps | More verbose than CLI for simple deploys |
| CloudFormation Change Sets | Pre-flight checks before updates | See exactly what will change before you apply | Extra step in the workflow |
| AWS SAM / CDK | Serverless apps or higher-level abstraction | Less manual YAML, built-in best practices | Extra learning curve, not always needed |
When to choose what:
- Use the CLI for simple stacks and quick experiments — it's the fastest way to go from template to live resources.
- Use Change Sets (via CLI) when you're updating a production stack and want to review the diff first. The CLI supports this with
aws cloudformation create-change-set. - Use boto3 when you're already writing Python and need to integrate stack deployment with your application logic — for example, a Lambda function that deploys infrastructure on demand.
- Use the console when you're investigating a problem and want to click through events visually.
Pro tip: For automation, always use the CLI or SDK — never the console. The console is for humans, not machines.
Troubleshooting & edge cases
You will hit errors. Here are the most common ones and how to fix them.
1. ValidationError: Bucket name already exists
S3 bucket names are globally unique. If you hardcode a name that someone else owns, creation fails.
Fix: Remove BucketName from your template to let AWS generate a random name, or add a unique suffix like !Join ["-", ["my-bucket", AWS::AccountId]].
2. No updates are to be performed
When you run update-stack but haven't changed anything, CloudFormation complains.
Fix: Make a real change to your template. If you're just adding a new resource, you'll see it in the output.
3. Stack status is ROLLBACK_COMPLETE
CloudFormation rolled back because something failed during creation. This usually means one of your resource properties was invalid.
Fix: Check the stack events:
aws cloudformation describe-stack-events --stack-name my-s3-stack
Look for the StatusReason field to see the exact error.
4. Permission denied on create-stack
Your IAM user doesn't have the cloudformation:CreateStack permission, or the role isn't allowed to create the resource (e.g., S3 bucket).
Fix: Attach the AWSCloudFormationFullAccess policy (or a custom one) to your user, and ensure your credentials allow s3:CreateBucket.
5. Template size too large
If your YAML exceeds 51,200 bytes, the CLI rejects it.
Fix: Upload the template to S3 and use --template-url instead of --template-body.
Pro tip: Always use
validate-templatebefore every deploy. It catches 90% of syntax errors before you burn time on a failed stack.
What you learned & what's next
You now know how to deploy a CloudFormation stack with AWS CLI: you can write a template, validate it, create a stack, wait for completion, verify resources, and update or delete it. This is a foundational DevOps skill that makes your infrastructure reproducible, versionable, and automated.
You also saw how to wrap the CLI in a Python script, which is exactly what you'll do in larger pipelines. Up next in this track, you'll learn how to manage stack updates and change sets — so you can make changes to production infrastructure without fear. You'll also explore how to use parameters and outputs to make your templates reusable across environments.
Now go ahead: create your own simple stack, update it, and then delete it. Practice until the commands feel automatic.
Practice recap
Create a simple EC2 instance stack using the CLI, then update it to change the instance type. Finally, delete the stack and verify that both the instance and stack are gone. This will reinforce the full create-update-delete lifecycle you'll use in production.
Common mistakes
- Hardcoding unique resource names (like S3 bucket names) that collide with other accounts — always use
!Refor!Subto generate unique names. - Skipping
validate-templatebefore creating a stack, which leads to avoidable failures that waste time and money on rollbacks. - Forgetting to wait for stack creation with
wait stack-create-complete— your script might try to use a resource before it exists. - Running
update-stackwithout making any changes, causing aNo updates are to be performederror — always modify the template first. - Not checking stack events after a rollback; the exact error is literally in the
StatusReason, so always look there.
Variations
- Use
aws cloudformation deployinstead ofcreate-stack— it's a higher-level command that handles changes and waits automatically, but it's newer and less explicit. - Use
boto3(the AWS SDK for Python) instead of the CLI when you need to integrate deployment with Python application logic, like in a Lambda function. - Use CloudFormation Change Sets via
create-change-setandexecute-change-setfor safe, reviewed updates to production stacks.
Real-world use cases
- Automated CI/CD pipeline that deploys a staging stack on every Git push using a Python script that calls the AWS CLI.
- Disaster recovery: replicate an entire production environment in another region by deploying the same template via CLI and parameter overrides.
- Ephemeral environments for feature branches — spin up a stack, run tests, then delete it automatically to save costs.
Key takeaways
- A CloudFormation stack is a group of AWS resources defined by a YAML template and deployed as a unit.
- The core CLI commands are
create-stack,update-stack, anddelete-stack, pluswaitfor synchronous completion. - Always validate your template before deploying — it catches syntax errors early and saves time.
- Use
describe-stack-eventsto diagnose failures; theStatusReasonfield tells you exactly what went wrong. - Wrap CLI calls in Python scripts to automate deployments in your DevOps pipeline.
- Delete your stacks when you're done to avoid unnecessary charges — resources like EC2 and RDS incur costs by the hour.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.