eazary
eazary

Reputation: 159

Terraform - Variable inside a variable

I would like to use a variable inside a variable.

This is my resource:

resource "aws_route" "vpc_peering_accepter" {
provider = "aws.accepter"
count = length(data.terraform_remote_state.vpc.outputs.${var.region}-vpc-private_routing_tables)
route_table_id = tolist(data.terraform_remote_state.vpc.outputs.${var.region}-vpc-private_routing_tables)[count.index]
destination_cidr_block = var.vpc_cidr
vpc_peering_connection_id = aws_vpc_peering_connection.peer.*.id[0]
}

Of course this one is not working. What's the best practice to do it?

Thanks, Elad

Upvotes: 2

Views: 2494

Answers (2)

Martin Atkins
Martin Atkins

Reputation: 74694

Because data.terraform_remote_state.vpc.outputs is a mapping, you can use either attribute syntax or index syntax to access the values inside:

  • Attribute syntax: data.terraform_remote_state.vpc.outputs.us-west-1-vpc-private_routing_tables
  • Index syntax: data.terraform_remote_state.vpc.outputs["us-west-1-vpc-private_routing_tables"]

An advantage of index syntax is that you can use any expression within those brackets as long as its result is a string. In particular, you can use the template interpolation syntax:

data.terraform_remote_state.vpc.outputs["${var.region}-vpc-private_routing_tables"]

With that said, in this sort of situation where you are producing the same information for a number of different objects -- regions, in this case -- it's more conventional to gather all of these values into a single mapping when you declare the output, so that these related values are explicitly grouped together in a single collection. For example:

output "vpc_private_routing_table_ids" {
  value = {
    us-east-1 = aws_route_table.us-east-1.id
    us-west-2 = aws_route_table.us-west-2.id
  }
}

Then from the perspective of the consumer -- that is, the module that is using data "terraform_remote_state" to access these outputs -- this appears as a simple map keyed by region:

data.terraform_remote_state.vpc.outputs.vpc_private_routing_table_ids[var.region]

If you are producing many different objects on a per-region basis then you might choose to gather all of their ids together into a single output, which might be more convenient to use elsewhere:

output "regions" {
  value = {
    us-east-1 = {
      vpc_id                 = aws_vpc.us-east-1.id
      subnet_ids             = aws_subnet.us-east-1[*].id
      private_route_table_id = aws_route_table.us-east-1.id
    }
    us-west-1 = {
      vpc_id                 = aws_vpc.us-west-1.id
      subnet_ids             = aws_subnet.us-west-1[*].id
      private_route_table_id = aws_route_table.us-west-1.id
    }
  }
}

...which would then appear as follows in the consumer module:

data.terraform_remote_state.vpc.outputs.regions[var.region].private_route_table_id

Ultimately you can structure your output values however you like, but I'd recommend choosing a shape that optimizes for clarity in the configuration that is referring to the data. That usually means making the referring expressions as simple as possible, and ideally avoiding complex expressions like string template syntax whenever possible.

Upvotes: 0

Andy Shinn
Andy Shinn

Reputation: 28543

You can combine Local Values with the lookup function to accomplish this.

In the following example the null datasource is mimicking data.terraform_remote_state.vpc.outputs:

variable "region" {
  default = "us-east1"
}

locals {
  vpc_private_routing_tables = "${var.region}-vpc-private_routing_tables"
}

data "null_data_source" "values" {
  inputs = {
    us-east1-vpc-private_routing_tables = "11111111"
    us-east2-vpc-private_routing_tables = "22222222"
  }
}

output "vpc_peering" {
  value = lookup(data.null_data_source.values.inputs, local.vpc_private_routing_tables)
}

Upvotes: 2

Related Questions