Terraform has a cool resource block called the 'dynamic' block that allows generating multiple nested blocks for a resource.
This tutorial will show you how to generate multiple IAM policy statements using this dynamic block.
In this example we have a list of AWS Principals that we want to allow access to our bucket named dev-to-multi-account-bucket.
# variables.tf
variable "aws_accounts" {
  type        = map(list(string))
  description = "A map of lists of AWS Principals"
  default     = {
    "mgmt" = ["arn:aws:iam::123456789876:root"]
    "prod" = ["arn:aws:iam::098765432123:root"]
  }
}
# bucket_policy.tf
resource "aws_s3_bucket_policy" "cross_account_policy" {
    bucket = "dev-to-multi-account-bucket"
    policy = data.aws_iam_policy_document.allow_access_from_another_account.json
}
# dynamic policy statement block for each account specified
data "aws_iam_policy_document" "allow_access_from_another_account" {
    dynamic "statement" {
        for_each = var.aws_accounts
        content {
            actions   = ["s3:*"]
            resources = [
                "arn:aws:s3:::dev-to-multi-account-bucket/${statement.key}*",
                ]
            principals {
                type = "AWS"
                identifiers = "${flatten(statement.value)}"
            } 
        }
    }
}
Explaination
So what's really happening here? Let's look at the aws_iam_policy_document.allow_access_from_another_account "data" resource block.
We are leveraging the dynamic block within this resource to iterate through the aws_accounts variable. This generates multiple statements inside the policy allowing us to rely on the variable's length.
The variable statement has two important attributes: key and value. These allow you to target the respective section of the key-value map.
We can access these attributes through the statement variable. Terraform automatically assigns this variable name from the dynamic block's configuration. In this case, it's named statement.
After running terraform plan, we see a plan that looks something like this:
  # aws_s3_bucket_policy.cross_account_policy will be created
  + resource "aws_s3_bucket_policy" "cross_account_policy" {
      + bucket = "dev-to-multi-account-bucket"
      + id     = (known after apply)
      + policy = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "s3:*"
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = "arn:aws:iam::123456789876:root"
                        }
                      + Resource  = [
                          + "arn:aws:s3:::dev-to-multi-account-bucket/mgmt*",
                        ]
                      + Sid       = ""
                    },
                  + {
                      + Action    = "s3:*"
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = "arn:aws:iam::098765432123:root"
                        }
                      + Resource  = [
                          + "arn:aws:s3:::dev-to-multi-account-bucket/prod*",
                        ]
                      + Sid       = ""
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
    }
Nice!
Find more about dynamic blocks in Terraform's official documentation page.
 
 
              
 
    
Top comments (0)