← SCP Explorer

Deny S3 bucket deletion

Prevents deletion of S3 buckets except by specific infrastructure automation roles, providing protection against accidental data loss.

Policy description

What this SCP does

This Service Control Policy (SCP) prevents deletion of S3 buckets except by specific infrastructure automation roles, providing protection against accidental data loss. This is particularly useful for preserving important storage resources.

By restricting bucket deletion to only roles with the "infra-*" team tag, this policy ensures that your critical data storage cannot be removed by unauthorized users or by mistake, while still allowing infrastructure teams to manage storage resources when necessary.

Validation strategy

How to test this SCP works

To validate this SCP, test bucket deletion with roles that have different team tags.

  • Valid test: Create IAM roles with different team tags (infra-ops vs. dev-team)
  • Expected result: Bucket deletion succeeds when performed by principals tagged with Team=infra-*, but fails when performed by any other principals

For thorough testing, create test buckets and attempt to delete them using different IAM roles to verify that only appropriately tagged roles can perform deletion operations.

SCP policy & validation scenarios

{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyS3BucketDeletion", "Effect": "Deny", "Action": "s3:DeleteBucket", "Resource": "*", "Condition": { "StringNotLike": { "aws:PrincipalTag/Team": "infra-*" } } } ] }
# Create roles with different team tags aws iam create-role \ --role-name infra-role \ --assume-role-policy-document file://trust-policy.json \ --tags Key=Team,Value=infra-ops aws iam create-role \ --role-name dev-role \ --assume-role-policy-document file://trust-policy.json \ --tags Key=Team,Value=dev-team # Create test buckets and try to delete them using each role # Deletion should succeed when using infra-role but fail with dev-role
import boto3 import json import time # Initialize IAM client iam_client = boto3.client('iam') s3_client = boto3.client('s3') # Create test roles with different team tags trust_policy = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": {"Service": "ec2.amazonaws.com"}, "Action": "sts:AssumeRole" } ] } try: # Create role with infra tag iam_client.create_role( RoleName='infra-role', AssumeRolePolicyDocument=json.dumps(trust_policy), Tags=[ { 'Key': 'Team', 'Value': 'infra-ops' } ] ) print("Created role with infra tag") # Create role with dev tag iam_client.create_role( RoleName='dev-role', AssumeRolePolicyDocument=json.dumps(trust_policy), Tags=[ { 'Key': 'Team', 'Value': 'dev-team' } ] ) print("Created role with dev tag") # Create test bucket bucket_name = f"test-scp-bucket-{int(time.time())}" s3_client.create_bucket(Bucket=bucket_name) print(f"Created test bucket: {bucket_name}") print("Note: To fully test this SCP, you'd need to:") print("1. Create EC2 instance profiles for both roles") print("2. Launch EC2 instances with each profile") print("3. Try to delete the bucket from each instance") print("Deletion should succeed from the infra instance but fail from the dev instance") # Clean up s3_client.delete_bucket(Bucket=bucket_name) iam_client.delete_role(RoleName='infra-role') iam_client.delete_role(RoleName='dev-role') print("Cleaned up test resources") except Exception as e: print(f"Error in test setup: {e}")