Replace value in list(map(string))

Hi all. I’m new here and I am faced with the problem of substitution values in maps.

Let’s presume I have such a variable:

variable "c1" {
  default = [
    {
      "path_pattern" : "/static1/*",
      "target_origin_id" : "s3_bucket_one",
      "viewer_protocol_policy" : "redirect-to-https",
      "allowed_methods" : [
        "GET",
        "HEAD",
        "OPTIONS"
      ],
      "cached_methods" : [
        "GET",
        "HEAD"
      ],
      "compress" : true,
      "query_string" : true,
      "cache_policy_id" : "7-days"
    },
    {
      "path_pattern" : "/static2/*",
      "target_origin_id" : "s3_bucket_one",
      "viewer_protocol_policy" : "redirect-to-https",
      "allowed_methods" : [
        "GET",
        "HEAD",
        "OPTIONS"
      ],
      "cached_methods" : [
        "GET",
        "HEAD"
      ],
      "compress" : true,
      "query_string" : true,
      "cache_policy_id" : "3-days"
    }
  ]
}

Next, I need to take the value of each cache_policy_id and get it through the data aws_cloudfront_cache_policy
I’m doing it with the code:

data "aws_cloudfront_cache_policy" "id" {
  name = local.c3[0]
}

locals {
  c3 = [for i, v in var.c1 : v["cache_policy_id"]]
}

But I presume it wouldn’t work with more than 1 policy in the variable above. (So probably you can assist with this too)

And the main problem is to have a variable with the next values:

variable "c1" {
  default = [
    {
      "path_pattern" : "/static/*",
      "target_origin_id" : "s3_bucket_one",
      "viewer_protocol_policy" : "redirect-to-https",
      "allowed_methods" : [
        "GET",
        "HEAD",
        "OPTIONS"
      ],
      "cached_methods" : [
        "GET",
        "HEAD"
      ],
      "compress" : true,
      "query_string" : true,
      "cache_policy_id" : "8765-4322-1234-5678"
    },
    {
      "path_pattern" : "/static2/*",
      "target_origin_id" : "s3_bucket_one",
      "viewer_protocol_policy" : "redirect-to-https",
      "allowed_methods" : [
        "GET",
        "HEAD",
        "OPTIONS"
      ],
      "cached_methods" : [
        "GET",
        "HEAD"
      ],
      "compress" : true,
      "query_string" : true,
      "cache_policy_id" : "1234-5678-9012-3456"
    }
  ]
}

So the cache_policy_id is represented with its id, not the name itself. And all other information should be as it was provided to the variable.

Any ideas on how can it be done?

You already have a list so you don’t need to that c3 local. The entries can be addressed directly, and is one of the main benefits of using a list over a map.

var.c1[*].cache_policy_id

So I think this is what you want

locals {
   cache_policy_id = {
     "7-days" = "8765-4322-1234-5678"
     "3-days" = "1234-5678-9012-3456"
  }
}
data "aws_cloudfront_cache_policy" "id" {
  for_each = toset(var.c1[*].cache_policy_id)
  name     = local.cache_policy_id[each.key]
}

Thanks, @cregkly for the reply, but in this case, I should already know those ids, and they are created dynamically, so I have no clue about the ids themselves. That’s the main problem, substituting the name in value with its id taken from the data object.

You explanation of the problem wasn’t very clear. I can only work with the information you share.

I wasn’t sure what the relationship between the two versions of var.c1 was or how they were set.

This might be an xy problem

Ok, got your point @cregkly.

So to be clear:

In an AWS account, we have several cache policies for CloudFront distributions. When developers want to create CDN they should provide some information for ordered_cache_behavior. One field is calling cache_policy_id and this id is being generated by the AWS per policy.

We have several AWS accounts and in all of them I’ve created the same policies (same names) to avoid misconfiguration, I want developers to provide only policy names (1-day, 3-days, 7-days, etc.) and inside my module, it will be converted to the ids.

So the variable can have 1 to infinite numbers of behaviors. And also it can have or not have this cache_policy_id. But right now I’m considering only that the field cache_policy_id is present in all behaviors.

I would have a terraform root module that creates the cache policies with your required names, and then store them in SSM parameter store.

Then instead of the local table lookup do the lookup against the SSM parameter.

You could also use secrets manager for the same thing, in which case you could store them all in one secret as a json map.

Even though, I have to substitute this value somehow. So how can I iterate through the map and change the value?

My gut says it is possible, but the local with for loops will be complicated.

I would consider changing the variable into an object and work with the individual values at the time you use them and update them then.

There are two parts to the problem here:

  1. Resolving the names to IDs.

@cregkly already nearly provided an answer, but there was a misunderstanding and it was a bit more complicated than it needed to be. I think all you need there is:

data "aws_cloudfront_cache_policy" "lookup" {
  for_each = toset(var.c1[*].cache_policy_id)
  name     = each.key
}
  1. Merging the acquired IDs back in to the overall data structure

As @cregkly says, the for constructs can get a bit complicated, but this isn’t too bad a case. I think it can be done relatively simply with

[for entry in var.c1: merge(entry, {cache_policy_id =
  data.aws_cloudfront_cache_policy.lookup[entry.cache_policy_id].id}]
1 Like

Thanks a lot, @maxb, it works for me.

Maybe you can advise how to make it with if too?

So let’s presume there is several policies with cache_policy_id and several without. Is it possible to check

if cache_policy_id != null:
  replace with the id
else:
 skip this field

if is it not possible, then I will tell devs to use policies all the time.

Thanks.