Search code examples
terraformterraform-template-file

Templating in Terraform module - argument is not expected here


During the creation of the new TF module, I met the issue with templatefile functionality.

In the triggers.tf I declared the module with an argument which is templated config. The module ./modules/trigger is using provided argument (filename).

During the TF plan I'm receiving:

 Error: Unsupported argument
│
│   on triggers.tf line 43, in module "trigger":
│   43:   filename = templatefile("${path.module}/templates/trigger.yaml.tpl", {
│
│ An argument named "filename" is not expected here.

./modules/trigger/main.tf

resource "null_resource" "trigger" {

  filename = var.filename

  provisioner "local-exec" {
    command = <<EOT
      gcloud alpha ... --trigger_config ${self.filename}  \
EOT
  }
}

triggers.tf

locals {
  repos = yamldecode(file("${path.module}/repositories/repos.yaml"))
  repos = {
    for pair in setproduct([local.repos["project"]], local.repos["repos"]) : "${pair[0]}/${pair[1]}" => {
      project = pair[0]
      repo    = pair[1]
    }
  }
}

module "trigger" {
  source   = "./modules/trigger"
  for_each = local.repos

  filename = templatefile("${path.module}/templates/trigger.yaml.tpl", {
    trigger_event           = "push"
    bitbucket_project       = each.value["project"]
    bitbucket_repo          = each.value["repo"]
  })
}

/repositories/repos.yaml

project: test
repos:
  - cloud-test

/templates/trigger.yaml.tpl

name: "${trigger_event}"
project: "${bitbucket_project}"
repo: "${bitbucket_repo}"

Solution

  • Here are the changes that are required for this to work:

    1. Create a file using local_file resource
    2. Update the null_resource in the module to use only the variable and not the non-existent argument (i.e., filename)

    Note that renaming local variables is not allowed, so having two repos definitions will cause errors. I suggest changing it to something like:

    locals {
      repositories = yamldecode(file("${path.module}/repositories/repos.yaml"))
      repos = {
        for pair in setproduct([local.repositories["project"]], local.repositories["repos"]) : "${pair[0]}/${pair[1]}" => {
          project = pair[0]
          repo    = pair[1]
        }
      }
    }
    

    So in the root of the module, here is what part of code you need:

    resource "local_file" "trigger_file" {
      for_each = local.repos
      content = templatefile("${path.module}/templates/trigger.yaml.tpl", {
        trigger_event     = "push"
        bitbucket_project = each.value["project"]
        bitbucket_repo    = each.value["repo"]
      })
      filename = "${path.module}/trigger-${each.value["project"]}.yaml"
    }
    

    The local_file resource will create as many files as you need, where the names of the files will be different depending on the project name. So for your current example this will crate a file with name trigger-test.yaml in the same directory. Then, in the module you are calling, the null_resource would change to:

    resource "null_resource" "trigger" {
    
      provisioner "local-exec" {
        command = <<EOT
          gcloud alpha ... --trigger_config ${var.filename}  \
    EOT
      }
    }
    

    This should work if you have defined the variable filename in the module. Lastly, when calling the module, you can use the following code:

    module "trigger" {
      source   = "./modules/trigger"
      for_each = local.repos
    
      filename = "${path.module}/trigger-${each.value["project"]}.yaml"
    }