How to set parameters on a aws_iam_policy_document?

Hello,

I’m currently writing the Terraform module for an architecture that was created entirely in the AWS console and things are moving along quite well.
I’m currently writing the IAM policies and have been using aws_iam_policy_document because it allows for validation from Terraform itself before deploying the architecture.

I have multiple policies that are all a variation of this definition:

resource aws_iam_policy InvokeMyLambda {
    name        = "InvokeMyLambda"
    description = "Allows invoking MyLambda"
    policy      = data.aws_iam_policy_document.InvokeMyLambda.json 
}

data aws_iam_policy_document InvokeMyLambda {
    statement {
        sid = "VisualEditor0"
        effect = "Allow"
        actions = [
            "lambda:InvokeFunction"
        ]
        resources = [
            "arn:aws:lambda:${var.region}:${data.aws_caller_identity.current.account_id}:function:MyLambda"
        ]
    }
}

After two copy/paste where I simply modified MyLambda, the name of the resource and data item, I feel this is not a good way to work and am looking for a better way to do things.

It would be nice if there was a syntax like this:

resource aws_iam_policy InvokeMyLambda {
    PasteItem("InvokeLambdaPolicy", {LambdaName = "MyLambda"})
}

data aws_iam_policy_document InvokeMyLambda {
    PasteItem("InvokeLambdaPolicyDocument", {LambdaName = "MyLambda"})
}

resource aws_iam_policy InvokeOtherLambda {
    PasteItem("InvokeLambdaPolicy", {LambdaName = "OtherLambda"})
}

data aws_iam_policy_document InvokeOtherLambda {
    PasteItem("InvokeLambdaPolicyDocument", {LambdaName = "OtherLambda"})
}

I went looking in the documentation but I could not find something like that.
I did find the templatefile method but it means losing the validation by terraform which is really nice to have.
I also looked at Modules but it does not appear possible to have a module create resources with a name based on an input parameter. Nor did I find a way to declare a “partial” resource to be included like above.

Running terraform --version gives this:

Terraform v1.0.11
on windows_amd64
+ provider registry.terraform.io/hashicorp/aws v3.66.0
+ provider registry.terraform.io/hashicorp/null v3.1.0
+ provider registry.terraform.io/hashicorp/random v3.1.0

So I believe I’m using a recent enough version.

What would you suggest that I try next?

Well, modules are actually a way to solve this.
It’s just that the same module has to be instantiated multiple times and then the root module references each instance of the module where it needs it.
If the module exports the appropriate value, we can then have what we want.
I did it like this for the AssumeRole policy that I kept repeating with just a change of service: Create a folder named AssumeRole and put this inside a singe file:

variable "ServicePrincipal" {
    description = "The unique service principal for the policy"
    default = null
    type = string
}

variable "ServicePrincipals" {
    description = "The service principal identifiers array for the policy"
    default = null
    type = list(string)
}

data aws_iam_policy_document policy {
    statement {
        actions = [
            "sts:AssumeRole"
        ]
        principals {
            type = "Service"
            identifiers = (var.ServicePrincipal != null) ? [var.ServicePrincipal] : var.ServicePrincipals
        }
        effect = "Allow"
    }
}

output "json" {
    value = data.aws_iam_policy_document.policy.json
}

Then, in the root module, I have used it like this:

module ApiGatewayAssumeRolePolicy {
    source = "./AssumeRole"

    ServicePrincipal = "apigateway.amazonaws.com"
}


module LambdaAssumeRolePolicy {
    source = "./AssumeRole"

    ServicePrincipal = "lambda.amazonaws.com"
}

resource aws_iam_role S3GetObject {
    name                = "S3GetObject"
    description         = "Role for the API Gateway documentation S3 integration"
    assume_role_policy  = module.ApiGatewayAssumeRolePolicy.json
    tags                = local.tags
}

resource aws_iam_role MyLambda {
    name                = "MyLambda "
    description         = "Role for the MyLambda Lambda"
    assume_role_policy  = module.LambdaAssumeRolePolicy.json
    tags                = local.tags
}

I have two drawbacks here

  1. I need to run terraform init each time I add a new instantiation of the module.
  2. There is no way to tell that ServicePrincipal and ServicePrincipals cannot be both null

But apart from this, it’s working just fine