Skip to main content
The module provides two mechanisms for attaching bucket policies: built-in managed policies for common AWS services, and a policy variable for attaching arbitrary custom JSON policies.

Attaching a custom policy

Set attach_policy = true and provide a JSON policy document via the policy variable:
data "aws_iam_policy_document" "bucket_policy" {
  statement {
    principals {
      type        = "AWS"
      identifiers = [aws_iam_role.this.arn]
    }

    actions = [
      "s3:ListBucket",
    ]

    resources = [
      "_S3_BUCKET_ARN_",
    ]
  }
}

module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-s3-bucket"

  attach_policy = true
  policy        = data.aws_iam_policy_document.bucket_policy.json
}

Policy placeholders

When writing a custom policy you often need to reference the bucket’s ID, ARN, or the owning account ID. These values are not always known ahead of time — especially when bucket_prefix is used to generate a unique name. The module replaces the following placeholder strings at apply time:
PlaceholderReplaced with
_S3_BUCKET_ID_The bucket name (aws_s3_bucket.this[0].id)
_S3_BUCKET_ARN_The bucket ARN (aws_s3_bucket.this[0].arn)
_AWS_ACCOUNT_ID_The caller’s AWS account ID
This is especially useful when using bucket_prefix, because the final bucket name is generated by AWS and is not known until after creation:
data "aws_iam_policy_document" "bucket_policy" {
  # Reference the bucket ARN via placeholder — works even with bucket_prefix
  statement {
    principals {
      type        = "AWS"
      identifiers = [aws_iam_role.this.arn]
    }

    actions = ["s3:ListBucket"]

    resources = ["_S3_BUCKET_ARN_"]

    condition {
      test     = "StringNotEquals"
      variable = "aws:PrincipalAccount"
      values   = ["_AWS_ACCOUNT_ID_"]
    }
  }
}

module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket_prefix = "my-bucket-"

  attach_policy = true
  policy        = data.aws_iam_policy_document.bucket_policy.json
}

Built-in managed policies

The module includes built-in policies for common scenarios. Enable them with the corresponding boolean variable:
VariableDescription
attach_deny_insecure_transport_policyDeny all requests not using HTTPS
attach_require_latest_tls_policyDeny TLS versions older than 1.2
attach_deny_incorrect_encryption_headersDeny PutObject with the wrong SSE algorithm header
attach_deny_incorrect_kms_key_sseDeny PutObject that uses a KMS key other than allowed_kms_key_arn
attach_deny_unencrypted_object_uploadsDeny PutObject without an SSE header
attach_deny_ssec_encrypted_object_uploadsDeny PutObject using SSE-C (customer-provided keys)
attach_elb_log_delivery_policyAllow Classic ELB to write access logs
attach_lb_log_delivery_policyAllow ALB/NLB to write access logs
attach_cloudtrail_log_delivery_policyAllow CloudTrail to write logs
attach_waf_log_delivery_policyAllow WAF to write logs
attach_access_log_delivery_policyAllow S3 server access log delivery
All active policies are merged into a single aws_s3_bucket_policy resource. Custom (policy) and built-in policies are combined automatically.

Combining custom and built-in policies

module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-s3-bucket"

  # Custom policy
  attach_policy = true
  policy        = data.aws_iam_policy_document.bucket_policy.json

  # Built-in security policies
  attach_deny_insecure_transport_policy    = true
  attach_require_latest_tls_policy         = true
  attach_deny_unencrypted_object_uploads   = true

  # KMS key enforcement
  attach_deny_incorrect_kms_key_sse = true
  allowed_kms_key_arn               = aws_kms_key.objects.arn
}

Viewing the applied policy

The applied policy JSON is available via the s3_bucket_policy output:
output "bucket_policy" {
  value = module.s3_bucket.s3_bucket_policy
}

Build docs developers (and LLMs) love