CloudFormation & CDK Basics
Seven short examples that take you from a template string to a running stack: creating it, waiting for it, reading its outputs, updating it, and cleaning it up.
Search across all documentation pages
Seven short examples that take you from a template string to a running stack: creating it, waiting for it, reading its outputs, updating it, and cleaning it up.
Each example shows the same operation in both Python (boto3) and TypeScript (AWS SDK for JavaScript v3), so the mechanics translate one to one.
pip install boto3 (boto3 1.43.x, Python 3.10+). TypeScript: npm install @aws-sdk/client-cloudformation (Node.js 18+).aws configure, environment variables, or an IAM role with cloudformation:* on the stacks you manage.CreateStack accepts a template as a JSON or YAML string via TemplateBody, or a URL to an S3 object via TemplateURL.
# --- Python (boto3) ---
import boto3
template = """
Resources:
MyBucket:
Type: AWS::S3::Bucket
"""
cfn = boto3.client("cloudformation", region_name="us-east-1")
cfn.create_stack(
StackName="my-first-stack",
TemplateBody=template,
)// --- TypeScript (AWS SDK v3) ---
import { CloudFormationClient, CreateStackCommand } from "@aws-sdk/client-cloudformation";
const template = `
Resources:
MyBucket:
Type: AWS::S3::Bucket
`;
const cfn = new CloudFormationClient({ region: "us-east-1" });
await cfn.send(new CreateStackCommand({
StackName: "my-first-stack",
TemplateBody: template,
}));CreateStack returns immediately with a StackId; creation happens asynchronously.TemplateURL instead of TemplateBody for templates over 51,200 bytes - large templates must be uploaded to S3 first.StackName must be unique per region and account, and becomes part of the stack's ARN.Creation is asynchronous, so poll with a waiter rather than assuming it finished.
# --- Python (boto3) ---
import boto3
cfn = boto3.client("cloudformation", region_name="us-east-1")
waiter = cfn.get_waiter("stack_create_complete")
waiter.wait(StackName="my-first-stack")
print("stack created")// --- TypeScript (AWS SDK v3) ---
import { CloudFormationClient, waitUntilStackCreateComplete } from "@aws-sdk/client-cloudformation";
const cfn = new CloudFormationClient({ region: "us-east-1" });
await waitUntilStackCreateComplete(
{ client: cfn, maxWaitTime: 600 },
{ StackName: "my-first-stack" },
);
console.log("stack created");DescribeStacks internally until StackStatus reaches a terminal state.StackStatus for the failure reason.maxWaitTime (SDK v3) bounds how long you wait before giving up; boto3's waiter has its own default retry count.CreateStack returns.DescribeStacks returns the current status plus any Outputs the template declared.
# --- Python (boto3) ---
import boto3
cfn = boto3.client("cloudformation", region_name="us-east-1")
resp = cfn.describe_stacks(StackName="my-first-stack")
stack = resp["Stacks"][0]
print(stack["StackStatus"])
for output in stack.get("Outputs", []):
print(output["OutputKey"], output["OutputValue"])// --- TypeScript (AWS SDK v3) ---
import { CloudFormationClient, DescribeStacksCommand } from "@aws-sdk/client-cloudformation";
const cfn = new CloudFormationClient({ region: "us-east-1" });
const resp = await cfn.send(new DescribeStacksCommand({ StackName: "my-first-stack" }));
const stack = resp.Stacks?.[0];
console.log(stack?.StackStatus);
for (const output of stack?.Outputs ?? []) {
console.log(output.OutputKey, output.OutputValue);
}Stacks is a list even when you pass a single StackName - always index into it.Outputs is absent if the template declares none, so guard with a default empty list.StackStatus values like CREATE_COMPLETE or ROLLBACK_COMPLETE tell you what happened.Related: Stack Creation, Updates & Change Sets - the safe way to apply changes to a stack that already exists.
Parameters lets a template accept caller-supplied values instead of hardcoding them.
# --- Python (boto3) ---
import boto3
template = """
Parameters:
BucketNameSuffix:
Type: String
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "my-app-${BucketNameSuffix}"
"""
cfn = boto3.client("cloudformation", region_name="us-east-1")
cfn.create_stack(
StackName="my-parameterized-stack",
TemplateBody=template,
Parameters=[{"ParameterKey": "BucketNameSuffix", "ParameterValue": "prod-01"}],
)// --- TypeScript (AWS SDK v3) ---
import { CloudFormationClient, CreateStackCommand } from "@aws-sdk/client-cloudformation";
const template = `
Parameters:
BucketNameSuffix:
Type: String
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "my-app-\${BucketNameSuffix}"
`;
const cfn = new CloudFormationClient({ region: "us-east-1" });
await cfn.send(new CreateStackCommand({
StackName: "my-parameterized-stack",
TemplateBody: template,
Parameters: [{ ParameterKey: "BucketNameSuffix", ParameterValue: "prod-01" }],
}));Parameters is a list of key/value pairs matching the template's Parameters section.Default fails CreateStack immediately.UsePreviousValue: true on updates to keep an existing parameter unchanged.Templates that create IAM roles, users, or policies require you to explicitly acknowledge that with Capabilities.
# --- Python (boto3) ---
import boto3
template = """
Resources:
MyRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: {Service: lambda.amazonaws.com}
Action: sts:AssumeRole
"""
cfn = boto3.client("cloudformation", region_name="us-east-1")
cfn.create_stack(
StackName="my-iam-stack",
TemplateBody=template,
Capabilities=["CAPABILITY_IAM"],
)// --- TypeScript (AWS SDK v3) ---
import { CloudFormationClient, CreateStackCommand } from "@aws-sdk/client-cloudformation";
const template = `
Resources:
MyRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: {Service: lambda.amazonaws.com}
Action: sts:AssumeRole
`;
const cfn = new CloudFormationClient({ region: "us-east-1" });
await cfn.send(new CreateStackCommand({
StackName: "my-iam-stack",
TemplateBody: template,
Capabilities: ["CAPABILITY_IAM"],
}));Capabilities, CreateStack fails with an InsufficientCapabilitiesException for templates creating IAM resources.CAPABILITY_NAMED_IAM instead when the template assigns explicit names to IAM resources.CAPABILITY_AUTO_EXPAND is a separate capability needed for templates using macros or nested transforms.UpdateStack applies a new template or new parameter values to a stack that already exists.
# --- Python (boto3) ---
import boto3
updated_template = """
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
VersioningConfiguration:
Status: Enabled
"""
cfn = boto3.client("cloudformation", region_name="us-east-1")
cfn.update_stack(
StackName="my-first-stack",
TemplateBody=updated_template,
)
cfn.get_waiter("stack_update_complete").wait(StackName="my-first-stack")// --- TypeScript (AWS SDK v3) ---
import { CloudFormationClient, UpdateStackCommand, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
const updatedTemplate = `
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
VersioningConfiguration:
Status: Enabled
`;
const cfn = new CloudFormationClient({ region: "us-east-1" });
await cfn.send(new UpdateStackCommand({
StackName: "my-first-stack",
TemplateBody: updatedTemplate,
}));
await waitUntilStackUpdateComplete({ client: cfn, maxWaitTime: 600 }, { StackName: "my-first-stack" });UpdateStack applies immediately with no preview - for production changes, prefer a change set instead (next page).DeleteStack removes every resource in the stack as one unit, honoring each resource's deletion policy.
# --- Python (boto3) ---
import boto3
cfn = boto3.client("cloudformation", region_name="us-east-1")
cfn.delete_stack(StackName="my-first-stack")
cfn.get_waiter("stack_delete_complete").wait(StackName="my-first-stack")
print("stack deleted")// --- TypeScript (AWS SDK v3) ---
import { CloudFormationClient, DeleteStackCommand, waitUntilStackDeleteComplete } from "@aws-sdk/client-cloudformation";
const cfn = new CloudFormationClient({ region: "us-east-1" });
await cfn.send(new DeleteStackCommand({ StackName: "my-first-stack" }));
await waitUntilStackDeleteComplete({ client: cfn, maxWaitTime: 600 }, { StackName: "my-first-stack" });
console.log("stack deleted")DeletionPolicy of Retain or Snapshot.stack_delete_complete also succeeds when the stack simply no longer exists, so it is safe to call after a manual cleanup.Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 25, 2026