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

Sponsored

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.

  1. Write your template — create a YAML file that defines your resources. Start small: maybe an S3 bucket or an EC2 instance.
  2. Validate the template locally — use aws cloudformation validate-template to catch syntax errors before you deploy.
  3. Create the stack — run aws cloudformation create-stack, passing the template file and a stack name.
  4. Wait for completion — use aws cloudformation wait stack-create-complete to block until the stack is ready.
  5. Verify the resources — query the stack outputs or list resources to confirm everything is as expected.
  6. Update (when needed) — modify the template and run aws cloudformation update-stack.
  7. Delete (when done) — run aws cloudformation delete-stack to 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-template before 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 !Ref or !Sub to generate unique names.
  • Skipping validate-template before 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-stack without making any changes, causing a No updates are to be performed error — 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

  1. Use aws cloudformation deploy instead of create-stack — it's a higher-level command that handles changes and waits automatically, but it's newer and less explicit.
  2. 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.
  3. Use CloudFormation Change Sets via create-change-set and execute-change-set for 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, and delete-stack, plus wait for synchronous completion.
  • Always validate your template before deploying — it catches syntax errors early and saves time.
  • Use describe-stack-events to diagnose failures; the StatusReason field 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.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.