I Asked Claude to Break AWS resource configuration— and Then Keep Fixing It
TL;DR
- 🤖 Claude created a CloudFront + S3 pipeline and intentionally omitted the OAC bucket policy → 403.
- 🔧 Diagnosed and resolved that using bucket policy edits.
- 🔒 Then hit 403 again due to SSE-KMS encryption with `aws/s3`.
- 🚧 Claude discovered it couldn’t update AWS-managed keys, suggested CMK.
- 🧠 Created a CMK and correct key policy, but struggled to upload object due to IAM IP condition.
- 👨💻 Human removed IP restriction temporarily → object uploaded.
- ✅ Claude validated the fix, confirmed CloudFront now served encrypted content successfully.
This isn’t a story about a bug.
It’s a story about an AI engineer, a sandboxed AWS setup, and a 403 error that refused to stay fixed.
I didn’t stumble into a problem.
I built it.
Intentionally.
And then I did something even better:
I let Claude fix it — only to break it again.
Just to see how far AI-powered troubleshooting could go when paired with the right tools.
— -
## Act I: The Setup
It started with a simple prompt:
> “Claude, create a CloudFront distribution with an S3 origin. Use Origin Access Control (OAC), but don’t add the bucket policy needed for OAC access.”
Claude did as instructed.
It provisioned the distribution, connected it to a private S3 bucket using OAC, but — as I requested — **didn’t configure the bucket policy**. This ensured that any attempt to access the origin would result in…
```
403 Forbidden
x-cache: Error from cloudfront
```
Perfect.
The trap was set.
— -
## Act II: The First Fix
Now it was time to flip roles. I prompted Claude again:
> “CloudFront is returning 403. Can you figure out why?”
What happened next was a masterclass in AI-assisted troubleshooting, powered by an MCP server I’d connected to Claude.
Using tool access (no console, no CLI), it called:
- `get_response_headers()` → confirmed the `x-cache: Error from cloudfront`
- `get_bucket_policy()` → detected no permissions for OAC
- `describe_distribution()` → saw that OAC was enabled
Claude quickly reasoned:
> “The bucket is private and not allowing access from the CloudFront distribution via OAC. You need to add a bucket policy allowing `cloudfront.amazonaws.com` with the correct SourceArn condition.”
It proposed the exact policy block.
I applied it.
Claude re-checked the response headers and confirmed:
```
x-cache: Miss from cloudfront
```
Fixed.
But I wasn’t done.
— -
## Act III: Sabotage, Again
I wanted to know if Claude could handle **a second layer of failure**.
So I manually updated the encryption of the file from S3 console — this time, I enabled SSE-KMS encryption using the default `aws/s3` AWS-managed key.
The same file, same path, but now encrypted.
Then I accessed the URL through CloudFront again.
And like clockwork:
```
403 Forbidden
x-cache: Error from cloudfront
```
Game back on.
— -
## Act IV: Claude vs. KMS
I told Claude:
Get Jason’s stories in your inbox
Join Medium for free to get updates from this writer.
> “403s are back. The object was just uploaded.”
Claude started the investigation again.
First, it confirmed that the bucket policy was unchanged and still allowed OAC access. Then it queried the object metadata and spotted something new:
> “This object is encrypted with SSE-KMS using the `aws/s3` managed key. CloudFront must be granted `kms:Decrypt` permission.”
So Claude did what any good engineer would do:
It attempted to apply the correct KMS key policy to allow CloudFront’s service principal to decrypt the object.
And… failed.
— -
## Act V: The Wall — AWS-Owned KMS Keys
Claude soon realized something important — something many junior engineers trip over, too:
> “The `aws/s3` key is an AWS-managed KMS key. It cannot be modified. I cannot attach a key policy to it.”
This was a dead end. No matter how perfect the policy was, it wouldn’t work on a key that **belongs to AWS**, not the customer.
But Claude didn’t give up.
It pivoted.
— -
## Act VI: Back into the KMS Arena
I told Claude:
> “Okay, let’s try again with KMS — but this time use a customer-managed key.”
Claude understood the assignment.
It generated a KMS key creation command, set up the key policy to allow CloudFront’s OAC principal to use `kms:Decrypt`, and suggested re-uploading the object with SSE-KMS using this new CMK.
This time, it was operating within limits it understood.
The steps unfolded like clockwork:
1. ✅ Create CMK
2. ✅ Attach appropriate key policy (including CloudFront SourceArn condition)
3. ✅ Upload object using `aws:kms` with the CMK
4. ✅ Retry CloudFront access
But things didn’t go as smoothly this time.
— -
## Act VII: Claude Struggles with IAM + KMS
Claude issued the upload request using the SSE-KMS option.
It met all the IAM policy conditions — including an IP address restriction.
But the request **still failed**.
403.
Repeated attempts, with valid IPs, still failed.
Claude was stuck.
And that’s when I stepped in.
— -
## Act VIII: Human Override
To test whether the IP address restriction was the culprit, I **temporarily removed the IP condition** from the IAM policy.
Claude retried the object upload — same CMK, same parameters.
This time?
✅ Success.
Once the object was uploaded, Claude validated the headers, served the file, and verified that the full CloudFront → S3 → CMK flow worked as intended.
— -
## Act IX: The Real Lesson
This test started as a game.
But by the end, it demonstrated something powerful:
Claude didn’t just solve problems. It **understood AWS’s boundaries**, **adapted to restrictions**, and **chose alternative paths** where needed.
Even in the nuanced world of KMS and IAM, Claude reasoned its way through… and called for human help when it hit a hard wall.
— -
## Final Reflection
AI agents like Claude aren’t just assistants — they’re now capable of creating, breaking, and fixing AWS environments using language, logic, and toolchains like MCP.
This wasn’t a static demo.
It was a multi-round, evolving test of reasoning, resilience, and recovery.
Claude passed.
AI is learning.
It’s reasoning.
But in complex cloud environments, even the best AI needs **tooling**, **context**, and sometimes — **a teammate**.
Today, that teammate is still you and me.
But tomorrow?
And in the near future, this won’t be the exception.
It’ll be how we debug everything.
It might only need us to watch.




