2
votes

i have an unusual issue with terraform.

i create two VPCs and within the same terraform script i want to add a private hosted zone.

i do the following: data "aws_vpcs" "foo" {} this is getting me the VPCs that are created in the region.

normally i can output the IDs of the VPCs like:

output "test" {
  value = data.aws_vpcs.foo.ids
}

this is giving me a list like:

[ "vpc-0c8446a2164b7d0af", "vpc-0e7c63c3f383d115d", ]

now, from this list i would like to get the 1st VPC id: "vpc-0c8446a2164b7d0af"

the problem is that it does not work. i try using the element function like: element(data.aws_vpcs.foo.ids, 0) element([data.aws_vpcs.foo.ids], 0)

i also try to assign this to a value like: data.aws_vpcs.foo.ids[0]

it does not work and i cannot find any other options on terraform to help me solve this.

i want to use the 1st VPC id to create a resource:

resource "aws_route53_zone" "private" {
 name = "example.com"

 vpc {
   vpc_id = data.aws_vpcs.foo.ids[0]
   }
}

so i can get the 1st VPC (it does not matter the order) from the List of VPCs i get in the region.

when i run terraform plan i get the error:

Error: Invalid index

 on main.tf line 25, in resource "aws_route53_zone" "private":
 25:     vpc_id = data.aws_vpcs.foo.ids[0]

This value does not have any indices.
3
What error do you get when you say it doesn't work? And can you edit your question to show the Terraform code for the aws_vpcs data source as well please?ydaetskcoR
You should edit your question to include the error, not post it in a comment.ydaetskcoR

3 Answers

5
votes

Terraform distinguishes between list values and set values, where a set is like the mathematical idea of a set where it's an unordered collection of values and you can ask whether a particular element is present in the set, and you can enumerate all of the elements in the set, but there are no indices or keys by which you can look up a single item.

The aws_vpcs data source returns a set of strings because there is no inherent ordering to VPCs in AWS: you can't take a pair of VPCs and decide which one is "first" without imposing your own sorting criteria.

However, if you don't have any particular sorting criteria and just wish to take any one of the returned VPCs, you can use the sort function to indicate that Terraform should sort the VPC id strings lexically, which will then produce a list you can take the first element of:

resource "aws_route53_zone" "private" {
  name = "example.com"

  vpc {
    vpc_id = sort(data.aws_vpcs.foo.ids)[0]
  }
}

Beware though that this is essentially just choosing one item from the set arbitrarily, so the result will be consistent as long as the list never changes but if a new item is added to the list it may end up selecting a different element if it happens to sort before the one it was selecting before.

For that reason, when using data sources to retrieve VPCs we usually want to retrieve a specific single VPC using the aws_vpc data source, using a specific enough query that it will only ever return the single VPC we need. Under this model, Terraform will return an error if there isn't exactly one result, which is often preferable to implicitly making an incorrect selection.

3
votes

element does not work since the vpc id list , is not a list rather a set of strings. So, the following does not work. otherwise there is no issue in this command.

 "${element(data.aws_vpcs.foo.ids, 0)}"

However, I found another way to get an element out of using sort. But again, it is sorted. If you are fine with any vpc id , then it should be fine.

I still prefer the suggestion by Falk, since you are trying to extract a vpc id within same script , so you can directly access the vpc and then get id.

Just as an exercise, I put the script below to access the id multiple ways. Happy scripting !

resource "aws_vpc" "vpc1" {
  cidr_block = "10.20.0.0/16"
  tags = {
    name = "tf_vpc"
  }
}

resource "aws_vpc" "vpc2" {
  cidr_block = "10.30.0.0/16"
  tags = {
    name = "tf_vpc_2"
  }
}


data "aws_vpcs" foo{}

output "test" {
  value = "${data.aws_vpcs.foo.ids}"
}
output "test1" {
  value = "${aws_vpc.vpc1.id}"
}

output "test2" {
  value = "${aws_vpc.vpc2.id}"
}

output "counter" {
  value = "${length(data.aws_vpcs.foo.ids)}"

}


output "myvpcID" {
  value = sort(data.aws_vpcs.foo.ids)[0]
}
2
votes

If you created your VPCs with terraform within the same script, then you do not need the data-provider. Instead you should access the ids from the resource directly. See this answer for a similar situation and more details on how to resolve this.