S3 Buckets Policies for multiple buckets using for-each

I have to attach bucket policy to 10+ buckets. Creating 10+ buckets is not a problem but attacing a policy that the buckets can only be accessed if someone is accessing from vpc endpoints is a challenge( for me). I am pretty sure that experts like @apparentlymart can do some magic by combining aws_s3_bucket_policy with for_each or for

Appreciate your help. Thanks !

creating buckets ( not a problem)

variable "s3_bucket_name" {
  type    = "list"
  default = ["Test_1","Test_2","Test_3"]
}

resource "aws_s3_bucket" "b" { 
  count         = "${length(var.s3_bucket_name)}"
    bucket        = "${var.s3_bucket_name[count.index]}"
    acl           = "private"
    force_destroy = "true"
    }        

But how do I attach the policy dynamically. It will be dumb if I have to type the bucket policy 10+ times. Here is what I am showing for two buckets

resource "aws_s3_bucket_policy" "p1" {
  bucket = "${aws_s3_bucket.b.id}"

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Id": "MYBUCKETPOLICY",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::Test_1"
      "Condition": {
         "IpAddress": {"aws:SourceIp": "8.8.8.8/32"}
      }
    }
  ]
}
POLICY
}




resource "aws_s3_bucket_policy" "p2" {
  bucket = "${aws_s3_bucket.b.id}"

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Id": "MYBUCKETPOLICY",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::Test_2"
      "Condition": {
         "IpAddress": {"aws:SourceIp": "8.8.8.8/32"}
      }
    }
  ]
}
POLICY
}

Just bumping it again.
Wishing to get some help this time :cry:

Hi @Jim420,

I think something like this would do it:

variable "s3_bucket_names" {
  type    = set(string)
  default = ["Test_1","Test_2","Test_3"]
}

resource "aws_s3_bucket" "b" {
  for_each = var.s3_bucket_names

  bucket = each.key
  # ... and all of your other settings
}

resource "aws_s3_bucket_policy" "p1" {
  for_each = aws_s3_bucket.b

  bucket = each.key
  policy = jsonencode({
    Version = "2012-10-17"
    Id      = "MYBUCKETPOLICY",
    Statement = [
      {
        Sid       = "IPAllow"
        Effect    = "Deny"
        Principal = "*"
        Action    = "s3:*"
        Resource  = each.value.arn
        Condition = {
          IpAddress = {
            "aws:SourceIp": "8.8.8.8/32"
          }
        }
      }
    ]
  })
}

Note that because the policy’s for_each is over the bucket resource itself, in that block each.key is the bucket name and each.value is the object representing each bucket. That means I was able to use each.value.arn to access the ARN of each bucket as generated by the provider itself.

1 Like

@apparentlymart

Amazing !
Your magic worked.
Thank you!

@apparentlymart

Apologies for bumping an old topic, but I have a question along similar lines to this. I essentially want to do the same thing but rather than using jsonencode on an inline policy, I want to use the json render from a data source. The reason for this is readability as the policy required for my usecase is extensive and I keep it in it’s own file.

my policy resource is as follows:

resource "aws_s3_bucket_policy" "s3_bucket_policy" {
  for_each = aws_s3_bucket.s3_buckets
  bucket = each.value.id
  policy = data.aws_iam_policy_document.s3_bucket_policy.json
}

The error is telling me:

Error: each.value cannot be used in this context

  on data.tf line 133, in data "aws_iam_policy_document" "s3_bucket_policy":
 133:       "arn:aws:s3:::${each.value.id}/*",

A reference to "each.value" has been used in a context in which it
unavailable, such as when the configuration no longer contains the value in
its "for_each" expression. Remove this reference to each.value in your
configuration to work around this error.

Is what I want to do possible?

Hi @fferguson6,

In order for that to work you’ll need to use the same for_each argument in your data "aws_iam_policy_document" "s3_bucket_policy" block so that you can generate a separate policy document for each bucket.

As you noted this is an old topic, so if you have some more questions about that I’d invite you to start a new topic where we can hopefully see a more complete subset of your configuration and then I should be able to give you some more specific advice.

Hey, my apologies for responding to an old topic. I am trying to do something similar with attaching bucket policies to s3 buckets, but am getting an error. I cannot figure out why this error is coming. Below is how I am creating the buckets, bucket policies, and the errors:

resource "aws_s3_bucket" "dest_buckets" {
  for_each      = var.s3_bucket_names
  bucket        = "${each.key}-replica"
  acl           = "private"
  force_destroy = "true"

  versioning {
    enabled = true
  }
}

resource "aws_s3_bucket_policy" "dest_policy" {
  for_each = aws_s3_bucket.dest_buckets
  bucket   = each.key
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect    = "Allow"
        Action    = "s3:GetBucketVersioning"
        Resource  = each.value.arn
        Principal = "arn:aws:iam::account-id:role/Replication-Role"
      },
{
    Effect    = "Allow"
    Action    = "s3:PutBucketVersioning"
    Resource  = each.value.arn
    Principal = "arn:aws:iam::account-id:role/Replication-Role"
  },
  {
    Effect    = "Allow"
    Action    = "s3:ReplicateObject"
    Resource  = "${each.value.arn}/*"
    Principal = "arn:aws:iam::account-id:role/Replication-Role"
  },
  {
    Effect    = "Allow"
    Action    = "s3:ReplicateDelete"
    Resource  = "${each.value.arn}/*"
    Principal = "arn:aws:iam::account-id:role/Replication-Role"
  }
]
}
  )
}

Error: Error putting S3 policy: BucketRegionError: incorrect region, the bucket is not in 'us-east-2' region at endpoint ''
    status code: 301, request id: , host id:

  on s3.tf line 28, in resource "aws_s3_bucket_policy" "dest_policy":
  28: resource "aws_s3_bucket_policy" "dest_policy" {



Error: Error putting S3 policy: AccessDenied: Access Denied
        status code: 403, request id: A4BDA2A2818CE3C7, host id: BlVmLX44pl7itNWbfxKe0wnA0UxlGrH6b9HIQC0i09sk5NLl1jgSGrNZjEkHVhQeU2YN3lgi4ec=

  on s3.tf line 28, in resource "aws_s3_bucket_policy" "dest_policy":
  28: resource "aws_s3_bucket_policy" "dest_policy" {

Hey Buddy…
I have a similar config did you manage to get this resolved?..

@isandozi I have as similar config did you manage to get this resolved…

@kbab6aoo Please start a new topic, and describe the problem you personally are experiencing - this one has already been reused over multiple years.

Hey @apparentlymart apologies for bumping the old topic. I have a similar kind of question but bit different like i have one policy which implies on single bucket and another policy which implies to all bucket. Now how i use single code for both situations as every time my single policy replace by second policy.

As has been said several times already in this topic, please start a new topic for new questions.

Just because a question is about S3 bucket policies, does not make it similar to all other questions about S3 bucket policies.