S3 public access should be blocked at account level
Individual bucket policies and ACLs can be misconfigured by any IAM principal with sufficient permissions. The account-level Public Access Block overrides bucket-level settings, so no bucket in the account can accidentally or intentionally expose objects to the internet. Without it, a single overly permissive s3:PutBucketPolicy call can make sensitive data publicly readable.
Exposed S3 buckets have caused some of the most expensive data breaches in cloud security. Account-level blocking is a single Terraform resource that removes this risk across every bucket in the account, present and future, without ongoing maintenance.
Retrofit consideration
Enabling account-level public access block immediately breaks any bucket intentionally serving public content, static websites or public datasets. Before enabling, use aws s3api list-buckets to enumerate accounts and check each bucket for public policies or ACLs. Fix or migrate those workloads first.
Implementation
Choose the approach that matches how you manage Terraform.
Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.
module "s3_bucket" {
source = "soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "pcidss.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "hipaa.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "nist80053.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "nistcsf.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "fedrampmoderate.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "cisv80ig1.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "nist800171.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "cisacyberessentials.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "nydfs23.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "ffiec.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "acscessentialeight.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "cfrpart11.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "fedramplow.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "hipaasecurity2003.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "nistcsfv11.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "nist80053rev4.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
module "s3_bucket" {
source = "pcidssv321.compliance.tf/terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
}
If you use terraform-aws-modules/s3-bucket/aws//modules/account-public-access, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws//modules/account-public-access"
version = ">=5.0.0"
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
Use AWS provider resources directly. See docs for the resources involved: aws_s3_account_public_access_block.
resource "aws_s3_account_public_access_block" "this" {
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
What this control checks
This control validates the aws_s3_account_public_access_block resource. To pass, declare one resource of this type with all four arguments set to true:
block_public_acls: rejects PUT requests that include a public ACL.ignore_public_acls: causes S3 to ignore all public ACLs on buckets and objects in the account.block_public_policy: rejects PUT bucket policy calls if the policy grants public access.restrict_public_buckets: restricts access to buckets with public policies to AWS service principals and authorized users only.
If any argument is false, omitted (all four default to false), or the resource is missing entirely, the control fails. The account_id argument is optional; when omitted, the resource applies to the caller's account.
Common pitfalls
Default values are false
All four arguments on
aws_s3_account_public_access_blockdefault tofalse. Simply declaring the resource without setting each one totruewill passterraform applybut fail this control. Set all four explicitly, every time.Conflicts with public-facing S3 static websites
Account-level blocking overrides bucket-level settings with no exceptions. If any bucket uses S3 static website hosting with public read access, enabling
restrict_public_bucketsandblock_public_policywill break it immediately. Move those workloads to CloudFront with an Origin Access Control before enabling the account block.Multiple Terraform stacks managing the same account
The
aws_s3_account_public_access_blockresource is a singleton per AWS account. Manage it in exactly one state file, your account-baseline stack, not in each service stack. Two stacks managing the same resource compete on every apply, causing drift and potential state corruption.Cross-account access not blocked by restrict_public_buckets
restrict_public_bucketslimits access to buckets with public policies, but explicit cross-account bucket policies that don't grant public principals are unaffected. This control addresses public access only. Cross-account sharing requires separate controls.
Audit evidence
An auditor expects aws s3control get-public-access-block --account-id <ACCOUNT_ID> showing all four fields as true. The Config rule s3-account-level-public-access-blocks-periodic provides continuous evaluation; look for COMPLIANT status. S3 console screenshots of the 'Block Public Access settings for this account' page with all four settings enabled are also accepted.
For multi-account environments, an AWS Config aggregator or Security Hub consolidated findings page showing compliance across all member accounts gives auditors the cross-account view they need.
Framework-specific interpretation
SOC 2: CC6.1 and CC6.6 call for logical access controls and defined system boundaries. The account-level block is the S3 boundary control: it prevents any bucket in the account from becoming publicly accessible regardless of what individual resource owners configure.
PCI DSS v4.0: Cardholder data in S3 must not be publicly accessible, which falls under Requirement 7's access restriction controls and Requirement 1's network boundary requirements. Account-level blocking enforces this even when individual bucket policies are misconfigured.
HIPAA Omnibus Rule 2013: PHI stored in S3 must not be reachable by unauthorized individuals. The HIPAA Omnibus Rule's access control and audit requirements are supported by blocking public access at the account level, which ensures no bucket-level misconfiguration can expose health data to the internet.
NIST SP 800-53 Rev 5: AC-3 and AC-4 both apply here. AC-3 requires enforcement of access decisions; AC-4 requires information flow controls. The account-level block satisfies both by preventing any S3-to-internet data flow regardless of individual bucket configuration.
NIST Cybersecurity Framework v2.0: S3 account-level public access blocking maps to PR.DS (Data Security) and PR.AA (Access Control). Removing the possibility of public grants at the account level is exactly the kind of preventive data-at-rest control PR.DS expects.
FedRAMP Moderate Baseline Rev 4: At the Moderate baseline, AC-3 and AC-6 require access enforcement and least privilege for federal systems. Account-level public access blocking is a preventive control that bucket owners within the account cannot override, which is what FedRAMP evaluators want to see.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
s3_public_access_block_accountAWS Config Managed Rules:
S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS,S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS_PERIODICPowerpipe Controls:
aws_compliance.control.s3_public_access_block_account,aws_compliance.control.s3_public_access_block_bucket_accountProwler Check:
s3_account_level_public_access_blocksAWS Security Hub Controls:
S3.1,S3.8
Last reviewed: 2026-03-09