Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a function to find the index of an element in a list. #2704

Merged
merged 1 commit into from
Aug 12, 2015
Merged

Add a function to find the index of an element in a list. #2704

merged 1 commit into from
Aug 12, 2015

Conversation

thegedge
Copy link
Contributor

We've hacked around not being able to pass maps as inputs to modules with interpolations akin to the following (newlines and comments added for easier reading):

foo = "${
  element(
    split(",", var.values),
    length(split(  // length of the list by chomping everything from the key onwards
      ",",
      replace(var.keys, "/${var.key}.*/", "")
    )) - 1
  )
}"

In essence, we chomp everything in a comma-separated key list starting from the key we're searching for, split the result, find the length of that list minus one, and use that as the index into the comma-separated values list. With an index function we can at least get rid of the replace madness and make our intent clearer, with the above now looking something like this:

foo = "${
  element(
    split(",", var.values),
    index(split(",", var.keys), var.key)
  )
}"

@dalehamel
Copy link

👍 this is a much needed feature. The original syntax for this is super obfuscated, would be much nicer with this index function.

@dalehamel
Copy link

@catsby @phinze can you guys get some 👀 on this? ❤️

@dalehamel
Copy link

FYI this works around #1336

@phinze
Copy link
Contributor

phinze commented Jul 14, 2015

Looks good on first glance! Will give it a closer review and a merge within the next few days. 👍

@dalehamel
Copy link

Looks good on first glance! Will give it a closer review and a merge within the next few days

❤️ that's good to hear, we've already started programming against this and FWIW it's working well for us, solving the fundamental 'can't pass maps between modules' problem, albeit in a fairly clumpsy way.

Perhaps we could consider adding another function, something like 'lookup(list, list, key)' that would build a map from two parallel lists? This is a very common pattern for us, and I think others from what I hear in IRC and from reading through issues.

@mgresko
Copy link

mgresko commented Aug 11, 2015

any update on merge?

@pll
Copy link

pll commented Aug 11, 2015

Can we get this merged? All checks have passed and it's up to date with the base.

@phinze
Copy link
Contributor

phinze commented Aug 12, 2015

Sorry for the delay on this - folks!

LGTM, merging now.

phinze added a commit that referenced this pull request Aug 12, 2015
Add a function to find the index of an element in a list.
@phinze phinze merged commit cdcef1c into hashicorp:master Aug 12, 2015
@thegedge thegedge deleted the add-index-function branch August 13, 2015 13:56
@pll
Copy link

pll commented Aug 13, 2015

Awesome! Thanks so much guys!

@thegedge
Copy link
Contributor Author

Ahhh, with our own build including this commit I pretty much forgot about the PR. Thanks for merging it in, @phinze!

@bhourigan
Copy link

I'm running into this issue, but I'm not clear on how this can be used. Would someone be able to provide an example? I have one variable which is a map, but the citation in comment 0 references 3 variables.

foo = "${
  element(
    split(",", var.values),
    index(split(",", var.keys), var.key)
  )
}"

If I have the following map:

variable "zones" {
    default = {
        zone-0 = "us-east-1a"
        zone-1 = "us-east-1b"
    }
}

How could I just pass just us-east-1a and us-east-1b?

@thegedge
Copy link
Contributor Author

thegedge commented Oct 4, 2015

You have two options: 1) you can get rid of the map and replace it with two lists, or 2) maybe try using the undocumented keys and values functions. It looks like they both take one argument which is the name of a mapping (e.g., in your case, ${values("zones")}).

@bhourigan
Copy link

@thegedge 👍 thanks!

@Shruti29
Copy link

I am trying to do the same thing. But I am getting syntax parse error.
I am almost sure I am not doing this right.
I have map defined in the tfvars file as shown below:

example.0 = "value1"
example.1 = "value2"
count = 2

I am trying to pass it to a module so that I can use it in the template file as shown below.

resource "aws_instance" "exp" {
count = "${var.count}"
text1 = "${lookup(var.example, count.index)}"
}

In the module function:

module "sample_module" {
example = "${element(split(",", "${values(example)}"),index(split(",", "${keys(example)""}), "1"))}"
}

Any suggestion would be really helpful :)

@thegedge
Copy link
Contributor Author

@Shruti29 in your case, since the keys are just integers coming from the count value, I'd just pass in a list of the values and use the element function. E.g.:

In your module:

resource "aws_instance" "exp" {
  count = "${var.count}"
  text1 = "${element(split(",", var.example), count.index)}"
}

In your tf config that instantiates that module:

module "sample_module" {
  example = "value1,value2"
}

@Shruti29
Copy link

@thegedge Of course! This works! Thank you. 👍

@Shruti29
Copy link

@thegedge Using a list instead of maps made it less readable. Since the list can go on to contain 20-30 values.
Basically I have something like this in the tfvars file:

details.hostname1 = "10.0.0.1"
details.hostname2 = "10.0.0.2"

So the workaround I did was something like this

module "type_host" {
   host_names = "${replace(keys(var.details),"B780FFEC-B661-4EB8-9236-A01737AD98B6",",")}"
   host_ips = "${replace(values(var.details),"B780FFEC-B661-4EB8-9236-A01737AD98B6",",")}"
}

And in the main.tf

resource "aws_instance" "hosts" {
    ....
    count = 2
    private_ip = "${element(split(",", var.host_ips), count.index+1)}"
    tags {
       Name = "${element(split(",", var.host_names), count.index+1)}"
    }
   ...
}

This works perfectly well. But I am hardcoding the string delimeter **"B780FFEC-B661-4EB8-9236-A01737AD98B6"** to create a comma separated string (?list) in the replace module.

Cant seem to think of a cleaner way to use interpolation and create a comma separated string(?list) of keys and value.

Any better way that I am missing here ?

@thegedge
Copy link
Contributor Author

@Shruti29 You should be able to just do, for example, ${join(",", keys(var.details))}.

P.S. I highly recommend checking out the #terraform-tool IRC channel on Freenode. A lot of helpful people in there to help with these kinds of questions 😄

@Shruti29
Copy link

@thegedge I agree :) thanks

@ghost
Copy link

ghost commented Apr 26, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Apr 26, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants