skip to Main Content

running Terraform 1.7.4

for each row in arg map my SSM automation needs

{
     "description": "Invoke Lambda Function ${key}",
     "name": "InvokeLambdaFunction${key}",
     "action": "aws:invokeLambdaFunction",
     "inputs": {
         "FunctionName": "arn:aws:lambda:us-east-1:${account_number}:function:hello",
         "InvocationType": "RequestResponse",
         "Payload": "{"key1": "${account_number}", "key2": "<first value from arg_map>", "key3": "<second value from arg_map>"}"
     }
}

main.tf

provider "aws" {
  region = "us-east-1"  # Set your desired region here
}
    
variable "account_number" {
  default = "123456789"
}
    
variable "arg_map" {
  type = map(list(string))
  default = {
    "key" = ["a", "b"]
    "key" = ["d", "e"]
    "key" = ["g", "h"]
    "key" = ["j", "k"]
  }
}
    
resource "aws_ssm_document" "sync_epv2asm" {
  name          = "sync_epv2asm"
  document_type = "Automation"
 
  content = templatefile("${path.module}/ssm_document_template.tftpl", {
    account_number = var.account_number
    arg_map        = var.arg_map
  })
}

ssm_document_template.tftpl

{
  "schemaVersion": "0.3",
  "description": "My description.",
  "mainSteps": [
    % for key, values in arg_map:
     {
        "description": "Invoke Lambda Function ${key}",
        "name": "InvokeLambdaFunction${key}",
        "action": "aws:invokeLambdaFunction",
        "inputs": {
          "FunctionName": "arn:aws:lambda:us-east-1:${account_number}:function:hello",
          "InvocationType": "RequestResponse",
          "Payload": "{"key1": "${account_number}", "key2": "${values[0]}", "key3": "${values[1]}"}"
        }
    }% if not loop.last %,
    % endif
    % endfor
  ]
}

running terraform apply

│ Error: Invalid function argument

│ on main.tf line 23, in resource "aws_ssm_document" "sync_epv2asm":
│ 23: content = templatefile("${path.module}/ssm_document_template.tftpl", {
│ 24: account_number = var.account_number
│ 25: arg_map = var.arg_map
│ 26: })
│ ├────────────────
│ │ while calling templatefile(path, vars)
│ │ var.arg_map is a map of list of string

│ Invalid value for "vars" parameter: vars map does not contain key "key", referenced at ./ssm_document_template.tftpl:7,44-47.

googling around is not finding anything. I even tried some of the AI to see if they and identify my problem.

some of the things on the internet were showing "{}" the template around the "%". that did not change my error.

any thoughts on why I cannot get the template to work?

2

Answers


  1. Chosen as BEST ANSWER

    Apparently there was a problem with my terraform. for some reason the template would only use the last line of arg_map.
    I had to uninstall Terraform and reinstall terraform.

    It appears that the invoke lambda function is smart. it needed $LATEST or $, and the actual account number.

    Main.tf

    provider "aws" {
      region = "us-east-1" # Set your desired region here
    }
    
    variable "account_number" {
      default = "123456789"
    }
    
    variable "arg_map" {
      type = map(list(string))
      default = {
        "key1" = ["a", "b"]
        "key2" = ["d", "e"]
        "key3" = ["g", "h"]
        "key4" = ["j", "k"]
      }
    }
    
    resource "aws_ssm_document" "sync_epv2asm" {
      name          = "sync_epv2asm"
      document_type = "Automation"
    
      content = templatefile("${path.module}/ssm_document_template.tftpl", {
        account_number = var.account_number
        arg_map        = var.arg_map
      })
    }
    

    ssm_document_template.tftpl

    ${jsonencode({
      "schemaVersion": "0.3",
      "description": "My description.",
      "mainSteps": [
        for key, values in arg_map:
        {
          "description": "Invoke Lambda Function ${key}",
          "name": "InvokeLambdaFunction${key}",
          "action": "aws:invokeLambdaFunction",
          "inputs": {
            "FunctionName": "arn:aws:lambda:us-east-1:${account_number}:function:hello:$LATEST",
            "InvocationType": "RequestResponse",
            "Payload": "{"key1": "${account_number}", "key2": "${values[0]}", "key3": "${values[1]}"}"
          }
        }
      ]
    })}
    

  2. Since you are trying to create a JSON document, templatefile is almost never enough by itself to achieve that. Based on the documentation for templatefile, you can also use the built-in jsonencode function with the template. It should look something like the following:

    ${jsonencode({
      "schemaVersion": "0.3",
      "description": "My description.",
      "mainSteps": [
        for key, values in arg_map:
        {
          "description": "Invoke Lambda Function ${key}",
          "name": "InvokeLambdaFunction${key}",
          "action": "aws:invokeLambdaFunction",
          "inputs": {
            "FunctionName": "arn:aws:lambda:us-east-1:${account_number}:function:hello:$LATEST",
            "InvocationType": "RequestResponse",
            "Payload": "{"key1": "${account_number}", "key2": "${values[0]}", "key3": "${values[1]}"}"
          }
        }
      ]
    })}
    

    The plan output shows the result like this:

      # aws_ssm_document.sync_epv2asm will be created
      + resource "aws_ssm_document" "sync_epv2asm" {
          + arn              = (known after apply)
          + content          = jsonencode(
                {
                  + description   = "My description."
                  + mainSteps     = [
                      + {
                          + action      = "aws:invokeLambdaFunction"
                          + description = "Invoke Lambda Function key1"
                          + inputs      = {
                              + FunctionName   = "arn:aws:lambda:us-east-1:123456789:function:hello:$LATEST"
                              + InvocationType = "RequestResponse"
                              + Payload        = jsonencode(
                                    {
                                      + key1 = "123456789"
                                      + key2 = "a"
                                      + key3 = "b"
                                    }
                                )
                            }
                          + name        = "InvokeLambdaFunctionkey1"
                        },
                      + {
                          + action      = "aws:invokeLambdaFunction"
                          + description = "Invoke Lambda Function key2"
                          + inputs      = {
                              + FunctionName   = "arn:aws:lambda:us-east-1:123456789:function:hello:$LATEST"
                              + InvocationType = "RequestResponse"
                              + Payload        = jsonencode(
                                    {
                                      + key1 = "123456789"
                                      + key2 = "d"
                                      + key3 = "e"
                                    }
                                )
                            }
                          + name        = "InvokeLambdaFunctionkey2"
                        },
                    ]
                  + schemaVersion = "0.3"
                }
            )
          + created_date     = (known after apply)
          + default_version  = (known after apply)
          + description      = (known after apply)
          + document_format  = "JSON"
          + document_type    = "Automation"
          + document_version = (known after apply)
          + hash             = (known after apply)
          + hash_type        = (known after apply)
          + id               = (known after apply)
          + latest_version   = (known after apply)
          + name             = "sync_epv2asm"
          + owner            = (known after apply)
          + parameter        = (known after apply)
          + platform_types   = (known after apply)
          + schema_version   = (known after apply)
          + status           = (known after apply)
          + tags_all         = (known after apply)
        }
    

    I’ve trimmed the example to use only two keys, but this should work for any number of keys.

    NOTE: You also have to append the Lambda version to the Lambda ARN, either using $LATEST or a version number, otherwise, the SSM document will throw an error:

    Error: creating SSM Document (sync_epv2asm): InvalidDocumentContent: Input arn:aws:lambda:us-east-1:123456789:function:hello failed to match regex defined in document: (arn:aws)?(-[a-z]+)?(-[a-z]+)?(:lambda:)?([a-z]{2}-[a-z]+(-[a-z]+)?-d{1}:)?(d{12}:)?(function:)?([a-zA-Z0-9-]+)(:($LATEST|[a-zA-Z0-9-]+))?.

    Apply output:

    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
      + create
    
    Terraform will perform the following actions:
    
      # aws_ssm_document.sync_epv2asm will be created
      + resource "aws_ssm_document" "sync_epv2asm" {
          + arn              = (known after apply)
          + content          = jsonencode(
                {
                  + description   = "My description."
                  + mainSteps     = [
                      + {
                          + action      = "aws:invokeLambdaFunction"
                          + description = "Invoke Lambda Function key1"
                          + inputs      = {
                              + FunctionName   = "arn:aws:lambda:us-east-1:123456789:function:hello:$LATEST"
                              + InvocationType = "RequestResponse"
                              + Payload        = jsonencode(
                                    {
                                      + key1 = "123456789"
                                      + key2 = "a"
                                      + key3 = "b"
                                    }
                                )
                            }
                          + name        = "InvokeLambdaFunctionkey1"
                        },
                      + {
                          + action      = "aws:invokeLambdaFunction"
                          + description = "Invoke Lambda Function key2"
                          + inputs      = {
                              + FunctionName   = "arn:aws:lambda:us-east-1:123456789:function:hello:$LATEST"
                              + InvocationType = "RequestResponse"
                              + Payload        = jsonencode(
                                    {
                                      + key1 = "123456789"
                                      + key2 = "d"
                                      + key3 = "e"
                                    }
                                )
                            }
                          + name        = "InvokeLambdaFunctionkey2"
                        },
                    ]
                  + schemaVersion = "0.3"
                }
            )
          + created_date     = (known after apply)
          + default_version  = (known after apply)
          + description      = (known after apply)
          + document_format  = "JSON"
          + document_type    = "Automation"
          + document_version = (known after apply)
          + hash             = (known after apply)
          + hash_type        = (known after apply)
          + id               = (known after apply)
          + latest_version   = (known after apply)
          + name             = "sync_epv2asm"
          + owner            = (known after apply)
          + parameter        = (known after apply)
          + platform_types   = (known after apply)
          + schema_version   = (known after apply)
          + status           = (known after apply)
          + tags_all         = (known after apply)
        }
    
    Plan: 1 to add, 0 to change, 0 to destroy.
    
    Do you want to perform these actions?
      Terraform will perform the actions described above.
      Only 'yes' will be accepted to approve.
    
      Enter a value: yes
    
    aws_ssm_document.sync_epv2asm: Creating...
    aws_ssm_document.sync_epv2asm: Creation complete after 0s [id=sync_epv2asm]
    
    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search