Skip to content

Automating S3 Block Public Access Settings Across Your Organization

3 minute read
Content level: Foundational
0

A practical guide to implementing account-level S3 Block Public Access using CloudFormation StackSets

The Challenge

Organizations with multiple AWS accounts need consistent S3 security posture to prevent accidental public exposure of sensitive data. Manual configuration of S3 Block Public Access settings across hundreds of accounts becomes impractical and creates security gaps. Organizations need automated deployment that provides organization-wide protection regardless of individual bucket configurations.

This best practice is supported by CIS AWS Foundations Benchmark v3.0.0 S3.1 control, which requires account-level block public access settings for S3 general purpose buckets.

Account-Level vs Bucket-Level Protection

S3 Block Public Access operates at two levels: account-level and bucket-level. Account-level settings provide superior protection by overriding all bucket-level configurations, policies, and ACLs. This creates a security boundary that prevents public access regardless of individual bucket settings or misconfigurations.

Account-level settings effectively satisfy both CIS S3.1 (account-level) and S3.8 (bucket-level) requirements since the account-level configuration takes precedence.

Implementation Approach

CloudFormation StackSets provide scalable deployment using Lambda-backed custom resources to call the S3 Control API. AWS CloudFormation lacks native support for account-level S3 Block Public Access settings, making custom resources the standard implementation approach. The solution deploys consistently across all accounts and automatically applies to new accounts when configured with automatic deployment.

CloudFormation Template

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  S3AccountPublicAccessBlock:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt S3PublicAccessBlockFunction.Arn
      
  S3PublicAccessBlockFunction:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.12
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          import boto3
          import cfnresponse
          def handler(event, context):
              try:
                  s3control = boto3.client('s3control')
                  account_id = context.invoked_function_arn.split(':')[4]
                  if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
                      s3control.put_public_access_block(
                          AccountId=account_id,
                          PublicAccessBlockConfiguration={
                              'BlockPublicAcls': True,
                              'IgnorePublicAcls': True,
                              'BlockPublicPolicy': True,
                              'RestrictPublicBuckets': True
                          }
                      )
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
              except Exception as e:
                  print(f"Error: {str(e)}")
                  cfnresponse.send(event, context, cfnresponse.FAILED, {})
                  
  LambdaExecutionRole:
    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: S3ControlPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutAccountPublicAccessBlock
                  - s3:GetAccountPublicAccessBlock
                Resource: '*'

Deployment Strategy

Deploy using CloudFormation StackSets from the management account or delegated administrator. Target organizational units based on governance requirements and enable automatic deployment for new accounts.

# Create StackSet
aws cloudformation create-stack-set \
  --stack-set-name s3-block-public-access-stackset \
  --template-body file://s3-block-public-access.yaml \
  --capabilities CAPABILITY_IAM

# Deploy to organization
aws cloudformation create-stack-instances \
  --stack-set-name s3-block-public-access-stackset \
  --deployment-targets OrganizationalUnitIds=r-org-id \
  --regions us-east-1 \
  --operation-preferences MaxConcurrentPercentage=100

Operations and Maintenance

Account-level Block Public Access settings are persistent and require no ongoing maintenance once configured. The settings automatically apply to all existing and future S3 buckets in the account, providing comprehensive protection without per-bucket configuration.

AWS Security Hub provides built-in detective controls to monitor accounts without proper S3 Block Public Access configuration through CIS compliance checks.

Limitations and Alternatives

This approach creates Lambda functions in each account, which may be unnecessary overhead for one-time configuration.

For one-time setup, AWS CLI provides simpler deployment without per-account resources:

aws s3control put-public-access-block \
  --account-id 123456789012 \
  --public-access-block-configuration \
  BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

CloudFormation StackSets provide ongoing management and automatic application to new accounts, while CLI offers simpler one-time deployment for existing accounts.