DEV Community

Cover image for Using 'dynamic' block to generate IAM policy statements in Terraform
Arpan
Arpan

Posted on • Edited on

Using 'dynamic' block to generate IAM policy statements in Terraform

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"]
  }
}
Enter fullscreen mode Exit fullscreen mode
# 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)}"
            } 
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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"
            }
        )
    }
Enter fullscreen mode Exit fullscreen mode

Nice!

Find more about dynamic blocks in Terraform's official documentation page.

Top comments (0)