amazon-web-servicesterraformterraform-provider-aws

How to pass different principals when deploying a bucket policy using terraform


Using terraform I am creating a simple bucket and applying a simple bucket policy that allows a role and a user to get access to it. The role/user is different for each account that is being deployed.

# Creating buckets
resource "aws_s3_bucket" "buckets" {
  for_each = var.s3_bucket_names
    
  bucket = "${var.prefix}-${var.environment}-${lower(var.github_branch)}-${each.key}"
}
    
# Apply Bucket policy to buckets.
resource "aws_s3_bucket_policy" "allow_access_to_bucket" {
  for_each = aws_s3_bucket.buckets
    
  bucket = each.value.id
  policy = templatefile("s3_policy.json", {
    tmp_s3_arn  = "${each.value.arn}",
    object_access = "${var.s3_object_access}"
  })
}

Then the policy is like:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "S3ObjectPermissions",
                "Effect": "Allow",
                "Principal": {
                    "AWS": [
                        "arn:aws:iam::${tmp_account}:role/role_case_one",
                        "arn:aws:sts::${tmp_account}:assumed-role/dev_role/user@here.com"]
                },
                "Action": [
                    "s3:GetObject",
                    "s3:PutObject",
                    "s3:PutObjectAcl",
                    "s3:DeleteObject"
                ],
                "Resource": "${tmp_s3_arn}/*"
            }
        ]
    }

But I want to parametrize the principal as they may change on different accounts. I have tried to replace the list of principals for a list in terraform, but it is trying to "add" a new terraform with the information of the list.

I tried to pass just a string and the same, it tries to add the string ad additional principal.

    ~ Principal = {
     ~ AWS = [
       - "arn:aws:iam::12345:role/role_case_one",
       - "arn:aws:sts::12345:assumed-role/assumed-role/dev_role/user@here.com",
     ] -> "[arn:aws:iam::12345:role/role_case_one,arn:aws:iam::12345:assumed-role/assumed-role/dev_role/user@here.com]"

How can I achieve this?


Solution

  • I think what you want is to use the built-in jsonencode function within the templated file. For example, something like this should work:

    # Creating buckets
    resource "aws_s3_bucket" "buckets" {
      for_each = var.s3_bucket_names
        
      bucket = "${var.prefix}-${var.environment}-${lower(var.github_branch)}-${each.key}"
    }
        
    # Apply Bucket policy to buckets.
    resource "aws_s3_bucket_policy" "allow_access_to_bucket" {
      for_each = aws_s3_bucket.buckets
        
      bucket = each.value.id
      policy = templatefile("s3_policy.json", {
        tmp_s3_arn    = each.value.arn
        roles         = ["arn:aws:iam::12345:role/role_case_one", "arn:aws:sts::12345:assumed-role/assumed-role/dev_role/user@here.com"]
      })
    }
    

    The templated file should then look like the following:

    ${jsonencode({
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "S3ObjectPermissions",
                "Effect": "Allow",
                "Principal": {
                    "AWS": [ for role in roles:
                       role
                    ]
                },
                "Action": [
                    "s3:GetObject",
                    "s3:PutObject",
                    "s3:PutObjectAcl",
                    "s3:DeleteObject"
                ],
                "Resource": "${tmp_s3_arn}/*"
            }
        ]
    })}
    

    You can also check more examples in the documentation. This works:

    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
      + create
    
    Terraform will perform the following actions:
    
      # aws_s3_bucket.buckets["averyimportantbucketmarkoe"] will be created
      + resource "aws_s3_bucket" "buckets" {
          + acceleration_status         = (known after apply)
          + acl                         = (known after apply)
          + arn                         = (known after apply)
          + bucket                      = "averyimportantbucketmarkoe"
          + bucket_domain_name          = (known after apply)
          + bucket_prefix               = (known after apply)
          + bucket_regional_domain_name = (known after apply)
          + force_destroy               = false
          + hosted_zone_id              = (known after apply)
          + id                          = (known after apply)
          + object_lock_enabled         = (known after apply)
          + policy                      = (known after apply)
          + region                      = (known after apply)
          + request_payer               = (known after apply)
          + tags_all                    = (known after apply)
          + website_domain              = (known after apply)
          + website_endpoint            = (known after apply)
        }
    
      # aws_s3_bucket_policy.allow_access_to_bucket["averyimportantbucketmarkoe"] will be created
      + resource "aws_s3_bucket_policy" "allow_access_to_bucket" {
          + bucket = (known after apply)
          + id     = (known after apply)
          + policy = (known after apply)
        }
    
    Plan: 2 to add, 0 to change, 0 to destroy.
    
    Do you want to perform these actions?
      Terraform will perform the actions described above.
      Only 'yes' will be accepted to approve.
    
      Enter a value: yes
    
    aws_s3_bucket.buckets["averyimportantbucketmarkoe"]: Creating...
    aws_s3_bucket.buckets["averyimportantbucketmarkoe"]: Creation complete after 1s [id=averyimportantbucketmarkoe]
    aws_s3_bucket_policy.allow_access_to_bucket["averyimportantbucketmarkoe"]: Creating...
    aws_s3_bucket_policy.allow_access_to_bucket["averyimportantbucketmarkoe"]: Creation complete after 1s [id=averyimportantbucketmarkoe]
    
    Apply complete! Resources: 2 added, 0 changed, 0 destroyed.