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)