1001Ferramentas
☁️Generators

AWS S3 IAM Policy Generator

Generate an IAM JSON policy for AWS S3: read-only, write, full access or per-bucket custom. IAM-paste-ready.


  

S3 Bucket Policies: resource-based JSON, principals, conditions and the patterns that matter

An S3 bucket policy is a JSON document attached directly to a bucket — a resource-based policy, distinct from an IAM identity-based policy that lives on a user/role. The syntax is almost identical to IAM (Version, Statement, Effect, Action, Resource, Condition) with one critical addition: Principal is mandatory, naming who the statement applies to.

Both policy types are evaluated together. For cross-account access you need both the bucket policy (granting on the resource side) and the caller's IAM policy (granting on the identity side). Inside the same account, either one is enough, but the bucket policy is usually clearer because the bucket itself documents who can read it.

Principal: who the policy applies to

  • "Principal": "*" — anonymous public (use only for static website hosting and read-only access).
  • {"AWS": "arn:aws:iam::123456789012:user/Alice"} — a specific IAM user.
  • {"AWS": "arn:aws:iam::123456789012:role/AppRole"} — a specific IAM role (preferred over users).
  • {"AWS": "123456789012"} — any identity in that account (still requires identity-side permission).
  • {"Service": "cloudfront.amazonaws.com"} — AWS service principal, e.g. CloudFront OAC.
  • {"Federated": "cognito-identity.amazonaws.com"} — federated identity providers.

Common patterns

Public read for static website hosting:

{
  "Effect": "Allow",
  "Principal": "*",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::my-site/*"
}

Restrict by source IP range: "Condition": { "IpAddress": { "aws:SourceIp": ["10.0.0.0/24", "203.0.113.0/24"] } }.

Require HTTPS (deny plain HTTP):

{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:*",
  "Resource": ["arn:aws:s3:::bucket", "arn:aws:s3:::bucket/*"],
  "Condition": { "Bool": { "aws:SecureTransport": "false" } }
}

CloudFront OAC (Origin Access Control) — modern replacement for legacy OAI: only the specified CloudFront distribution can read; combine with a Block Public Access bucket.

Block Public Access, MFA Delete and versioning

Block Public Access (BPA) is an account- and bucket-level switch that overrides any Allow in your bucket policy. Even a perfectly correct public-read policy will return 403 if BPA is on. Always check BPA first when debugging "AccessDenied" on a public bucket.

MFA Delete on a versioned bucket forces an MFA token on every DeleteObjectVersion — required for compliance buckets. Versioning introduces separate actions like s3:GetObjectVersion and s3:DeleteObjectVersion; granting s3:GetObject alone does not let you read old versions.

Object Lock enforces WORM (Write Once Read Many) retention for GDPR/SOX/HIPAA. Once locked in compliance mode, not even the root user can delete the object until retention expires.

Pitfalls

  • Explicit Deny always wins, even against an Allow in the same policy or an IAM allow elsewhere.
  • Bucket policy applies to the bucket as a whole. To restrict by path, encode the prefix in the Resource ARN: arn:aws:s3:::bucket/users/${aws:username}/*.
  • KMS encryption keys have their own key policy — granting s3:GetObject is not enough if objects are encrypted with a customer-managed key.
  • Cross-account: the bucket policy is just half the story. The remote principal still needs IAM permission in their own account.
  • Public-read policies on buckets with sensitive prefixes are an exfiltration risk — use BPA plus least-privilege IAM, not Principal: *.

FAQ

Bucket policy vs ACL? Bucket policies are JSON, granular, and the modern way. ACLs are XML, legacy, and AWS now recommends disabling them via the Object Ownership setting (BucketOwnerEnforced).

Can I delegate access to another AWS account? Yes — put the remote account or IAM principal in the Principal field, and have them attach a matching IAM policy on their side. Both halves are required.

CORS vs bucket policy — which do I need? Both. CORS governs browser access from a web page (response headers); bucket policy governs API access (authentication and authorisation). A bucket can have both.

Why is my policy still denying access? Check, in order: Block Public Access setting, explicit Deny in any policy or SCP, KMS key policy if encrypted, and whether you addressed the bucket-level vs object-level ARN distinction (bucket vs bucket/*).

How big can a bucket policy be? 20 KB (20,480 bytes) JSON. If you hit the limit, consolidate statements with arrays of resources or move logic to IAM identity policies on the accessing principals.

Related Tools