The short answer
Turn on deletion protection for every RDS instance that matters, turn on S3 Versioning for buckets you can’t lose, and give the agent its own AWS role with an explicit deny on delete actions. Then add a pre-command hook in the coding agent that asks before aws rds delete-db-instance, aws s3 rb --force and aws s3 rm --recursive. The first three work even when the agent calls AWS from code; the hook only sees the shell.
Deedglass
A tool call meets your rules before it runs
Asks to run
DELETE FROM visits;
Deletes every row in visits
Deedglass checks your rules
- Reading dataAllowed
- Deleting a whole table (DROP, TRUNCATE)Never
- Changing or deleting rows without saying which (WHERE)This one
- Other changesWait for a person
What one command deletes
The RDS docs are blunt. Delete an instance without a final snapshot and no final snapshot exists to restore. Choose not to retain automated backups and RDS deletes the automated backups in the same Region, and they can’t be recovered.1 With the CLI that’s one line:
Code Commands typed in a terminal. The text around it says what happens.
aws rds delete-db-instance --db-instance-identifier prod \
--skip-final-snapshot --delete-automated-backups
An agent that hits “a final snapshot identifier is required” learns --skip-final-snapshot as quickly as it learns -auto-approve for Terraform. For buckets, aws s3 rb --force deletes every object and then the bucket, and aws s3 rm --recursive deletes everything under a prefix.7
The PocketOS incident was a different cloud, but the same shape: one token that could delete both the data and its backups.
1. Turn on RDS deletion protection
You can’t delete an RDS instance while deletion protection is on. It’s on by default for instances created in the console.1 Through the API and CLI it isn’t on by default, so check instances that scripts or Terraform created:2
Code Commands typed in a terminal. The text around it says what happens.
aws rds describe-db-instances \
--query 'DBInstances[*].[DBInstanceIdentifier,DeletionProtection]' --output table
aws rds modify-db-instance --db-instance-identifier prod --deletion-protection
Changing the setting doesn’t cause an outage.1 The catch is that turning it off is also one modify-db-instance call, so this layer only holds if the agent’s credentials can’t call rds:ModifyDBInstance on that instance. See step 3.
If the worst happens anyway, AWS says to contact support immediately: recovery might be possible for up to six days after the delete request.1 Don’t plan around it.
2. Turn on S3 Versioning
With versioning on, deleting an object inserts a delete marker instead of removing it, and you can restore the previous version. Versioning is off on new buckets until you enable it.3
Code Commands typed in a terminal. The text around it says what happens.
aws s3api put-bucket-versioning --bucket my-bucket \
--versioning-configuration Status=Enabled
MFADelete=Enabled with --mfa adds MFA delete, so permanently deleting a version needs a code from a device the agent doesn’t have.4
Versioning also blunts aws s3 rb --force. The flag deletes the current objects, but versioned objects aren’t deleted, so on a versioned bucket the bucket delete fails because the bucket isn’t empty.5 You lose nothing you can’t restore from the versions.
3. An explicit deny for the agent
Give the agent its own role or profile, never your admin profile, and attach a deny. In AWS, an explicit deny in an identity policy, a permissions boundary or an SCP overrides any allow.6
Code Data in JSON, the format software uses to pass records and settings.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AgentCannotDeleteData",
"Effect": "Deny",
"Action": [
"rds:DeleteDBInstance",
"rds:DeleteDBCluster",
"rds:DeleteDBSnapshot",
"rds:ModifyDBInstance",
"s3:DeleteBucket",
"s3:DeleteObjectVersion",
"s3:PutBucketVersioning",
"dynamodb:DeleteTable",
"iam:*"
],
"Resource": "*"
}
]
}
The less obvious lines matter most. rds:ModifyDBInstance stops the agent turning deletion protection off. s3:PutBucketVersioning stops it suspending versioning. s3:DeleteObjectVersion stops it purging versions. iam:* stops it rewriting its own policy. Narrow ModifyDBInstance to production ARNs if the agent legitimately resizes development databases.
This is the layer that holds no matter how the call is made: CLI, boto3, the JavaScript SDK or a Terraform apply.
4. A hook before the shell
A pre-command hook adds a prompt for the commands the agent types. We ran Deedglass’s shell guard from 0.13.0 on 2026-10-27:7
| Command | Answer |
|---|---|
aws rds delete-db-instance --db-instance-identifier prod --skip-final-snapshot |
asks |
aws s3 rb s3://my-bucket --force |
asks |
aws s3 rm s3://my-bucket --recursive |
asks |
gcloud sql instances delete prod |
asks |
Code What the screen or file shows, copied as is.
$ npx deedglass@0.13.0 guard shell --no-files --command 'aws rds delete-db-instance --db-instance-identifier prod --skip-final-snapshot'
Deedglass asks first: aws rds delete-db-instance deletes the RDS database and its automated backups.
aws-rds-delete aws rds delete-db-instance deletes the RDS database and its automated backups
Each of those is a catalog row with its AWS or Google Cloud reference page and the date it was checked.7 npx deedglass protect installs the guard in Claude Code, Cursor, Codex CLI, Gemini CLI and Copilot CLI in one step.7
Be clear about the limits. The hook reads text, so a delete inside a Python script using boto3, a shell script run by path that it can’t read, or a value built at run time passes untouched.7 If the hook can’t run at all, the command goes ahead unchecked.7 The catalog also covers RDS, S3 and Cloud SQL deletes, not every AWS service: aws dynamodb delete-table isn’t in it. That’s why the deny in step 3 is the backstop and the hook is the early warning.
Questions people ask
Can I recover an RDS instance an agent deleted?
If a final snapshot was taken or automated backups were retained, you can restore from them. If neither, AWS says to contact support immediately, since recovery might be possible for up to six days after the delete request.1
Does aws s3 rb –force delete a versioned bucket?
No. It deletes the current objects, but versioned objects aren’t deleted, so the bucket delete fails because the bucket isn’t empty.5
Is deletion protection on by default for RDS?
For instances created in the console, yes. Through the CLI and API it isn’t, so check instances that scripts or infrastructure code created.12
The ten-minute version
If you have ten minutes before letting an agent loose in a repository with AWS access:
-
describe-db-instancesshowsDeletionProtection: Trueon every production instance. - Production buckets have versioning
Enabled. - The agent’s environment uses a profile that isn’t yours (
AWS_PROFILE=agent). - That profile’s role carries the deny above, ideally in a permissions boundary.
- No long-lived access keys in
.envfiles the agent can read. Our post on API keys in .env files covers how agents find them. - A pre-command hook asks before RDS and S3 deletes.
The same thinking for infrastructure code is in stopping an agent running terraform destroy, and the wider list is the checklist before an agent touches production.
To check a command yourself, run npx deedglass guard shell --no-files --command 'aws s3 rb s3://my-bucket --force'. It reads the command and never runs it. To grade what an agent can reach on your own machine, run npx deedglass scan (what the grade means). To watch the checks without installing anything, open the playground.
Written with AI assistance and checked against our claims register.