复用CloudFormation模板适配已有/新建S3桶并配置跨区域复制
解决CloudFormation模板复用问题:存在S3桶则仅配置复制规则,不存在则创建并配置
原模板问题分析
- 直接声明
AWS::S3::Bucket资源,当目标桶已存在时,CloudFormation会因桶名全局唯一报错,无法跳过创建步骤 - 复制配置的目标桶ARN末尾缺少闭合单引号,属于语法错误
- IAM复制策略的资源权限范围不合理,复制操作仅需目标桶对象权限,无需源桶对象权限
- 桶策略仅限制了桶本身的非HTTPS访问,未覆盖桶内对象
解决方案思路
通过Lambda-backed自定义资源检查目标桶是否存在,结合CloudFormation条件判断,实现:
- 桶不存在:创建桶+配置版本控制+复制规则+桶策略
- 桶已存在:跳过桶创建,直接为现有桶配置复制规则和桶策略(前提是现有桶已启用版本控制)
修改后的完整模板
AWSTemplateFormatVersion: 2010-09-09 Parameters: TargetBucketName: Type: String Default: test-source-us-east-1 SourceBucketARN: Type: String Default: arn:aws:s3:::test-source-us-west-2 Resources: # Lambda函数:检查目标桶是否存在及版本控制状态 CheckBucketExistsFunction: Type: AWS::Lambda::Function Properties: Runtime: python3.12 Handler: index.lambda_handler Role: !GetAtt CheckBucketRole.Arn Code: ZipFile: | import boto3 import cfnresponse def lambda_handler(event, context): s3 = boto3.client('s3', region_name='us-east-1') bucket_name = event['ResourceProperties']['BucketName'] response_data = {} try: # 检查桶是否存在 s3.head_bucket(Bucket=bucket_name) # 检查版本控制是否启用 versioning = s3.get_bucket_versioning(Bucket=bucket_name) if versioning.get('Status') != 'Enabled': response_data['Exists'] = 'false' response_data['Error'] = 'Bucket exists but versioning is not enabled' cfnresponse.send(event, context, cfnresponse.FAILED, response_data) return response_data['Exists'] = 'true' cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data) except s3.exceptions.ClientError as e: if e.response['Error']['Code'] == '404': response_data['Exists'] = 'false' cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data) else: response_data['Error'] = str(e) cfnresponse.send(event, context, cfnresponse.FAILED, response_data) # Lambda执行角色 CheckBucketRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: S3CheckAccess PolicyDocument: Statement: - Effect: Allow Action: - s3:HeadBucket - s3:GetBucketVersioning Resource: !Sub arn:aws:s3:::${TargetBucketName} - Effect: Allow Action: logs:* Resource: arn:aws:logs:*:*:* # 自定义资源:触发桶检查 CheckBucketExists: Type: AWS::CloudFormation::CustomResource Properties: ServiceToken: !GetAtt CheckBucketExistsFunction.Arn BucketName: !Ref TargetBucketName # 条件判断:仅当桶不存在时创建桶 BucketDoesNotExist: Condition: !Equals [!GetAtt CheckBucketExists.Exists, 'false'] # 创建目标桶(仅当桶不存在时执行) TargetS3Bucket: Type: AWS::S3::Bucket Condition: BucketDoesNotExist UpdateReplacePolicy: Retain Properties: BucketName: !Ref TargetBucketName VersioningConfiguration: Status: Enabled PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true # 复制角色 ReplicationRole: Type: AWS::IAM::Role DeletionPolicy: Retain Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: s3.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: S3ReplicationPolicy PolicyDocument: Statement: # 源桶权限:读取版本对象、列表桶等 - Effect: Allow Action: - s3:ListBucket - s3:GetReplicationConfiguration - s3:GetObjectVersionForReplication - s3:GetObjectVersionAcl - s3:GetObjectVersionTagging - s3:GetObjectRetention - s3:GetObjectLegalHold Resource: - !Sub arn:aws:s3:::${TargetBucketName} - !Sub arn:aws:s3:::${TargetBucketName}/* # 目标桶权限:复制对象、删除等 - Effect: Allow Action: - s3:ReplicateObject - s3:ReplicateDelete - s3:ReplicateTags - s3:ObjectOwnerOverrideToBucketOwner Resource: !Sub ${SourceBucketARN}/* # 复制配置:兼容已存在的桶 BucketReplicationConfig: Type: AWS::S3::BucketReplicationConfiguration Properties: Bucket: !Ref TargetBucketName Role: !GetAtt ReplicationRole.Arn Rules: - Id: CrossRegionReplicationToUsWest2 Status: Enabled DeleteMarkerReplication: Status: Enabled Priority: 1 Filter: Prefix: '' Destination: Bucket: !Ref SourceBucketARN # 桶策略:强制HTTPS访问,兼容已存在的桶 TargetBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref TargetBucketName PolicyDocument: Version: "2012-10-17" Statement: - Effect: Deny Principal: "*" Action: s3:* Resource: - !Sub arn:aws:s3:::${TargetBucketName} - !Sub arn:aws:s3:::${TargetBucketName}/* Condition: Bool: aws:SecureTransport: "false"
关键修改说明
- 自定义资源检查桶状态:通过Lambda检查目标桶是否存在,同时验证版本控制是否启用(跨区域复制必须要求版本控制开启)
- 条件创建桶:用
Condition控制AWS::S3::Bucket资源的创建,仅当桶不存在时执行 - 独立复制配置资源:使用
AWS::S3::BucketReplicationConfiguration单独管理复制规则,而非嵌套在桶资源中,这样即使桶已存在也能直接配置 - 优化IAM权限:严格区分源桶和目标桶的权限范围,避免过度授权
- 修复语法错误:补全目标桶ARN的闭合单引号,完善桶策略的资源范围
注意事项
- 若现有桶未启用版本控制,模板会部署失败,需先手动开启版本控制
- 确保执行CloudFormation的角色拥有足够权限:创建Lambda、IAM角色、S3桶及配置复制规则的权限
- 首次部署后,若后续复用模板,CloudFormation会自动跳过桶创建步骤,仅检查并更新复制规则和桶策略
内容的提问来源于stack exchange,提问作者EnergeticBlue
相关产品推荐
相关产品推荐

