2

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
                    })
                }
            }
        }
    ]
}

1 Answer 1

1

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])}
    )
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you a lot, Grzegorz. This great lesson for me looks like a kind of magic for me :) I used env01 and app02 in locals because don't know how to better organize these variables.
The "small maps" could be converted to lists. So that your locals definition is simplified and the code in StringEqualsIfExists too.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.