Terraform modules have variables for the parts that vary. Things that don't need to vary should be hardcoded as a best practice.
Otherwise you will be creating modules that do nothing and take code as input.
Any sufficiently complicated [dynamic Terraform module] contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of [Terraform itself]..
remix of Greenspun's tenth rule
data "aws_iam_policy_document" "assume_role_policy" {
statement {
sid = "AllowAssumeRole"
effect = "Allow"
actions = [
"sts:AssumeRole",
]
resources = var.trusted_arns
}
}
You can grant access to a set of AWS buckets as a variable like this:
statement {
sid = "AllowS3Access"
effect = "Allow"
actions = [
"s3:GetBucketLocation",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:GetObject",
"s3:ListMultipartUploadParts",
"s3:AbortMultipartUpload",
]
resources = var.allowed_read_only_s3_bucket_arns
}
If you are bent on making dynamic policies you can do this:
data "aws_iam_policy_document" "meta_policy" {
dynamic "statement" {
for_each = var.statements
content {
sid = each.key
effect = each.value.effect
actions = each.value.actions
resources = each.value.resources
}
}
}
Would be used with a variable like this:
variable "statements" {
description = "I didn't want to use resources, so I made meta-Terraform"
type = map(object(
{
sid = string
effect = string
actions = list(string)
resources = list(string)
}
))
}
Someone looking at such a module would have no way of knowing what it needs and would be relying purely on input variables. Maybe this is suitable for your use case, but I recommend against it.
Your colleagues, including your future self will thank you if you embrace hardcoding things that don't actually change.