0
votes

I would like to see if it's possible to manipulate each resource in a for_each without creating more resources by the reference of the resource.

For example, I'm creating 3 AWS S3 buckets, in my S3 module I have this resource:

    resource "aws_s3_bucket" "s3-bucket" {
    for_each = var.bucket_details
    bucket = each.value.bucket
    acl    = each.value.acl
    policy = each.value.policy
   }

And this is my .tfvars:

    bucket_details = {

          bucket_1 = {
            bucket = "stg-kikeman-bkt-1"
            acl = "private"
            policy = <<EOF
            { ... <= THIS IS A JSON S3 POLICY 
            }
            EOF
          },
          bucket_2 = { ... }
          bucket_3 = { ... }

After an apply, it suppose to create 3 buckets with the values of the bucket name, acl and its policy.

But I'm stuck with the error:

 on modules/s3/s3.tf line 15, in resource "aws_s3_bucket" "s3-bucket":
│   15:     policy = each.value.policy
│     ├────────────────
│     │ each.value is map of string with 3 elements
│ 
│ This map does not have an element with the key "policy"

It looks like the value for the key policy which is a json policy is not being recognized as a "value", I've tried using the jsonencode({the_json_policy}) but I got the error of

Functions may not be called here

I'm looking on how can I use the json policy as a "value" form a map, if it's possible of course.

2

2 Answers

0
votes

Is it possible?

It is. Depending of what you want to change in your policy, you would have to conditionally set attributes. based on the bucket name which requires different settings.

0
votes

for_each is intended for situations where you want to systematically declare multiple objects based on data in a map. If you need to have differences between the instances then you will need to somehow represent those differences in the map you provide, so that the design is still systematic.

One way to do it would be to make your source collection instead be a mapping from names to objects containing policy documents and ACL strings:

locals {
  buckets = {
    stg-bkt-1 = {
      acl = "private"
      policy = {
        Version = "2012-10-17"
        Id      = Test
        Statement = [
          {
            Sid = "1"
            Effect = "Deny"
            Principal = "*"
            Action = "*"
            Resource = "arn:aws:s3:::stg-bkt-1/*"
            Condition = {
              Bool = {
                "aws:SecureTransport": "false"
              }
            }
          },
        ]
      }
    }
    stg-bkt-2 = {
      acl = "public-read"
      policy = {
        Version = "2012-10-17"
        Id      = Test
        Statement = [
          {
            Sid       = "1"
            Effect    = "Deny"
            Principal = "*"
            Action    = "*"
            Resource  = "arn:aws:s3:::stg-bkt-2/*"
            Condition = {
              Bool = {
                "aws:SecureTransport": "false"
              }
            }
          },
          {
            Sid       = "2"
            Effect    = "Allow"
            Principal = "*"
            Action    = ["s3:GetObject"]
            Resource  = "arn:aws:s3:::stg-bkt-2/*"
          },
        ]
      }
    }
  }
}

resource "aws_s3_bucket" "s3-bucket" {
  for_each = var.buckets

  bucket = each.key
  acl    = each.value.acl
  policy = jsonencode(each.value.policy)
}

In situations where the resource configuration ends up just entirely references to each.key and each.value I'd typically say that this isn't really systematic at all and so it'd be simpler and easier to just write out multiple resource "aws_s3_bucket" blocks. But if you can adjust this so that all of the buckets still have some characteristics in common -- for example, if the definition in the mapping only included additional IAM policy statements to be concatenated with the ones that are common to all buckets -- then it seems justified to use for_each to systematically declare them, so that those common parts will be forced to stay common as you change details in future.