AWS CLI CloudFront Commands: Create a Distribution and Invalidate Cache
We serve files fast worldwide: a CloudFront distribution in front of a private S3 bucket, config updates with ETag, cache invalidation, then disable and delete.
What you'll learn in this chapter
- Origin access control
- Create a distribution
- Bucket policy for CloudFront
- Invalidate the cache
- Update, disable and delete
1. The big picture: local milk booths
Why do we need this?
The dairy is far, so milk is kept in small booths in every area. CloudFront keeps copies of your files at edge locations near users, and only goes to the origin (S3) when a copy is missing or old.
What is it?
Commands are in aws cloudfront. It is global; updates and deletes need the current ETag as --if-match.
How to do it (step by step)
2. Origin access control
Why do we need this?
The bucket should stay private; only CloudFront may read it.
What is it?
An OAC signs CloudFront requests to S3. The bucket policy then allows cloudfront.amazonaws.com for your distribution only.
How to do it (step by step)
- Create the OAC.
OAC=$(aws cloudfront create-origin-access-control --origin-access-control-config Name=cli-oac,SigningProtocol=sigv4,SigningBehavior=always,OriginAccessControlOriginType=s3 --query OriginAccessControl.Id --output text)
echo $OAC
Expected output:
E2QWRUHAPOMQZL
3. Create the distribution
Why do we need this?
Users in Delhi and London should both load the site quickly over HTTPS.
What is it?
A distribution config lists origins, a default cache behavior, viewer protocol policy and a default root object. CallerReference must be unique.
How to do it (step by step)
- Write the config.
cat > dist.json <<J
{"CallerReference":"cli-$(date +%s)","Comment":"cli site","Enabled":true,"DefaultRootObject":"index.html",
"Origins":{"Quantity":1,"Items":[{"Id":"s3","DomainName":"cli-site-bucket.s3.ap-south-1.amazonaws.com","OriginAccessControlId":"$OAC","S3OriginConfig":{"OriginAccessIdentity":""}}]},
"DefaultCacheBehavior":{"TargetOriginId":"s3","ViewerProtocolPolicy":"redirect-to-https","CachePolicyId":"658327ea-f89d-4fab-a63d-7e88639e58f6"}}
J
Expected output:
(no output means success)
- Create and wait.
D=$(aws cloudfront create-distribution --distribution-config file://dist.json --query Distribution.[Id,DomainName] --output text)
echo $D
aws cloudfront wait distribution-deployed --id ${D%%[[:space:]]*}
Expected output:
E1ABCDEF234567 d111111abcdef8.cloudfront.net
4. Bucket policy and test
Why do we need this?
Without the policy CloudFront gets 403 from S3.
What is it?
Allow s3:GetObject to the cloudfront service principal, with AWS:SourceArn equal to your distribution ARN.
How to do it (step by step)
- Apply the policy.
ID=E1ABCDEF234567
cat > bp.json <<J
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"cloudfront.amazonaws.com"},"Action":"s3:GetObject","Resource":"arn:aws:s3:::cli-site-bucket/*","Condition":{"StringEquals":{"AWS:SourceArn":"arn:aws:cloudfront::111122223333:distribution/$ID"}}}]}
J
aws s3api put-bucket-policy --bucket cli-site-bucket --policy file://bp.json
Expected output:
(no output means success)
- Test.
curl -sI https://d111111abcdef8.cloudfront.net/ | grep -iE "^HTTP|x-cache"
Expected output:
HTTP/2 200
x-cache: Miss from cloudfront
- Test again (from cache).
curl -sI https://d111111abcdef8.cloudfront.net/ | grep -i x-cache
Expected output:
x-cache: Hit from cloudfront
5. Invalidate, update, disable
Why do we need this?
You uploaded a new index.html but users still see the old one; later you want to change or remove the distribution.
What is it?
create-invalidation removes cached paths. update-distribution needs the full config and the ETag from get-distribution-config.
How to do it (step by step)
- Invalidate.
aws cloudfront create-invalidation --distribution-id $ID --paths "/index.html" "/css/*" --query Invalidation.Status --output text
Expected output:
InProgress
- Get config and ETag, then disable.
aws cloudfront get-distribution-config --id $ID > cfg.json
E=$(jq -r .ETag cfg.json)
jq .DistributionConfig cfg.json | jq ".Enabled=false" > new.json
aws cloudfront update-distribution --id $ID --if-match $E --distribution-config file://new.json --query Distribution.Status --output text
Expected output:
InProgress
6. Same action in Python, Node.js, Java, Go, .NET and PHP
Why do we need this?
Scripts start in the CLI. Apps call the same API through an SDK. Knowing both helps you move a tested command into real code.
What is it?
Each CLI command maps to one API call. For example aws cloudfront create-invalidation --distribution-id $ID --paths "/*" maps to the Invalidate API.
How to do it (step by step)
Same action (Invalidate) from code. Every SDK reads the same profile, region and credentials as the CLI.
| Stack | Code |
|---|---|
| Python (boto3) | boto3.client("cloudfront").create_invalidation(DistributionId=id,InvalidationBatch={"Paths":{"Quantity":1,"Items":["/*"]},"CallerReference":str(time.time())}) |
| Node.js (v3) | await cf.send(new CreateInvalidationCommand({DistributionId:id,InvalidationBatch:{Paths:{Quantity:1,Items:["/*"]},CallerReference:Date.now().toString()}})); |
| Java (v2) | cf.createInvalidation(r -> r.distributionId(id).invalidationBatch(b -> b.callerReference(""+System.currentTimeMillis()).paths(p -> p.quantity(1).items("/*")))); |
| Go (v2) | c.CreateInvalidation(ctx, &cloudfront.CreateInvalidationInput{DistributionId: aws.String(id), InvalidationBatch: batch}) |
| .NET | await cf.CreateInvalidationAsync(new CreateInvalidationRequest{DistributionId=id,InvalidationBatch=batch}); |
| PHP | $cf->createInvalidation(["DistributionId"=>$id,"InvalidationBatch"=>$batch]); |
7. Clean up so nothing keeps costing
Why do we need this?
Lab resources left running cost money and can be a security risk.
What is it?
Delete in reverse order: children first, then parents.
How to do it (step by step)
- Wait for the disabled state to deploy.
aws cloudfront wait distribution-deployed --id $ID
Expected output:
(no output means success)
- Delete with the new ETag.
E=$(aws cloudfront get-distribution --id $ID --query ETag --output text)
aws cloudfront delete-distribution --id $ID --if-match $E
Expected output:
(no output means success)
- Delete the OAC.
E=$(aws cloudfront get-origin-access-control --id $OAC --query ETag --output text)
aws cloudfront delete-origin-access-control --id $OAC --if-match $E
Expected output:
(no output means success)
- Verify.
aws cloudfront list-distributions --query "DistributionList.Items[?Id=='$ID']"
Expected output:
[]
Ravindra Bagale's Tip
"PreconditionFailed" means your ETag is old. Every update returns a new ETag, so fetch it again right before each update or delete.
8. Interview questions and answers
Q. What is an edge location?
A CloudFront site near users that caches content.
Q. OAC vs public bucket?
OAC keeps the bucket private and lets only your distribution read it.
Q. Why --if-match?
Optimistic locking with the ETag so two people do not overwrite each other.
Q. How to show new files immediately?
create-invalidation for the paths, or use versioned file names.
Q. Why disable before delete?
CloudFront only deletes distributions that are disabled and deployed.
Try at home
- Put an index.html in a private bucket and serve it through CloudFront over HTTPS.
- Change the file, invalidate it and confirm the new version.
In short
- OAC keeps S3 private.
- create-distribution.
- Bucket policy with SourceArn.
- create-invalidation.
- ETag + --if-match.
Practice Tasks
Nairobi Ride is a transport business in Nairobi. Wanjiru Kamau looks after its IT and cloud work. They count money in KES and run their AWS resources in the af-south-1 Region. In the tasks below you do real jobs for them using what this lesson teaches. All names, account IDs, IP addresses and numbers are made up for practice.
0 / 10
Easy List distributions
Why: Find the distribution ID and domain name.
What to do: List all CloudFront distributions.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Easy How many addresses in a /16?
Why: Picking a VPC size needs this count.
What to do: Nairobi Ride plans the VPC 10.205.0.0/16. How many IP addresses does it hold in total?
- Write down the numbers the task gives you.
- Work it out step by step on paper.
- Type only the final answer in the box.
- Click Check answer.
Easy Port for SSH
Why: Security group rules need the right port.
What to do: Which TCP port must the security group open for SSH?
- Write down the numbers the task gives you.
- Work it out step by step on paper.
- Type only the final answer in the box.
- Click Check answer.
Medium Clear the CloudFront cache
Why: After a deploy, invalidate so visitors get the new files now, not after the TTL.
What to do: Invalidate all paths (/*) on distribution E2QWRUHAPOMQZL.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Hard Create a distribution from a config file
Why: The full config (origins, behaviors, certificate) is easiest to keep in JSON.
What to do: Create a CloudFront distribution from dist.json.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Hard Invalidate two paths
Why: Invalidating only what changed is cheaper and faster.
What to do: On E2QWRUHAPOMQZL, invalidate /index.html and /css/site.css.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Hard Bucket policy for CloudFront OAC
Why: With OAC, only your CloudFront distribution may read the private bucket.
What to do: Allow the service cloudfront.amazonaws.com to s3:GetObject on nairobiride-site/*, only when aws:SourceArn equals arn:aws:cloudfront::104591109902:distribution/E2QWRUHAPOMQZL.
- Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
- In each statement write Effect, Action and Resource.
- Give only what the task needs (least privilege).
- Use double quotes and commas correctly. Click Check answer.
Hard Create an Origin Access Control
Why: OAC lets only CloudFront read your private S3 bucket.
What to do: Create an origin access control from oac.json.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Hard Request a free certificate for CloudFront
Why: CloudFront only uses ACM certificates from us-east-1. DNS validation renews automatically.
What to do: Request a certificate for nairobiride.co.ke and www.nairobiride.co.ke with DNS validation, in us-east-1.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Hard Make a CloudFront signed URL
Why: Signed URLs let only paying users watch a video, for a limited time.
What to do: Sign https://d111.cloudfront.net/course/intro.mp4 with key pair ID K2JCJMDEHXQW5F, private key private_key.pem, expiring 2026-12-31.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Revision, comparison and interview cards
Revise this lesson in a few minutes. Save the cards on your phone or download them as PNG files. The QR code on each card opens this lesson.




More cards: Ravi Sir's Cards.