2
votes

I am creating a bucket and then a bucket policy to allow all the listed accounts access. Seems to pass on terraform plan/apply, but looking at the console, only one account is listed, seems to be trampling previous policy adds.

I have a variable list and code to generate bucket/policy:

variable "accounts" {
    type = "list"
    default = [
         "111111111111",
         "222222222222",
         "333333333333",
         "444444444444",
         "555555555555",
         "666666666666",
         "777777777777"
    ]
}


resource "aws_s3_bucket" "my_bucket" {
  bucket = "${var.bucket_name}"
}
resource "aws_s3_bucket_policy" "my_bucket_policy" {
  bucket = "${aws_s3_bucket.my_bucket.id}"
  count = "${length(var.accounts)}"
  policy =<<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${element(var.accounts, count.index)}:root"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::${var.bucket_name}/CloudTrail/AWSLogs/${element(var.accounts, count.index)}/*"
        }
    ]
}
EOF
}

Is there a way to just iterate the variable list in the Resource or Principal sections only? Goal would be to generate a single bucket policy that has all arns from the variables.

1
Currently there is no for loop for this case to create rules for each account.BMW

1 Answers

0
votes

Each bucket only supports one bucket policy, so you can't create multiple bucket policies.

Instead you can create multiple statements inside a single policy, one for each account. Terraform doesn't support iteration, but you can get much of the same effect using formatlist() and join() functions.

resource "aws_s3_bucket_policy" "my_bucket_policy" {
  bucket = "${aws_s3_bucket.my_bucket.id}"
  policy =<<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        ${join(",\n",formatlist("
        {
            \"Effect\": \"Allow\",
            \"Principal\": {
                \"AWS\": \"arn:aws:iam::%s:root\"
            },
            \"Action\": \"s3:PutObject\",
            \"Resource\": \"arn:aws:s3:::%s/CloudTrail/AWSLogs/%s/*\"
         }
         ", var.accounts, var.bucket_name, var.accounts))}
    ]
}
EOF
}

In this case, I put the entire statement content as the first argument of a formatlist() function. It was necessary to escape the double quotes, but Terraform doesn't require newlines to be escaped. It contains %s where you want to put the replacements. The other formatlist() arguments are the replacements.

The output of formatlist() is a list, so I had to join the list into a single output string using the join() function. The join() function also allowed me to add the comma separators between each statement.