Home > Software engineering >  Terraform dynamic block in a template for nested values
Terraform dynamic block in a template for nested values

Time:11-29

I would be appreciated for help or any idea on this task. As you may see in locals.var Application and Environment keys have different values. But as the result JSON file, these values are similar for both keys. How to set correct corresponding values?

I guess value_tag should be set dynamically. for_each - creates a lot of files. Dynamically block looks don't work for this. Also, I need it all in one JSON file.

Incorrect result

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "ec2:*",
            "Resource": "*",
            "Condition": {
                "ForAllValues:StringEquals": {
                    "aws:TagKeys": [
                        "Application",
                        "Environment"
                    ]
                },
                "StringEqualsIfExists": {
                    "aws:RequestTag/Application": [
                        "development",
                        "production"
                    ],
                    "aws:RequestTag/Environment": [
                        "development",
                        "production"
                    ]
                }
            }
        }
    ]
}

Correct result should be like this

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "ec2:*",
            "Resource": "*",
            "Condition": {
                "ForAllValues:StringEquals": {
                    "aws:TagKeys": [
                        "Application",
                        "Environment"
                    ]
                },
                "StringEqualsIfExists": {
                    "aws:RequestTag/Application": [
                        "app-01",
                        "app-02"
                    ],
                    "aws:RequestTag/Environment": [
                        "development",
                        "production"
                    ]
                }
            }
        }
    ]
}
locals {
  enforce_tag = {
    Environment = {
      env01 = "development"
      env02 = "production"
    }
      Application = {
        app01 = "app-01"
        app02 = "app-02"
      }
  }
}
data "template_file" "enforcetags" {
  template = templatefile("${path.module}/enforcetags.tpl",
    {
      key_tag = [for key, value in local.enforce_tag : key]
      value_tag   = local.enforce_tag.Environment
    }
  )
}

Template file:

{
    "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}" => value_tag
                    })
                }
            }
        }
    ]
}

CodePudding user response:

You are close, but a few things are missing. First comes some explanation, below you can find what I believe is the solution to your problem.

With this:

  value_tag   = local.enforce_tag.Environment

you only ever touch the Environment map, there's no reference to the Application map. Instead you can use this to pass both these maps (a map of 2 maps):

      value_tag   = local.enforce_tag

To avoid confusion let's call them:

  • the big map called enforce_tag consisting of the following:
  • the first small map (for environments)
  • the second small map (for applications)

Then in order to use it properly in the template, when iterating over your tags (Application, Environment) you need to take respective small map for relevant tag:

value_tag[key]

And the last thing is that you don't seem to ever use the keys in the small maps (i.e. nowhere in your desired output I can see env01 or app02). Nevertheless, maybe you need it this way for other reasons. If so, you are interested only in the values of the small maps. Not the whole maps, i.e.

values(value_tag[key])

In short, the following should work:

Terraform

data "template_file" "enforcetags" {
  template = templatefile("${path.module}/enforcetags.tpl",
    {
      key_tag = [for key, value in local.enforce_tag : key]
      value_tag   = local.enforce_tag
    }
  )
}

Template fragment

"StringEqualsIfExists": ${jsonencode(
        {for key in key_tag: "aws:RequestTag/${key}" => values(value_tag[key])}
    )
}
  • Related