CloudFront via SDK Basics
Six short examples that take you from an empty account to a live distribution in front of an S3 or ALB origin, then through reading, listing, and updating it.
Search across all documentation pages
Six short examples that take you from an empty account to a live distribution in front of an S3 or ALB origin, then through reading, listing, and updating it.
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-cloudfront (Node.js 18+).aws configure, environment variables, or an IAM role with cloudfront:* on the distributions you manage.us-east-1.CloudFront is a global service with a single endpoint, so pin the client to us-east-1.
# --- Python (boto3) ---
import boto3
cf = boto3.client("cloudfront", region_name="us-east-1")
resp = cf.list_distributions()
print(resp["DistributionList"].get("Quantity", 0))// --- TypeScript (AWS SDK v3) ---
import { CloudFrontClient, ListDistributionsCommand } from "@aws-sdk/client-cloudfront";
const cf = new CloudFrontClient({ region: "us-east-1" });
const resp = await cf.send(new ListDistributionsCommand({}));
console.log(resp.DistributionList?.Quantity ?? 0);cloudfront in boto3 and @aws-sdk/client-cloudfront in SDK v3.us-east-1 for the control plane.us-east-1....List or Distribution envelope, so expect nesting.CreateDistribution takes a DistributionConfig describing origins, the default behavior, and a unique CallerReference.
# --- Python (boto3) ---
import boto3, time
cf = boto3.client("cloudfront", region_name="us-east-1")
resp = cf.create_distribution(DistributionConfig={
"CallerReference": f"web-{int(time.time())}", # unique per create
"Comment": "static site",
"Enabled": True,
"Origins": {"Quantity": 1, "Items": [{
"Id": "s3-assets",
"DomainName": "my-bucket.s3.us-east-1.amazonaws.com",
"S3OriginConfig": {"OriginAccessIdentity": ""},
}]},
"DefaultCacheBehavior": {
"TargetOriginId": "s3-assets",
"ViewerProtocolPolicy": "redirect-to-https",
# Managed "CachingOptimized" policy - replaces legacy ForwardedValues.
"CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6",
},
})
print(resp["Distribution"]["Id"], resp["Distribution"]["Status"])// --- TypeScript (AWS SDK v3) ---
import { CloudFrontClient, CreateDistributionCommand } from "@aws-sdk/client-cloudfront";
const cf = new CloudFrontClient({ region: "us-east-1" });
const resp = await cf.send(new CreateDistributionCommand({ DistributionConfig: {
CallerReference: `web-${Date.now()}`, // unique per create
Comment: "static site",
Enabled: true,
Origins: { Quantity: 1, Items: [{
Id: "s3-assets",
DomainName: "my-bucket.s3.us-east-1.amazonaws.com",
S3OriginConfig: { OriginAccessIdentity: "" },
}] },
DefaultCacheBehavior: {
TargetOriginId: "s3-assets",
ViewerProtocolPolicy: "redirect-to-https",
// Managed "CachingOptimized" policy - replaces legacy ForwardedValues.
CachePolicyId: "658327ea-f89d-4fab-a63d-7e88639e58f6",
},
} }));
console.log(resp.Distribution?.Id, resp.Distribution?.Status);CallerReference must be unique per create; reusing one returns the existing distribution instead of erroring.TargetOriginId in the behavior must match an origin Id exactly, or the call is rejected.CachingOptimized here); do not also pass ForwardedValues.OriginAccessControlId to the origin instead of leaving OriginAccessIdentity blank (see the OAC example below).Related: CloudFront's Edge Caching Model - what these config fields mean.
GetDistribution returns the full object plus its deployment Status and current ETag.
# --- Python (boto3) ---
import boto3
cf = boto3.client("cloudfront", region_name="us-east-1")
resp = cf.get_distribution(Id="E1234ABCDEF")
print(resp["Distribution"]["Status"]) # InProgress or Deployed
print(resp["Distribution"]["DomainName"]) # dxxxx.cloudfront.net
print(resp["ETag"]) # needed for updates// --- TypeScript (AWS SDK v3) ---
import { CloudFrontClient, GetDistributionCommand } from "@aws-sdk/client-cloudfront";
const cf = new CloudFrontClient({ region: "us-east-1" });
const resp = await cf.send(new GetDistributionCommand({ Id: "E1234ABCDEF" }));
console.log(resp.Distribution?.Status); // InProgress or Deployed
console.log(resp.Distribution?.DomainName); // dxxxx.cloudfront.net
console.log(resp.ETag); // needed for updatesStatus is InProgress right after a create or update and becomes Deployed once every edge has the change.ETag is the config's version fingerprint; you must supply it as IfMatch on any update or delete.DomainName is the CloudFront hostname you point DNS (a CNAME or Route 53 alias) at.Status rather than assuming the distribution is live immediately.ListDistributions enumerates the account's distributions, paged with a marker.
# --- Python (boto3) ---
import boto3
cf = boto3.client("cloudfront", region_name="us-east-1")
paginator = cf.get_paginator("list_distributions")
for page in paginator.paginate():
for d in page["DistributionList"].get("Items", []):
print(d["Id"], d["DomainName"], d["Enabled"])// --- TypeScript (AWS SDK v3) ---
import { CloudFrontClient, ListDistributionsCommand } from "@aws-sdk/client-cloudfront";
const cf = new CloudFrontClient({ region: "us-east-1" });
let marker: string | undefined;
do {
const page = await cf.send(new ListDistributionsCommand({ Marker: marker }));
for (const d of page.DistributionList?.Items ?? []) {
console.log(d.Id, d.DomainName, d.Enabled);
}
marker = page.DistributionList?.NextMarker;
} while (marker);DistributionList.Items; an empty account omits Items entirely, so guard for it.Marker / NextMarker, not the NextToken some services use.GetDistribution for the full config and ETag.Updating is a three-step dance: get the current config, change it, then UpdateDistribution with the ETag as IfMatch.
# --- Python (boto3) ---
import boto3
cf = boto3.client("cloudfront", region_name="us-east-1")
cur = cf.get_distribution_config(Id="E1234ABCDEF")
config, etag = cur["DistributionConfig"], cur["ETag"]
config["Comment"] = "static site (updated)" # mutate the FULL config
cf.update_distribution(Id="E1234ABCDEF", DistributionConfig=config, IfMatch=etag)// --- TypeScript (AWS SDK v3) ---
import {
CloudFrontClient, GetDistributionConfigCommand, UpdateDistributionCommand,
} from "@aws-sdk/client-cloudfront";
const cf = new CloudFrontClient({ region: "us-east-1" });
const cur = await cf.send(new GetDistributionConfigCommand({ Id: "E1234ABCDEF" }));
const config = cur.DistributionConfig!;
config.Comment = "static site (updated)"; // mutate the FULL config
await cf.send(new UpdateDistributionCommand({
Id: "E1234ABCDEF", DistributionConfig: config, IfMatch: cur.ETag,
}));UpdateDistribution replaces the entire config, so you must send back the full object, not a patch.GetDistributionConfig (not GetDistribution, whose shape differs) and reuse its ETag.IfMatch returns PreconditionFailed (HTTP 412); re-fetch and retry.ETag for the next change.You cannot delete an enabled distribution; disable it first, wait for Deployed, then delete with the latest ETag.
# --- Python (boto3) ---
import boto3
cf = boto3.client("cloudfront", region_name="us-east-1")
cur = cf.get_distribution_config(Id="E1234ABCDEF")
config, etag = cur["DistributionConfig"], cur["ETag"]
config["Enabled"] = False # step 1: disable
upd = cf.update_distribution(Id="E1234ABCDEF", DistributionConfig=config, IfMatch=etag)
cf.get_waiter("distribution_deployed").wait(Id="E1234ABCDEF") # step 2: wait
cf.delete_distribution(Id="E1234ABCDEF", IfMatch=upd["ETag"]) # step 3: delete// --- TypeScript (AWS SDK v3) ---
import {
CloudFrontClient, GetDistributionConfigCommand, UpdateDistributionCommand,
DeleteDistributionCommand, waitUntilDistributionDeployed,
} from "@aws-sdk/client-cloudfront";
const cf = new CloudFrontClient({ region: "us-east-1" });
const cur = await cf.send(new GetDistributionConfigCommand({ Id: "E1234ABCDEF" }));
const config = cur.DistributionConfig!;
config.Enabled = false; // step 1: disable
const upd = await cf.send(new UpdateDistributionCommand({
Id: "E1234ABCDEF", DistributionConfig: config, IfMatch: cur.ETag,
}));
await waitUntilDistributionDeployed({ client: cf, maxWaitTime: 900 }, { Id: "E1234ABCDEF" }); // step 2
await cf.send(new DeleteDistributionCommand({ Id: "E1234ABCDEF", IfMatch: upd.ETag })); // step 3distribution_deployed waiter (boto3) or waitUntilDistributionDeployed (SDK v3) between the two steps.ETag changes after the disable update, so pass the update's new ETag to DeleteDistribution.CreateOriginAccessControl makes the modern OAC that lets only CloudFront read a private S3 bucket.
# --- Python (boto3) ---
import boto3
cf = boto3.client("cloudfront", region_name="us-east-1")
oac = cf.create_origin_access_control(OriginAccessControlConfig={
"Name": "assets-oac",
"OriginAccessControlOriginType": "s3",
"SigningBehavior": "always",
"SigningProtocol": "sigv4",
})
print(oac["OriginAccessControl"]["Id"]) # set as OriginAccessControlId on the origin// --- TypeScript (AWS SDK v3) ---
import { CloudFrontClient, CreateOriginAccessControlCommand } from "@aws-sdk/client-cloudfront";
const cf = new CloudFrontClient({ region: "us-east-1" });
const oac = await cf.send(new CreateOriginAccessControlCommand({ OriginAccessControlConfig: {
Name: "assets-oac",
OriginAccessControlOriginType: "s3",
SigningBehavior: "always",
SigningProtocol: "sigv4",
} }));
console.log(oac.OriginAccessControl?.Id); // set as OriginAccessControlId on the originCreateCloudFrontOriginAccessIdentity (OAI) still works but is legacy.OriginAccessControlId, and leave S3OriginConfig.OriginAccessIdentity empty.cloudfront.amazonaws.com service principal for this distribution.Related: Cache Behaviors & Origin Groups - routing multiple origins from one distribution.
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 23, 2026