Loading...
Loading...
Compare original and translation side by side
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-projectAWSTemplateFormatVersion: 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-projectAWSTemplateFormatVersion: 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.ArnAWSTemplateFormatVersion: 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.ArnAWSTemplateFormatVersion: 2010-09-09 # Required - template version
Description: Optional description string # Optional descriptionAWSTemplateFormatVersion: 2010-09-09 # 必填 - 模板版本
Description: Optional description string # 可选描述undefinedundefinedAWSTemplateFormatVersion2010-09-09AWSTemplateFormatVersion: 2010-09-09
Description: My S3 CloudFormation TemplateAWSTemplateFormatVersion2010-09-09AWSTemplateFormatVersion: 2010-09-09
Description: My S3 CloudFormation TemplateAWSTemplateFormatVersion: 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 loggingAWSTemplateFormatVersion: 2010-09-09
Description: >
本模板创建一个启用版本控制的S3存储桶
以实现数据保护,包含以下内容:
- 带版本控制配置的存储桶
- 用于数据保留的生命周期规则
- 服务器访问日志MetadataMetadata:
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 VersioningMetadataMetadata:
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 VersioningResourcesResources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-data-bucket
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: trueResourcesResources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-data-bucket
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: trueParameters:
ExistingBucketName:
Type: AWS::S3::Bucket::Name
Description: Select an existing S3 bucket
BucketNamePrefix:
Type: String
Description: Prefix for new bucket namesParameters:
ExistingBucketName:
Type: AWS::S3::Bucket::Name
Description: Select an existing S3 bucket
BucketNamePrefix:
Type: String
Description: Prefix for new bucket namesParameters:
LatestBucketPolicy:
Type: AWS::SSM::Parameter::Value<String>
Description: Latest bucket policy from SSM
Default: /s3/bucket-policy/latestParameters:
LatestBucketPolicy:
Type: AWS::SSM::Parameter::Value<String>
Description: Latest bucket policy from SSM
Default: /s3/bucket-policy/latestParameters:
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 productionParameters:
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 productionMappingsMappings:
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]MappingsMappings:
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]ConditionsParameters:
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: 30ConditionsParameters:
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: 30TransformAWSTemplateFormatVersion: 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}-imagesTransformAWSTemplateFormatVersion: 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:
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.WebsiteURLOutputs:
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.WebsiteURLOutputs:
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}-BucketRegionOutputs:
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}-BucketRegionParameters:
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-BucketArnParameters:
DataBucketName:
Type: AWS::S3::Bucket::Name
Description: Data bucket name from data stack
# 用户可在控制台中从导出值中选择
# 或使用Fn::ImportValue进行程序化访问
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-BucketArnundefinedundefined
Application stack imports these values:
```yaml
应用栈导入这些值:
```yamlundefinedundefinedResources:
SecureBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-secure-bucket
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: trueResources:
SecureBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-secure-bucket
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: trueResources:
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 MFAResources:
VersionedBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-versioned-bucket
VersioningConfiguration:
Status: Enabled
# 使用MFADelete要求删除版本时需MFA验证
# MFADelete: Enabled # 可选,需要MFAResources:
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: GLACIERResources:
LifecycleBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-lifecycle-bucket
LifecycleConfiguration:
Rules:
# 30天后过期对象
- Id: ExpireOldObjects
Status: Enabled
ExpirationInDays: 30
NoncurrentVersionExpirationInDays: 7
# 90天后归档到Glacier
- Id: ArchiveToGlacier
Status: Enabled
Transitions:
- Days: 90
StorageClass: GLACIER
- Days: 365
StorageClass: DEEP_ARCHIVE
NoncurrentVersionTransitions:
- NoncurrentDays: 30
StorageClass: GLACIERResources:
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:AssumeRoleResources:
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:AssumeRoleResources:
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: falseResources:
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: falseResources:
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}-oaiResources:
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}-oaiResources:
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 VPCEndpointIdResources:
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 VPCEndpointIdAWSTemplateFormatVersion: 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 AccessLogBucketAWSTemplateFormatVersion: 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 AccessLogBucket{
"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"]
}
}
}
]
}{
"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"]
}
}
}
]
}aws cloudformation set-stack-policy \
--stack-name my-s3-stack \
--stack-policy-body file://stack-policy.jsonaws cloudformation set-stack-policy \
--stack-name my-s3-stack \
--stack-policy-body file://stack-policy.json{
"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": "*"
}
]
}{
"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": "*"
}
]
}undefinedundefinedundefinedundefinedimport 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:
raiseimport 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:
raise#!/bin/bash#!/bin/bashundefinedundefinedundefinedundefinedundefinedundefined#!/bin/bash#!/bin/bashecho "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 tableundefinedecho "Drift report saved to: $REPORT_FILE"
# 显示摘要
aws cloudformation describe-stack-resource-drifts \
--stack-name $STACK_NAME \
--query 'StackResourceDrifts[?DriftStatus==`MODIFIED`].[LogicalResourceId,ResourceType,PropertyDifferences[].PropertyName]' \
--output tableundefinedimport 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_resultsimport boto3
from datetime import datetime
def detect_drift_all_stacks(prefix="prod-"):
cfn = boto3.client('cloudformation')
s3 = boto3.client('s3')
# 列出所有带指定前缀的栈
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}")
# 启动漂移检测
response = cfn.detect_drift(StackName=stack_name)
detection_id = response['Id']
# 等待完成(简化版,生产环境应使用waiter)
waiter = cfn.get_waiter('stack_drift_detection_complete')
waiter.wait(StackName=stack_name)
# 获取状态
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':
# 获取详细漂移信息
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_results#!/bin/bash#!/bin/bashecho "Waiting for stack update to complete..."
aws cloudformation wait stack-update-complete \
--stack-name $STACK_NAME
echo "Stack update complete!"undefinedecho "Waiting for stack update to complete..."
aws cloudformation wait stack-update-complete \
--stack-name $STACK_NAME
echo "Stack update complete!"undefinedundefinedundefinedundefinedundefinedimport 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:
passimport 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:
# 创建变更集
kwargs = {
'StackName': stack_name,
'TemplateBody': template_body,
'ChangeSetName': changeset_name,
'ChangeSetType': 'UPDATE'
}
if parameters:
kwargs['Parameters'] = parameters
response = cfn.create_change_set(**kwargs)
# 等待创建完成
waiter = cfn.get_waiter('change_set_create_complete')
waiter.wait(StackName=stack_name, ChangeSetName=changeset_name)
# 获取变更集描述
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', []))}")
# 显示每个变更
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:
# 清理变更集
try:
cfn.delete_change_set(
StackName=stack_name,
ChangeSetName=changeset_name
)
except Exception:
passundefinedundefinedundefinedundefined