
Aws Cloudformation S3
- 71 installs
- 318 repo stars
- Updated June 22, 2026
- giuseppe-trisciuoglio/developer-kit
Author AWS CloudFormation templates for S3 buckets, bucket policies, versioning, and lifecycle rules.
About
Provides CloudFormation patterns for Amazon S3 including bucket configurations, policies, versioning, and lifecycle rules. A developer uses it to provision object-storage infrastructure as code with proper access control.
- Bucket policies for access control and versioning for data protection
- Lifecycle rules with Parameters, Outputs, Conditions, cross-stack references
Aws Cloudformation S3 by the numbers
- 71 all-time installs (skills.sh)
- Ranked #648 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill aws-cloudformation-s3Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 71 |
|---|---|
| repo stars | ★ 318 |
| Last updated | June 22, 2026 |
| Repository | giuseppe-trisciuoglio/developer-kit ↗ |
What it does
Author AWS CloudFormation templates for S3 buckets, bucket policies, versioning, and lifecycle rules.
Files
AWS CloudFormation S3 Patterns
Create production-ready Amazon S3 infrastructure using AWS CloudFormation templates. This skill covers S3 bucket configurations, bucket policies, versioning, lifecycle rules, and template structure best practices.
When to Use
Use this skill when:
- Creating S3 buckets with custom configurations
- Implementing bucket policies for access control
- Configuring S3 versioning for data protection
- Setting up lifecycle rules for data management
- Creating Outputs for cross-stack references
- Using Parameters with AWS-specific types
- Organizing templates with Mappings and Conditions
- Building reusable CloudFormation templates for S3
Quick Start
Basic S3 Bucket
AWSTemplateFormatVersion: 2010-09-09
Description: Simple S3 bucket with default settings
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-data-bucket
Tags:
- Key: Environment
Value: production
- Key: Project
Value: my-projectS3 Bucket with Versioning and Logging
AWSTemplateFormatVersion: 2010-09-09
Description: S3 bucket with versioning and access logging
Parameters:
BucketName:
Type: String
Description: Name of the S3 bucket
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
VersioningConfiguration:
Status: Enabled
LoggingConfiguration:
DestinationBucketName: !Ref AccessLogBucket
LogFilePrefix: logs/
Tags:
- Key: Name
Value: !Ref BucketName
AccessLogBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${BucketName}-logs
AccessControl: LogDeliveryWrite
Outputs:
BucketName:
Description: Name of the S3 bucket
Value: !Ref DataBucket
BucketArn:
Description: ARN of the S3 bucket
Value: !GetAtt DataBucket.ArnTemplate Structure
Template Sections Overview
AWS CloudFormation templates are JSON or YAML files with specific sections. Each section serves a purpose in defining your infrastructure.
AWSTemplateFormatVersion: 2010-09-09 # Required - template version
Description: Optional description string # Optional description
# Section order matters for readability but CloudFormation accepts any order
Mappings: {} # Static configuration tables
Metadata: {} # Additional information about resources
Parameters: {} # Input values for customization
Rules: {} # Parameter validation rules
Conditions: {} # Conditional resource creation
Transform: {} # Macro processing (e.g., AWS::Serverless)
Resources: {} # AWS resources to create (REQUIRED)
Outputs: {} # Return values after stack creationFormat Version
The AWSTemplateFormatVersion identifies the template version. Current version is 2010-09-09.
AWSTemplateFormatVersion: 2010-09-09
Description: My S3 CloudFormation TemplateDescription
Add a description to document the template's purpose. Must appear after the format version.
AWSTemplateFormatVersion: 2010-09-09
Description: >
This template creates an S3 bucket with versioning enabled
for data protection. It includes:
- Bucket with versioning configuration
- Lifecycle rules for data retention
- Server access loggingMetadata
Use Metadata for additional information about resources or parameters.
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Bucket Configuration
Parameters:
- BucketName
- EnableVersioning
- Label:
default: Lifecycle Rules
Parameters:
- RetentionDays
ParameterLabels:
BucketName:
default: Bucket Name
EnableVersioning:
default: Enable VersioningResources Section
The Resources section is the only required section. It defines AWS resources to provision.
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-data-bucket
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: trueParameters
Parameter Types
Use AWS-specific parameter types for validation and easier selection in the console.
Parameters:
ExistingBucketName:
Type: AWS::S3::Bucket::Name
Description: Select an existing S3 bucket
BucketNamePrefix:
Type: String
Description: Prefix for new bucket namesSSM Parameter Types
Reference Systems Manager parameters for dynamic values.
Parameters:
LatestBucketPolicy:
Type: AWS::SSM::Parameter::Value<String>
Description: Latest bucket policy from SSM
Default: /s3/bucket-policy/latestParameter Constraints
Add constraints to validate parameter values.
Parameters:
BucketName:
Type: String
Description: Name of the S3 bucket
Default: my-bucket
AllowedPattern: ^[a-z0-9][a-z0-9-]*[a-z0-9]$
ConstraintDescription: Bucket names must be lowercase, numbers, or hyphens
RetentionDays:
Type: Number
Description: Number of days to retain objects
Default: 30
MinValue: 1
MaxValue: 365
ConstraintDescription: Must be between 1 and 365 days
Environment:
Type: String
Description: Deployment environment
Default: development
AllowedValues:
- development
- staging
- production
ConstraintDescription: Must be development, staging, or productionMappings
Use Mappings for static configuration data based on regions or other factors.
Mappings:
RegionConfig:
us-east-1:
BucketPrefix: us-east-1
us-west-2:
BucketPrefix: us-west-2
eu-west-1:
BucketPrefix: eu-west-1
EnvironmentSettings:
development:
VersioningStatus: Suspended
RetentionDays: 7
staging:
VersioningStatus: Enabled
RetentionDays: 30
production:
VersioningStatus: Enabled
RetentionDays: 90
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${BucketPrefix}-${Environment}-data
VersioningConfiguration:
Status: !FindInMap [EnvironmentSettings, !Ref Environment, VersioningStatus]Conditions
Use Conditions to conditionally create resources based on parameters.
Parameters:
EnableVersioning:
Type: String
Default: true
AllowedValues:
- true
- false
Environment:
Type: String
Default: development
AllowedValues:
- development
- staging
- production
CreateLifecycleRule:
Type: String
Default: true
AllowedValues:
- true
- false
Conditions:
ShouldEnableVersioning: !Equals [!Ref EnableVersioning, true]
IsProduction: !Equals [!Ref Environment, production]
ShouldCreateLifecycle: !Equals [!Ref CreateLifecycleRule, true]
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${Environment}-data-bucket
VersioningConfiguration:
Status: !If [ShouldEnableVersioning, Enabled, Suspended]
LifecycleRule:
Type: AWS::S3::Bucket
Condition: ShouldCreateLifecycle
Properties:
BucketName: !Sub ${Environment}-lifecycle-bucket
LifecycleConfiguration:
Rules:
- Status: Enabled
ExpirationInDays: !If
- IsProduction
- 90
- 30
NoncurrentVersionExpirationInDays: 30Transform
Use Transform for macros like AWS::Serverless for SAM templates.
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: SAM template with S3 bucket trigger
Resources:
ThumbnailFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: python3.9
CodeUri: function/
Events:
ImageUpload:
Type: S3
Properties:
Bucket: !Ref ImageBucket
Events: s3:ObjectCreated:*
ImageBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${AWS::StackName}-imagesOutputs and Cross-Stack References
Basic Outputs
Outputs:
BucketName:
Description: Name of the S3 bucket
Value: !Ref DataBucket
BucketArn:
Description: ARN of the S3 bucket
Value: !GetAtt DataBucket.Arn
BucketDomainName:
Description: Domain name of the S3 bucket
Value: !GetAtt DataBucket.DomainName
BucketWebsiteURL:
Description: Website URL for the S3 bucket
Value: !GetAtt DataBucket.WebsiteURLExporting Values for Cross-Stack References
Export values so other stacks can import them.
Outputs:
BucketName:
Description: Bucket name for other stacks
Value: !Ref DataBucket
Export:
Name: !Sub ${AWS::StackName}-BucketName
BucketArn:
Description: Bucket ARN for other stacks
Value: !GetAtt DataBucket.Arn
Export:
Name: !Sub ${AWS::StackName}-BucketArn
BucketRegion:
Description: Bucket region
Value: !Ref AWS::Region
Export:
Name: !Sub ${AWS::StackName}-BucketRegionImporting Values in Another Stack
Parameters:
DataBucketName:
Type: AWS::S3::Bucket::Name
Description: Data bucket name from data stack
# User selects from exported values in console
# Or use Fn::ImportValue for programmatic access
Resources:
BucketAccessRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: S3Access
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource: !Sub
- ${BucketArn}/*
- BucketArn: !ImportValue data-stack-BucketArnCross-Stack Reference Pattern
Create a dedicated data storage stack that exports values:
# storage-stack.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: S3 storage infrastructure stack
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${AWS::StackName}-data
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LogBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${AWS::StackName}-logs
AccessControl: LogDeliveryWrite
Outputs:
DataBucketName:
Value: !Ref DataBucket
Export:
Name: !Sub ${AWS::StackName}-DataBucketName
DataBucketArn:
Value: !GetAtt DataBucket.Arn
Export:
Name: !Sub ${AWS::StackName}-DataBucketArn
LogBucketName:
Value: !Ref LogBucket
Export:
Name: !Sub ${AWS::StackName}-LogBucketNameApplication stack imports these values:
# application-stack.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Application stack that imports from storage
Parameters:
StorageStackName:
Type: String
Description: Name of the storage stack
Default: storage-stack
Resources:
ApplicationBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${AWS::StackName}-application
CorsConfiguration:
CorsRules:
- AllowedHeaders:
- "*"
AllowedMethods:
- GET
- PUT
- POST
AllowedOrigins:
- !Ref ApplicationDomain
MaxAge: 3600S3 Bucket Configuration
Bucket with Public Access Block
Resources:
SecureBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-secure-bucket
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: trueBucket with Versioning
Resources:
VersionedBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-versioned-bucket
VersioningConfiguration:
Status: Enabled
# Use MFADelete to require MFA for version deletion
# MFADelete: Enabled # Optional, requires MFABucket with Lifecycle Rules
Resources:
LifecycleBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-lifecycle-bucket
LifecycleConfiguration:
Rules:
# Expire objects after 30 days
- Id: ExpireOldObjects
Status: Enabled
ExpirationInDays: 30
NoncurrentVersionExpirationInDays: 7
# Archive to Glacier after 90 days
- Id: ArchiveToGlacier
Status: Enabled
Transitions:
- Days: 90
StorageClass: GLACIER
- Days: 365
StorageClass: DEEP_ARCHIVE
NoncurrentVersionTransitions:
- NoncurrentDays: 30
StorageClass: GLACIERBucket with Cross-Region Replication
Resources:
SourceBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-source-bucket
VersioningConfiguration:
Status: Enabled
ReplicationConfiguration:
Role: !GetAtt ReplicationRole.Arn
Rules:
- Id: ReplicateToDestRegion
Status: Enabled
Destination:
Bucket: !Sub arn:aws:s3:::my-dest-bucket-${AWS::Region}
StorageClass: STANDARD_IA
EncryptionConfiguration:
ReplicaKmsKeyID: !Ref DestKMSKey
ReplicationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: s3.amazonaws.com
Action: sts:AssumeRoleBucket Policies
Bucket Policy for Private Access
Resources:
PrivateBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-private-bucket
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref PrivateBucket
PolicyDocument:
Statement:
- Sid: DenyPublicRead
Effect: Deny
Principal: "*"
Action:
- s3:GetObject
Resource: !Sub ${PrivateBucket.Arn}/*
Condition:
Bool:
aws:SecureTransport: falseBucket Policy for CloudFront OAI
Resources:
StaticWebsiteBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-static-website
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: error.html
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref StaticWebsiteBucket
PolicyDocument:
Statement:
- Sid: CloudFrontReadAccess
Effect: Allow
Principal:
CanonicalUser: !GetAtt CloudFrontOAI.S3CanonicalUserId
Action: s3:GetObject
Resource: !Sub ${StaticWebsiteBucket.Arn}/*
CloudFrontOAI:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub ${AWS::StackName}-oaiBucket Policy for VPC Endpoint
Resources:
PrivateBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-private-bucket
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref PrivateBucket
PolicyDocument:
Statement:
- Sid: AllowVPCEndpoint
Effect: Allow
Principal: "*"
Action: s3:GetObject
Resource: !Sub ${PrivateBucket.Arn}/*
Condition:
StringEquals:
aws:sourceVpce: !Ref VPCEndpointIdComplete S3 Bucket Example
AWSTemplateFormatVersion: 2010-09-09
Description: Production-ready S3 bucket with versioning, logging, and lifecycle
Parameters:
BucketName:
Type: String
Description: Name of the S3 bucket
Environment:
Type: String
Default: production
AllowedValues:
- development
- staging
- production
EnableVersioning:
Type: String
Default: true
AllowedValues:
- true
- false
RetentionDays:
Type: Number
Default: 90
Description: Days to retain objects
Conditions:
ShouldEnableVersioning: !Equals [!Ref EnableVersioning, true]
IsProduction: !Equals [!Ref Environment, production]
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
VersioningConfiguration:
Status: !If [ShouldEnableVersioning, Enabled, Suspended]
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LoggingConfiguration:
DestinationBucketName: !Ref AccessLogBucket
LogFilePrefix: !Sub ${BucketName}/logs/
LifecycleConfiguration:
Rules:
- Id: StandardLifecycle
Status: Enabled
ExpirationInDays: !Ref RetentionDays
NoncurrentVersionExpirationInDays: 30
Transitions:
- Days: 30
StorageClass: STANDARD_IA
- Days: 90
StorageClass: GLACIER
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Name
Value: !Ref BucketName
AccessLogBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${BucketName}-logs
AccessControl: LogDeliveryWrite
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
- Id: DeleteLogsAfter30Days
Status: Enabled
ExpirationInDays: 30
Outputs:
BucketName:
Description: Name of the S3 bucket
Value: !Ref DataBucket
BucketArn:
Description: ARN of the S3 bucket
Value: !GetAtt DataBucket.Arn
BucketDomainName:
Description: Domain name of the S3 bucket
Value: !GetAtt DataBucket.DomainName
BucketWebsiteURL:
Description: Website URL for the S3 bucket
Value: !GetAtt DataBucket.WebsiteURL
LogBucketName:
Description: Name of the access log bucket
Value: !Ref AccessLogBucketCloudFormation Best Practices
Stack Policies
Stack Policies protect stack resources from unintentional updates that could cause disruption or data loss. Use them to prevent accidental modifications to critical resources.
Setting a Stack Policy
{
"Statement": [
{
"Effect": "Allow",
"Action": "Update:*",
"Principal": "*",
"Resource": "*"
},
{
"Effect": "Deny",
"Action": [
"Update:Replace",
"Update:Delete"
],
"Principal": "*",
"Resource": "LogicalResourceId/DataBucket"
},
{
"Effect": "Deny",
"Action": "Update:*",
"Principal": "*",
"Resource": "LogicalResourceId/AccessLogBucket",
"Condition": {
"StringEquals": {
"ResourceType": ["AWS::S3::Bucket"]
}
}
}
]
}Applying Stack Policy via AWS CLI
aws cloudformation set-stack-policy \
--stack-name my-s3-stack \
--stack-policy-body file://stack-policy.jsonStack Policy for Production Environment
{
"Statement": [
{
"Effect": "Allow",
"Action": ["Update:Modify", "Update:Replace", "Update:Delete"],
"Principal": "*",
"Resource": "*",
"Condition": {
"StringEquals": {
"ResourceType": ["AWS::S3::Bucket"]
}
}
},
{
"Effect": "Deny",
"Action": "Update:Delete",
"Principal": "*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "Update:*",
"Principal": "AWS": ["arn:aws:iam::123456789012:role/AdminRole"],
"Resource": "*"
}
]
}Termination Protection
Termination Protection prevents accidental deletion of CloudFormation stacks. Always enable it for production stacks.
Enabling Termination Protection
# Enable termination protection when creating a stack
aws cloudformation create-stack \
--stack-name my-s3-stack \
--template-body file://template.yaml \
--enable-termination-protection
# Enable termination protection on existing stack
aws cloudformation update-termination-protection \
--stack-name my-s3-stack \
--enable-termination-protection
# Disable termination protection
aws cloudformation update-termination-protection \
--stack-name my-s3-stack \
--no-enable-termination-protectionTermination Protection in SDK (Python)
import boto3
def enable_termination_protection(stack_name):
cfn = boto3.client('cloudformation')
try:
cfn.update_termination_protection(
StackName=stack_name,
EnableTerminationProtection=True
)
print(f"Termination protection enabled for stack: {stack_name}")
except cfn.exceptions.TerminationProtectionError as e:
if "already" in str(e).lower():
print(f"Termination protection already enabled for stack: {stack_name}")
else:
raiseVerification Script
#!/bin/bash
# verify-termination-protection.sh
STACK_NAME=$1
if [ -z "$STACK_NAME" ]; then
echo "Usage: $0 <stack-name>"
exit 1
fi
STATUS=$(aws cloudformation describe-stacks \
--stack-name $STACK_NAME \
--query 'Stacks[0].TerminationProtection' \
--output text)
if [ "$STATUS" = "True" ]; then
echo "Termination protection is ENABLED for $STACK_NAME"
exit 0
else
echo "WARNING: Termination protection is DISABLED for $STACK_NAME"
exit 1
fiDrift Detection
Drift Detection identifies differences between the actual infrastructure and the CloudFormation template. Run it regularly to ensure compliance.
Detecting Drift
# Detect drift on a single stack
aws cloudformation detect-drift \
--stack-name my-s3-stack
# Detect drift and get detailed results
STACK_NAME="my-s3-stack"
# Start drift detection
aws cloudformation detect-drift \
--stack-name $STACK_NAME
# Wait for drift detection to complete
aws cloudformation wait stack-drift-detection-complete \
--stack-name $STACK_NAME
# Get drift detection status
STATUS=$(aws cloudformation describe-stack-drift-detection-status \
--stack-name $STACK_NAME \
--query 'StackDriftStatus' \
--output text)
echo "Stack drift status: $STATUS"
# Get detailed drift information
if [ "$STATUS" = "DRIFTED" ]; then
aws cloudformation describe-stack-resource-drifts \
--stack-name $STACK_NAME \
--query 'StackResourceDrifts[*].[LogicalResourceId,ResourceType,DriftStatus,PropertyDifferences]' \
--output table
fiDrift Detection Script with Reporting
#!/bin/bash
# detect-drift.sh
STACK_NAME=$1
REPORT_FILE="drift-report-${STACK_NAME}-$(date +%Y%m%d).json"
if [ -z "$STACK_NAME" ]; then
echo "Usage: $0 <stack-name> [report-file]"
exit 1
fi
if [ -n "$2" ]; then
REPORT_FILE=$2
fi
echo "Starting drift detection for stack: $STACK_NAME"
# Start drift detection
DETECTION_ID=$(aws cloudformation detect-drift \
--stack-name $STACK_NAME \
--query 'Id' \
--output text)
echo "Drift detection initiated. Detection ID: $DETECTION_ID"
# Wait for completion
echo "Waiting for drift detection to complete..."
aws cloudformation wait stack-drift-detection-complete \
--stack-name $STACK_NAME 2>/dev/null || true
# Get detection status
DRIFT_STATUS=$(aws cloudformation describe-stack-drift-detection-status \
--stack-name $STACK_NAME \
--query 'StackDriftStatus' \
--output text 2>/dev/null)
echo "Drift status: $DRIFT_STATUS"
# Get detailed results
if [ "$DRIFT_STATUS" = "DRIFTED" ]; then
echo "Resources with drift detected:"
aws cloudformation describe-stack-resource-drifts \
--stack-name $STACK_NAME \
--output json > "$REPORT_FILE"
echo "Drift report saved to: $REPORT_FILE"
# Display summary
aws cloudformation describe-stack-resource-drifts \
--stack-name $STACK_NAME \
--query 'StackResourceDrifts[?DriftStatus==`MODIFIED`].[LogicalResourceId,ResourceType,PropertyDifferences[].PropertyName]' \
--output table
else
echo "No drift detected. Stack is in sync with template."
echo "{}" > "$REPORT_FILE"
fiDrift Detection for Multiple Stacks
import boto3
from datetime import datetime
def detect_drift_all_stacks(prefix="prod-"):
cfn = boto3.client('cloudformation')
s3 = boto3.client('s3')
# List all stacks with prefix
stacks = cfn.list_stacks(
StackStatusFilter=['CREATE_COMPLETE', 'UPDATE_COMPLETE']
)['StackSummaries']
target_stacks = [s for s in stacks if s['StackName'].startswith(prefix)]
drift_results = []
for stack in target_stacks:
stack_name = stack['StackName']
print(f"Checking drift for: {stack_name}")
# Start drift detection
response = cfn.detect_drift(StackName=stack_name)
detection_id = response['Id']
# Wait for completion (simplified - in production use waiter)
waiter = cfn.get_waiter('stack_drift_detection_complete')
waiter.wait(StackName=stack_name)
# Get status
status = cfn.describe_stack_drift_detection_status(
StackName=stack_name
)
drift_results.append({
'stack_name': stack_name,
'drift_status': status['StackDriftStatus'],
'detection_time': datetime.utcnow().isoformat()
})
if status['StackDriftStatus'] == 'DRIFTED':
# Get detailed drift info
resources = cfn.describe_stack_resource_drifts(
StackName=stack_name
)['StackResourceDrifts']
drift_results[-1]['drifted_resources'] = [
{
'logical_id': r['LogicalResourceId'],
'type': r['ResourceType'],
'status': r['DriftStatus']
} for r in resources
]
return drift_resultsChange Sets
Change Sets preview changes before applying them. Always use them for production deployments to review impact.
Creating and Executing a Change Set
#!/bin/bash
# deploy-with-changeset.sh
STACK_NAME=$1
TEMPLATE_FILE=$2
CHANGESET_NAME="${STACK_NAME}-changeset-$(date +%Y%m%d%H%M%S)"
if [ -z "$STACK_NAME" ] || [ -z "$TEMPLATE_FILE" ]; then
echo "Usage: $0 <stack-name> <template-file>"
exit 1
fi
echo "Creating change set for stack: $STACK_NAME"
# Create change set
aws cloudformation create-change-set \
--stack-name $STACK_NAME \
--template-body file://$TEMPLATE_FILE \
--change-set-name $CHANGESET_NAME \
--capabilities CAPABILITY_IAM \
--change-set-type UPDATE
echo "Change set created: $CHANGESET_NAME"
# Wait for change set creation
aws cloudformation wait change-set-create-complete \
--stack-name $STACK_NAME \
--change-set-name $CHANGESET_NAME
# Display changes
echo ""
echo "=== Change Set Summary ==="
aws cloudformation describe-change-set \
--stack-name $STACK_NAME \
--change-set-name $CHANGESET_NAME \
--query '[ChangeSetName,Status,ChangeSetStatus,StatusReason]' \
--output table
echo ""
echo "=== Detailed Changes ==="
aws cloudformation list-change-sets \
--stack-name $STACK_NAME \
--query "Summaries[?ChangeSetName=='$CHANGESET_NAME'].[Changes]" \
--output text | python3 -m json.tool 2>/dev/null || \
aws cloudformation describe-change-set \
--stack-name $STACK_NAME \
--change-set-name $CHANGESET_NAME \
--query 'Changes[*].ResourceChange' \
--output table
# Prompt for execution
echo ""
read -p "Execute this change set? (yes/no): " CONFIRM
if [ "$CONFIRM" = "yes" ]; then
echo "Executing change set..."
aws cloudformation execute-change-set \
--stack-name $STACK_NAME \
--change-set-name $CHANGESET_NAME
echo "Waiting for stack update to complete..."
aws cloudformation wait stack-update-complete \
--stack-name $STACK_NAME
echo "Stack update complete!"
else
echo "Change set execution cancelled."
echo "To execute later, run:"
echo "aws cloudformation execute-change-set --stack-name $STACK_NAME --change-set-name $CHANGESET_NAME"
fiChange Set with Parameter Overrides
# Create change set with parameters
aws cloudformation create-change-set \
--stack-name my-s3-stack \
--template-body file://template.yaml \
--change-set-name my-changeset \
--parameters \
ParameterKey=BucketName,ParameterValue=my-new-bucket \
ParameterKey=Environment,ParameterValue=production \
--capabilities CAPABILITY_IAM
# Generate change set from existing stack
aws cloudformation create-change-set \
--stack-name my-s3-stack \
--template-body file://new-template.yaml \
--change-set-name migrate-to-new-template \
--change-set-type IMPORT \
--resources-to-import "[\"DataBucket\", \"AccessLogBucket\"]"Change Set Preview Script
import boto3
def preview_changes(stack_name, template_body, parameters=None):
cfn = boto3.client('cloudformation')
changeset_name = f"{stack_name}-preview-{int(__import__('time').time())}"
try:
# Create change set
kwargs = {
'StackName': stack_name,
'TemplateBody': template_body,
'ChangeSetName': changeset_name,
'ChangeSetType': 'UPDATE'
}
if parameters:
kwargs['Parameters'] = parameters
response = cfn.create_change_set(**kwargs)
# Wait for creation
waiter = cfn.get_waiter('change_set_create_complete')
waiter.wait(StackName=stack_name, ChangeSetName=changeset_name)
# Get change set description
changeset = cfn.describe_change_set(
StackName=stack_name,
ChangeSetName=changeset_name
)
print(f"Change Set: {changeset['ChangeSetName']}")
print(f"Status: {changeset['Status']}")
print(f"Number of changes: {len(changeset.get('Changes', []))}")
# Display each change
for change in changeset.get('Changes', []):
resource = change['ResourceChange']
print(f"\n{resource['Action']} {resource['LogicalResourceId']} ({resource['ResourceType']})")
if resource.get('Replacement') == 'True':
print(f" - This resource will be REPLACED (potential downtime)")
for detail in resource.get('Details', []):
print(f" - {detail['Attribute']}: {detail['Name']}")
return changeset
except cfn.exceptions.AlreadyExistsException:
print(f"Change set already exists")
return None
finally:
# Clean up change set
try:
cfn.delete_change_set(
StackName=stack_name,
ChangeSetName=changeset_name
)
except Exception:
passChange Set Best Practices
# Best practices for change sets
# 1. Always use descriptive change set names
CHANGESET_NAME="update-bucket-config-$(date +%Y%m%d)"
# 2. Use appropriate change set type
aws cloudformation create-change-set \
--stack-name my-stack \
--change-set-type UPDATE \
--template-body file://template.yaml
# 3. Review changes before execution
aws cloudformation describe-change-set \
--stack-name my-stack \
--change-set-name $CHANGESET_NAME \
--query 'Changes[].ResourceChange'
# 4. Use capabilities flag when needed
aws cloudformation create-change-set \
--stack-name my-stack \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
--template-body file://template.yaml
# 5. Set execution role for controlled deployments
aws cloudformation create-change-set \
--stack-name my-stack \
--execution-role-name arn:aws:iam::123456789012:role/CloudFormationExecutionRole \
--template-body file://template.yamlRelated Files
For detailed resource reference information, see:
- reference.md - Complete AWS::S3::Bucket and AWS::S3::BucketPolicy properties
For comprehensive examples, see:
- examples.md - Real-world S3 patterns and use cases
AWS CloudFormation S3 - Examples
This file contains comprehensive examples for Amazon S3 patterns with CloudFormation.
Example 1: Static Website Hosting
Complete static website with custom domain and CloudFront.
AWSTemplateFormatVersion: 2010-09-09
Description: Static website hosting on S3 with CloudFront distribution
Parameters:
DomainName:
Type: String
Description: Your domain name (e.g., example.com)
AllowedPattern: "[a-z0-9-]+\\.[a-z]+"
HostedZoneName:
Type: String
Description: Route 53 hosted zone name
Default: example.com
CertificateArn:
Type: String
Description: ACM certificate ARN for HTTPS
Conditions:
IsRootDomain: !Equals [!Ref DomainName, !Ref HostedZoneName]
Resources:
# S3 Buckets
WebsiteBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref DomainName
PublicAccessBlockConfiguration:
BlockPublicAcls: false
BlockPublicPolicy: false
IgnorePublicAcls: false
RestrictPublicBuckets: false
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: error.html
CorsConfiguration:
CorsRules:
- AllowedHeaders:
- "*"
AllowedMethods:
- GET
AllowedOrigins:
- "*"
MaxAge: 3600
Tags:
- Key: Website
Value: !Ref DomainName
WWWRedirectBucket:
Type: AWS::S3::Bucket
Condition: IsRootDomain
Properties:
BucketName: !Sub www.${DomainName}
AccessControl: Private
WebsiteConfiguration:
RedirectAllRequestsTo:
HostName: !Ref DomainName
Protocol: https
# Bucket Policies
WebsiteBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref WebsiteBucket
PolicyDocument:
Statement:
- Sid: CloudFrontOAI
Effect: Allow
Principal:
CanonicalUser: !GetAtt CloudFrontOAI.S3CanonicalUserId
Action: s3:GetObject
Resource: !Sub ${WebsiteBucket.Arn}/*
# CloudFront
CloudFrontOAI:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub OAI for ${DomainName}
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName: !GetAtt WebsiteBucket.RegionalDomainName
Id: S3Origin
S3OriginConfig:
OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${CloudFrontOAI}
Enabled: true
IPV6Enabled: true
DefaultRootObject: index.html
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
TargetOriginId: S3Origin
ForwardedValues:
QueryString: false
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
Compress: true
CustomErrorResponses:
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /error.html
- ErrorCode: 403
ResponseCode: 200
ResponsePagePath: /error.html
ViewerCertificate:
AcmCertificateArn: !Ref CertificateArn
MinimumProtocolVersion: TLSv1.2_2021
SslSupportMethod: sni-only
# Route 53 Records
DNSRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: !Sub ${HostedZoneName}.
Name: !Ref DomainName
Type: A
AliasTarget:
DNSName: !GetAtt CloudFrontDistribution.DomainName
EvaluateTargetHealth: false
HostedZoneId: !GetAtt CloudFrontDistribution.HostedZoneId
DNSRecordIPv6:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: !Sub ${HostedZoneName}.
Name: !Ref DomainName
Type: AAAA
AliasTarget:
DNSName: !GetAtt CloudFrontDistribution.DomainName
EvaluateTargetHealth: false
HostedZoneId: !GetAtt CloudFrontDistribution.HostedZoneId
Outputs:
WebsiteURL:
Description: URL of the website
Value: !Sub https://${DomainName}
Export:
Name: !Sub ${AWS::StackName}-WebsiteURL
DistributionID:
Description: CloudFront distribution ID
Value: !Ref CloudFrontDistribution
Export:
Name: !Sub ${AWS::StackName}-DistributionIDExample 2: Data Lake with Lifecycle Management
S3 bucket configured for data lake storage with tiered lifecycle.
AWSTemplateFormatVersion: 2010-09-09
Description: S3 bucket for data lake with tiered lifecycle management
Parameters:
BucketName:
Type: String
Description: Name of the data lake bucket
Default: my-data-lake
RawDataRetention:
Type: Number
Description: Days to retain raw data
Default: 90
ProcessedDataRetention:
Type: Number
Description: Days to retain processed data
Default: 365
ArchiveDataRetention:
Type: Number
Description: Days before archiving to Glacier
Default: 30
Resources:
DataLakeBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID: !Ref DataLakeKey
BucketKeyEnabled: true
VersioningConfiguration:
Status: Enabled
LifecycleConfiguration:
Rules:
# Raw data: move to Glacier after 30 days, delete after 90
- Id: RawDataLifecycle
Status: Enabled
PrefixFilter:
Prefix: raw/
Transitions:
- Days: !Ref ArchiveDataRetention
StorageClass: GLACIER
- Days: !Ref RawDataRetention
StorageClass: DEEP_ARCHIVE
NoncurrentVersionExpirationInDays: 30
NoncurrentVersionTransitions:
- NoncurrentDays: 7
StorageClass: GLACIER
# Processed data: move to IA after 30 days, archive after 180
- Id: ProcessedDataLifecycle
Status: Enabled
PrefixFilter:
Prefix: processed/
Transitions:
- Days: 30
StorageClass: STANDARD_IA
- Days: 180
StorageClass: GLACIER
ExpirationInDays: !Ref ProcessedDataRetention
# Analytics output: delete after 90 days
- Id: AnalyticsOutputLifecycle
Status: Enabled
PrefixFilter:
Prefix: analytics/
ExpirationInDays: 90
# Incomplete multipart uploads cleanup
- Id: AbortIncompleteUploads
Status: Enabled
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 7
Tags:
- Key: DataClassification
Value: confidential
- Key: Environment
Value: production
DataLakeKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for data lake encryption
EnableKeyRotation: true
KeyPolicy:
Version: "2012-10-17"
Statement:
- Sid: Enable IAM policies
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action: kms:*
Resource: "*"
- Sid: Allow data lake access
Effect: Allow
Principal:
AWS: !Ref DataLakeRole
Action:
- kms:Encrypt
- kms:Decrypt
- kms:GenerateDataKey
Resource: "*"
DataLakeRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: glue.amazonaws.com
Action: sts:AssumeRole
Outputs:
BucketName:
Description: Name of the data lake bucket
Value: !Ref DataLakeBucket
Export:
Name: !Sub ${AWS::StackName}-BucketName
BucketArn:
Description: ARN of the data lake bucket
Value: !GetAtt DataLakeBucket.Arn
Export:
Name: !Sub ${AWS::StackName}-BucketArnExample 3: Event-Driven Processing Pipeline
S3 bucket with event notifications triggering Lambda functions.
AWSTemplateFormatVersion: 2010-09-09
Description: S3 bucket with Lambda processing pipeline
Parameters:
BucketName:
Type: String
Description: Name of the processing bucket
Default: my-processing-bucket
ProcessingFunctionName:
Type: String
Description: Name of the Lambda function
Default: process-data
Resources:
ProcessingBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
NotificationConfiguration:
LambdaConfigurations:
- Event: s3:ObjectCreated:*
Function: !GetAtt ProcessingFunction.Arn
Filter:
S3Key:
Rules:
- Name: prefix
Value: uploads/
- Event: s3:ObjectRemoved:*
Function: !GetAtt CleanupFunction.Arn
Filter:
S3Key:
Rules:
- Name: suffix
Value: .tmp
CorsConfiguration:
CorsRules:
- AllowedHeaders:
- "*"
AllowedMethods:
- PUT
- POST
AllowedOrigins:
- "*"
MaxAge: 3600
ProcessingFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Ref ProcessingFunctionName
Handler: index.handler
Runtime: python3.9
Code:
S3Bucket: !Ref ProcessingBucket
S3Key: functions/process.zip
Timeout: 300
Role: !GetAtt LambdaRole.Arn
Environment:
Variables:
OUTPUT_BUCKET: !Ref OutputBucket
CleanupFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${ProcessingFunctionName}-cleanup
Handler: cleanup.handler
Runtime: python3.9
Code:
S3Bucket: !Ref ProcessingBucket
S3Key: functions/cleanup.zip
Timeout: 60
Role: !GetAtt LambdaRole.Arn
OutputBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${BucketName}-output
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
- Id: CleanOldOutput
Status: Enabled
ExpirationInDays: 7
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: S3Access
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
- s3:DeleteObject
Resource:
- !Sub ${ProcessingBucket.Arn}
- !Sub ${ProcessingBucket.Arn}/*
- !Sub ${OutputBucket.Arn}
- !Sub ${OutputBucket.Arn}/*
BucketPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref ProcessingFunction
Action: lambda:InvokeFunction
Principal: s3.amazonaws.com
SourceArn: !GetAtt ProcessingBucket.Arn
Outputs:
ProcessingBucketName:
Description: Name of the processing bucket
Value: !Ref ProcessingBucket
Export:
Name: !Sub ${AWS::StackName}-ProcessingBucketNameExample 4: Multi-Environment S3 Configuration
Template with conditions for different environments.
AWSTemplateFormatVersion: 2010-09-09
Description: Multi-environment S3 bucket configuration
Parameters:
Environment:
Type: String
Description: Deployment environment
Default: development
AllowedValues:
- development
- staging
- production
BucketSuffix:
Type: String
Description: Optional suffix for bucket name
Default: ""
EnableVersioning:
Type: String
Default: true
AllowedValues:
- true
- false
EnableLogging:
Type: String
Default: true
AllowedValues:
- true
- false
RetentionDays:
Type: Number
Description: Data retention period in days
Default: 30
Mappings:
EnvironmentConfig:
development:
EnableVersioning: false
EnableLogging: false
RetentionDays: 7
AccessLevel: private
staging:
EnableVersioning: true
EnableLogging: true
RetentionDays: 30
AccessLevel: private
production:
EnableVersioning: true
EnableLogging: true
RetentionDays: 90
AccessLevel: private-log-delivery
Conditions:
ShouldEnableVersioning: !Equals [!Ref EnableVersioning, true]
ShouldEnableLogging: !Equals [!Ref EnableLogging, true]
HasBucketSuffix: !Not [!Equals [!Ref BucketSuffix, ""]]
IsDevelopment: !Equals [!Ref Environment, development]
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !If
- HasBucketSuffix
- !Sub ${Environment}-${BucketSuffix}
- !Sub ${Environment}-data
VersioningConfiguration:
Status: !If [ShouldEnableVersioning, Enabled, Suspended]
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LoggingConfiguration: !If
- ShouldEnableLogging
- DestinationBucketName: !Ref AccessLogBucket
LogFilePrefix: !Sub ${Environment}/
- !Ref AWS::NoValue
LifecycleConfiguration:
Rules:
- Id: DataRetention
Status: Enabled
ExpirationInDays: !Ref RetentionDays
NoncurrentVersionExpirationInDays: 7
Tags:
- Key: Environment
Value: !Ref Environment
- Key: ManagedBy
Value: CloudFormation
AccessLogBucket:
Type: AWS::S3::Bucket
Condition: ShouldEnableLogging
Properties:
BucketName: !Sub ${Environment}-access-logs
AccessControl: LogDeliveryWrite
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
- Id: CleanLogs
Status: Enabled
ExpirationInDays: 30
Outputs:
BucketName:
Description: Name of the data bucket
Value: !Ref DataBucket
Export:
Name: !Sub ${AWS::StackName}-BucketName
Environment:
Description: Deployment environment
Value: !Ref EnvironmentExample 5: S3 Bucket with Object Lock
Bucket configured for compliance with Object Lock.
AWSTemplateFormatVersion: 2010-09-09
Description: S3 bucket with Object Lock for compliance
Parameters:
BucketName:
Type: String
Description: Name of the bucket with Object Lock
RetentionPeriodDays:
Type: Number
Description: Default retention period in days
Default: 365
Resources:
ObjectLockBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
ObjectLockEnabled: true
ObjectLockConfiguration:
ObjectLockEnabled: Enabled
Rule:
DefaultRetention:
Mode: COMPLIANCE
Days: !Ref RetentionPeriodDays
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
VersioningConfiguration:
Status: Enabled
Tags:
- Key: Compliance
Value: enabled
- Key: RetentionPeriod
Value: !Ref RetentionPeriodDays
Outputs:
BucketName:
Description: Name of the Object Lock bucket
Value: !Ref ObjectLockBucket
Export:
Name: !Sub ${AWS::StackName}-BucketName
BucketArn:
Description: ARN of the Object Lock bucket
Value: !GetAtt ObjectLockBucket.Arn
Export:
Name: !Sub ${AWS::StackName}-BucketArnExample 6: Cross-Region Replication
S3 bucket with cross-region replication configuration.
AWSTemplateFormatVersion: 2010-09-09
Description: S3 bucket with cross-region replication
Parameters:
SourceBucketName:
Type: String
Description: Name of the source bucket
DestinationBucketName:
Type: String
Description: Name of the destination bucket
DestinationRegion:
Type: String
Description: Destination region
Default: us-west-2
Resources:
SourceBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref SourceBucketName
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
ReplicationConfiguration:
Role: !GetAtt ReplicationRole.Arn
Rules:
- Id: ReplicateToDestRegion
Status: Enabled
Priority: 1
Filter:
Prefix: ""
Destination:
Bucket: !Sub arn:aws:s3:::${DestinationBucketName}
StorageClass: STANDARD_IA
EncryptionConfiguration:
ReplicaKmsKeyID: !Ref DestKMSKey
Account: !Ref DestinationAccountId
ReplicationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: s3.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ReplicationPolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:GetReplicationConfiguration
- s3:GetObjectVersion
- s3:GetObjectVersionAcl
- s3:GetObjectVersionTagging
Resource:
- !Sub ${SourceBucket.Arn}
- !Sub ${SourceBucket.Arn}/*
- Effect: Allow
Action:
- s3:ReplicateObject
- s3:ReplicateDelete
- s3:ObjectOwnerOverrideToBucketOwner
- s3:ReplicateTags
Resource:
- !Sub arn:aws:s3:::${DestinationBucketName}
- !Sub arn:aws:s3:::${DestinationBucketName}/*
- Effect: Allow
Action:
- kms:Decrypt
- kms:Encrypt
- kms:GenerateDataKey
Resource: !Ref SourceKMSKey
Condition:
StringEquals:
kms:ViaService: !Sub s3.${AWS::Region}.amazonaws.com
- Effect: Allow
Action:
- kms:Decrypt
- kms:Encrypt
- kms:GenerateDataKey
Resource: !Ref DestKMSKey
Condition:
StringEquals:
kms:ViaService: !Sub s3.${DestinationRegion}.amazonaws.com
SourceKMSKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for source bucket encryption
DestKMSKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for destination bucket encryption
Outputs:
SourceBucketName:
Description: Name of the source bucket
Value: !Ref SourceBucket
Export:
Name: !Sub ${AWS::StackName}-SourceBucketName
DestinationBucketName:
Description: Name of the destination bucket
Value: !Ref DestinationBucketNameExample 7: S3 Inventory and Analytics
Bucket with analytics and inventory configurations.
AWSTemplateFormatVersion: 2010-09-09
Description: S3 bucket with analytics and inventory configurations
Parameters:
BucketName:
Type: String
Description: Name of the bucket
Resources:
AnalyticsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
AnalyticsConfigurations:
- Id: AnalyticsConfig
StorageClassAnalysis:
DataExport:
Destination:
S3BucketDestination:
Bucket: !Ref AnalyticsReportBucket
BucketAccountId: !Ref AWS::AccountId
Format: CSV
Prefix: analytics/
InventoryConfigurations:
- Id: DailyInventory
IncludedObjectVersions: Current
Schedule:
Frequency: Daily
Destination:
S3BucketDestination:
Bucket: !Ref InventoryReportBucket
BucketAccountId: !Ref AWS::AccountId
Format: CSV
Prefix: inventory/
Filter:
Prefix: data/
OptionalFields:
- Size
- LastModifiedDate
- StorageClass
- ETag
- IsMultipartUploaded
AnalyticsReportBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${BucketName}-analytics-reports
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
InventoryReportBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${BucketName}-inventory-reports
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
- Id: CleanOldReports
Status: Enabled
ExpirationInDays: 90
Outputs:
BucketName:
Description: Name of the analytics bucket
Value: !Ref AnalyticsBucketAWS CloudFormation S3 - Reference
This reference guide contains detailed information about AWS CloudFormation resources, intrinsic functions, and configurations for S3 infrastructure.
AWS::S3::Bucket
Creates an Amazon S3 bucket.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| AccelerateConfiguration | AccelerateConfiguration | No | Configures bucket acceleration |
| AccessControl | String | No | A canned ACL (Private, PublicRead, etc.) |
| AnalyticsConfigurations | List | No | Analytics configurations for inventory reports |
| BucketEncryption | BucketEncryption | No | Server-side encryption configuration |
| BucketName | String | No | Name of the bucket |
| CorsConfiguration | CorsConfiguration | No | CORS rules for cross-origin requests |
| EventBridgeConfiguration | EventBridgeConfiguration | No | EventBridge configuration |
| IntelligentTieringConfiguration | IntelligentTieringConfiguration | No | S3 Intelligent-Tiering configuration |
| InventoryConfigurations | List | No | Inventory configurations |
| LifecycleConfiguration | LifecycleConfiguration | No | Lifecycle rules for object management |
| LoggingConfiguration | LoggingConfiguration | No | Server access logging configuration |
| MetricsConfiguration | MetricsConfiguration | No | CloudWatch metrics configuration |
| NotificationConfiguration | NotificationConfiguration | No | Event notification configuration |
| ObjectLockConfiguration | ObjectLockConfiguration | No | Object Lock configuration |
| ObjectLockEnabled | Boolean | No | Whether Object Lock is enabled |
| OwnershipControls | OwnershipControls | No | Bucket ownership controls |
| PublicAccessBlockConfiguration | PublicAccessBlockConfiguration | No | Block public access settings |
| ReplicationConfiguration | ReplicationConfiguration | No | Cross-region replication rules |
| Tags | List | No | Tags assigned to the bucket |
| VersioningConfiguration | VersioningConfiguration | No | Versioning status |
| WebsiteConfiguration | WebsiteConfiguration | No | Static website hosting configuration |
VersioningConfiguration
VersioningConfiguration:
Status: Enabled | Suspended
MFADelete: Enabled | Disabled # OptionalCorsConfiguration
CorsConfiguration:
CorsRules:
- AllowedHeaders:
- "*"
AllowedMethods:
- GET
- PUT
- POST
- DELETE
- HEAD
AllowedOrigins:
- "https://example.com"
ExposedHeaders:
- ContentLength
- Date
MaxAge: 3600LifecycleConfiguration
LifecycleConfiguration:
Rules:
- ID: string
Status: Enabled | Disabled
PrefixFilter:
Prefix: logs/
TagFilter:
- Key: Environment
Value: production
ExpirationInDays: 30
ExpirationDate: "2024-12-31T00:00:00.000Z"
Transitions:
- Days: 30
StorageClass: STANDARD_IA | GLACIER | DEEP_ARCHIVE
- Days: 90
StorageClass: GLACIER
NoncurrentVersionExpirationInDays: 7
NoncurrentVersionTransitions:
- NoncurrentDays: 30
StorageClass: STANDARD_IALoggingConfiguration
LoggingConfiguration:
DestinationBucketName: !Ref LogBucket
LogFilePrefix: logs/
LogFilePrefix: !Sub ${AWS::StackName}/logs/BucketEncryption
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256 | aws:kms | aws:kms:dsse
KMSMasterKeyID: !Ref KMSKeyArn
BucketKeyEnabled: truePublicAccessBlockConfiguration
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: trueNotificationConfiguration
NotificationConfiguration:
LambdaConfigurations:
- Event: s3:ObjectCreated:*
Function: !GetAtt ProcessingFunction.Arn
Filter:
S3Key:
Rules:
- Name: prefix
Value: uploads/
- Event: s3:ObjectRemoved:*
Function: !GetAtt CleanupFunction.Arn
QueueConfigurations:
- Event: s3:ObjectCreated:*
Queue: !Ref EventQueue
Filter:
S3Key:
Rules:
- Name: suffix
Value: .log
TopicConfigurations:
- Event: s3:ObjectCreated:*
Topic: !Ref EventTopicReplicationConfiguration
ReplicationConfiguration:
Role: !GetAtt ReplicationRole.Arn
Rules:
- ID: string
Status: Enabled | Disabled
Priority: 1
Filter:
Prefix: ""
And:
Prefix: ""
Tags:
- Key: Key
Value: Value
Destination:
Bucket: arn:aws:s3:::destination-bucket
Account: destination-account-id
StorageClass: STANDARD | STANDARD_IA | INTELLIGENT_TIERING
EncryptionConfiguration:
ReplicaKmsKeyID: kms-key-arn
AccessControlTranslation:
Owner: Destination
Account: account-id
Metrics:
Status: Enabled
EventThreshold:
Minutes: 15
ReplicationTime:
Status: Enabled
Time:
Minutes: 15
SourceSelectionCriteria:
SseKmsEncryptedObjects:
Status: EnabledWebsiteConfiguration
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: error.html
RoutingRules:
- Condition:
KeyPrefixEquals: docs/
Redirect:
ReplaceKeyWith: documents/index.html
- Condition:
HttpErrorCodeReturnedEquals: 404
Redirect:
Protocol: https
HostName: example.com
ReplaceKeyWith: 404.htmlAttributes
| Attribute | Description |
|---|---|
| Arn | The Amazon Resource Name (ARN) of the bucket |
| DomainName | The DNS name of the bucket |
| DualStackDomainName | The DNS name of the bucket when using IPv6 |
| RegionalDomainName | The regional domain name of the bucket |
| WebsiteURL | URL of the website endpoint |
| S3CanonicalUserId | The canonical user ID for the bucket owner |
Examples
Basic Bucket
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-data-bucketBucket with Versioning and Logging
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-data-bucket
VersioningConfiguration:
Status: Enabled
LoggingConfiguration:
DestinationBucketName: !Ref LogBucket
LogFilePrefix: logs/
Tags:
- Key: Environment
Value: productionBucket with Lifecycle Rules
Resources:
LifecycleBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-lifecycle-bucket
LifecycleConfiguration:
Rules:
- Id: ArchiveOldData
Status: Enabled
PrefixFilter:
Prefix: archive/
Transitions:
- Days: 30
StorageClass: GLACIER
ExpirationInDays: 365Bucket with CORS
Resources:
CorsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-cors-bucket
CorsConfiguration:
CorsRules:
- AllowedHeaders:
- Authorization
- Content-Type
AllowedMethods:
- GET
- PUT
- POST
AllowedOrigins:
- "https://example.com"
- "https://*.example.com"
MaxAge: 3600AWS::S3::BucketPolicy
Applies a bucket policy to an Amazon S3 bucket.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| Bucket | String | Yes | Name of the bucket to apply the policy to |
| PolicyDocument | PolicyDocument | Yes | Policy to apply |
PolicyDocument Structure
PolicyDocument:
Version: "2012-10-17" | "2008-10-17"
Id: policy-id
Statement:
- Sid: statement-id
Effect: Allow | Deny
Principal:
AWS: arn:aws:iam::account-id:user/user-name
Service: service-name.amazonaws.com
CanonicalUser: canonical-user-id
"*": # All principals
Action:
- s3:GetObject
- s3:PutObject
- s3:DeleteObject
NotAction:
- s3:*
Resource:
- arn:aws:s3:::bucket-name
- arn:aws:s3:::bucket-name/*
NotResource:
- arn:aws:s3:::bucket-name/secret/*
Condition:
ConditionOperator:
ConditionKey: condition-valueCondition Operators
| Operator | Description |
|---|---|
| StringEquals | Exact string match |
| StringNotEquals | Negated string match |
| StringLike | String with wildcards |
| StringNotLike | Negated string with wildcards |
| NumericEquals | Exact number match |
| NumericNotEquals | Negated number match |
| NumericLessThan | Less than comparison |
| NumericLessThanEquals | Less than or equal |
| NumericGreaterThan | Greater than comparison |
| NumericGreaterThanEquals | Greater than or equal |
| Bool | Boolean comparison |
| IpAddress | IP address range |
| NotIpAddress | Excluded IP address |
| ArnEquals | ARN match |
| ArnLike | ARN with wildcards |
Common Condition Keys
| Key | Description |
|---|---|
| aws:sourceVpce | VPC endpoint ID |
| aws:sourceVpc | VPC ID |
| aws:PrincipalAccount | Principal's account ID |
| aws:PrincipalArn | Principal's ARN |
| aws:SecureTransport | Whether request uses HTTPS |
| s3:prefix | Object key prefix |
| s3:Delimiter | Delimiter for listing |
| s3:max-keys | Max keys in listing |
Examples
Allow Access from VPC Endpoint
Resources:
PrivateBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-private-bucket
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref PrivateBucket
PolicyDocument:
Statement:
- Sid: AllowVPCEndpoint
Effect: Allow
Principal: "*"
Action: s3:GetObject
Resource: !Sub ${PrivateBucket.Arn}/*
Condition:
StringEquals:
aws:sourceVpce: !Ref VPCEndpointIdDeny Unencrypted Uploads
Resources:
SecureBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-secure-bucket
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref SecureBucket
PolicyDocument:
Statement:
- Sid: DenyUnencryptedUploads
Effect: Deny
Principal: "*"
Action: s3:PutObject
Resource: !Sub ${SecureBucket.Arn}/*
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption: AES256
- Sid: DenyKMSUnencryptedUploads
Effect: Deny
Principal: "*"
Action: s3:PutObject
Resource: !Sub ${SecureBucket.Arn}/*
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption: aws:kms
Null:
s3:x-amz-server-side-encryption-aws-kms-key-id: falseAllow CloudFront OAI Access
Resources:
WebsiteBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-website-bucket
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: error.html
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref WebsiteBucket
PolicyDocument:
Statement:
- Sid: CloudFrontReadAccess
Effect: Allow
Principal:
CanonicalUser: !GetAtt CloudFrontOAI.S3CanonicalUserId
Action: s3:GetObject
Resource: !Sub ${WebsiteBucket.Arn}/*
CloudFrontOAI:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: Website OAICross-Account Access
Resources:
SharedBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-shared-bucket
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref SharedBucket
PolicyDocument:
Statement:
- Sid: CrossAccountRead
Effect: Allow
Principal:
AWS:
- arn:aws:iam::123456789012:role/ReadRole
- arn:aws:iam::123456789012:user/ReadUser
Action:
- s3:GetObject
- s3:GetObjectVersion
Resource: !Sub ${SharedBucket.Arn}/*
- Sid: CrossAccountWrite
Effect: Allow
Principal:
AWS: arn:aws:iam::123456789012:role/WriteRole
Action:
- s3:PutObject
Resource: !Sub ${SharedBucket.Arn}/*Intrinsic Functions
Fn::Ref
Returns the bucket name.
BucketName: !Ref DataBucketFn::GetAtt
Returns bucket attributes.
BucketArn: !GetAtt DataBucket.Arn
BucketDomainName: !GetAtt DataBucket.DomainName
WebsiteURL: !GetAtt DataBucket.WebsiteURL
S3CanonicalUserId: !GetAtt DataBucket.S3CanonicalUserIdFn::Sub
Substitutes variables in an input string with values.
BucketArn: !Sub "arn:aws:s3:::${BucketName}"Fn::Join
Appends a set of values into a single value.
Resource: !Join
- ""
- - "arn:aws:s3:::"
- !Ref BucketName
- "/*"Fn::ImportValue
Imports an output value exported by another stack.
BucketArn: !ImportValue storage-stack-BucketArnBest Practices
Security
1. Block Public Access: Always enable block public access settings 2. Use Bucket Policies: Define explicit access controls 3. Enable Versioning: Protect against accidental deletion 4. Use Encryption: Enable server-side encryption 5. Use VPC Endpoints: Keep traffic within AWS network
Cost Optimization
1. Lifecycle Rules: Move data to cheaper storage classes 2. Intelligent-Tiering: Use for unpredictable access patterns 3. Delete Old Versions: Clean up noncurrent versions 4. Monitor with Metrics: Track storage usage
Performance
1. Use Prefixes: Distribute objects across prefixes for parallelism 2. Enable Transfer Acceleration: For faster global uploads 3. Use Multi-Region Access Points: For low-latency access