Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terraform template jsonencoding with iterate

I'm looking for a solution for this template to get the correct JSON file. As you may see in the sections, aws:RequestTag/*** lacks commas at the end. If I use the comma in the template, then I will have an unnecessary comma at the end of the last string.

I wonder the ${jsonencode()} should help, but I'm still not realizing how it's using with %{ for key in key_tag ~} together.

I would be appreciated for any help.

Terraform:

resource "local_file" "enforcetags" {
  content = templatefile("${path.module}/enforcetags.tpl",
    {
      key_tag = ["development_prod", "production_prod", "rnd_prod"]
    }
  )
  filename = "./enforce_tags.json"
}

Template:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": "ec2:*",
                "Resource": "*",
                "Condition": {
                    "ForAllValues:StringEquals": {
                        "aws:TagKeys": ${jsonencode([for key in key_tag : "${key}"])}
                    },
                    "StringEqualsIfExists": {
%{ for key in key_tag ~}
                    "aws:RequestTag/${key}": ${jsonencode([for key in key_tag : "${key}"])}
%{ endfor ~}
                    }
                }
            }
        ]
    }

Output:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": "ec2:*",
                "Resource": "*",
                "Condition": {
                    "ForAllValues:StringEquals": {
                        "aws:TagKeys": ["development_prod","production_prod","rnd_prod"]
                    },
                    "StringEqualsIfExists": {

                       "aws:RequestTag/development_prod": ["development_prod","production_prod","rnd_prod"]
                       "aws:RequestTag/production_prod": ["development_prod","production_prod","rnd_prod"]
                       "aws:RequestTag/rnd_prod": ["development_prod","production_prod","rnd_prod"]
                    
                    }
                }
            }
        ]
    }
like image 637
Rostyslav Malenko Avatar asked Oct 25 '25 03:10

Rostyslav Malenko


2 Answers

Here you seem to have run into the typical challenges of trying to generate valid JSON using string concatenation, which is what template interpolation effectively is. This problem is common enough to have a dedicated section in the templatefile documentation page: Generating JSON or YAML from a template.

You can avoid this sort of friction by following the advice in that section and generating the entire JSON data structure with jsonencode, rather than just some sub-sections of it as you did in the example you shared. You can use Terraform's normal expression operators to generate dynamic parts as needed. For example:

${jsonencode({
  Version = "2012-10-17"
  Statement = [
    {
      Sid      = "VisualEditor0"
      Effect   = "Allow"
      Action   = "ec2:*"
      Resource = "*"
      Condition = {
        "ForAllValues:StringEquals" = {
          "aws:TagKeys" = key_tag
        },
        "StringEqualsIfExists": {
          for key in key_tag : "aws:RequestTag/${key}" => key_tag
        }
      }
    }
  ],
})}

With this approach you define the data structure using Terraform syntax instead of JSON syntax, and let jsonencode be responsible for encoding it to the equivalent valid JSON. jsonencode is guaranteed to always produce valid JSON, including commas in all of the expected places, and Terraform's object syntax doesn't require commas between attribute definitions anyway so you don't need to worry about writing them yourself.

(I also simplified your for expressions a little in the above, although that wasn't directly related to your question. In particular, if key_tag is already a sequence type anyway then [for key in key_tag : "${key}"] is just the same as writing key_tag directly, because there's no actual transformation occurring there.)

like image 108
Martin Atkins Avatar answered Oct 26 '25 18:10

Martin Atkins


To have a comma at the end, except the last string it should be:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": "ec2:*",
                "Resource": "*",
                "Condition": {
                    "ForAllValues:StringEquals": {
                        "aws:TagKeys": ${jsonencode([for key in key_tag : "${key}"])}
                    },
                    "StringEqualsIfExists": ${jsonencode(
                                {for key in key_tag: "aws:RequestTag/${key}" => key_tag})}
                }
            }
        ]
    }
like image 22
Marcin Avatar answered Oct 26 '25 17:10

Marcin