Terraform tuple to id for subnet - amazon-web-services

I got following output:
output "private_subnets" {
description = "List of IDs of private subnets"
value = module.vpc.private_subnets
}
which returns subnet ids like: [subnet-1***, subnet-2***, subnet-3***]
How can I use this in nlb resource?
dynamic "subnet_mapping" {
#for_each = { for k,v in module.vpc.private_subnets : k => v}
for_each = [for s in module.vpc.private_subnets : s]
content {
subnet_id = s
}
}
I have tried s.id, s.value, nothing works. I get following errors:
A reference to a resource type must be followed by at least one attribute access, specifying the resource name.

It should be:
dynamic "subnet_mapping" {
for_each = toset(module.vpc.private_subnets)
content {
subnet_id = subnet_mapping.value
}
}

Related

For_each and count in same terraform AWS resource

I'm trying to create "aws_route" in terraform iterating over a list of route_tables with vpc_peering of another service. The other service vpc destination_cidr_block is a list.
variable "route_tables" {
type = set(string)
description = "Set of route table entries eg : rt-1, rt-2 , rt-3"
}
variable "ext_service_destination_cidr_blocks"{
type = list(string)
description = "list of destination cidr blocks of external service, eg:[\"10.10.1.1/20\", \"10.2.10.1/10\"]"
}
resource "aws_route" "ext_service_route" {
// iterating over route tables [ rt-1, rt-2 , rt-3 ]
for_each = var.route_tables
route_table_id = each.key
// Iterating over cidr list
count = var.ext_service_destination_cidr_blocks
destination_cidr_block = var.ext_service_destination_cidr_blocks[count.index]
vpc_peering_connection_id = var.ext_service_peering_connection_id
}
Here, I would like to iterate over list of destination_cidr_block.
Simply put, I need a nested loop, count inside for_each.
I can't have both count and for_each in the same block, is there any workaround for this?
Or is there any way I can split this into two modules?
We can use setproduct to compute the Cartesian product of those two collections and create a map based on that. This map can be used to do for_each on it:
resource "aws_route" "ext_service_route" {
for_each = { for i, pair in tolist(setproduct(var.route_tables, var.ext_service_destination_cidr_blocks)) : "route-${i}" => { "name" : pair[0], "cidr" : pair[1] } }
route_table_id = each.value.name
destination_cidr_block = each.value.cidr
vpc_peering_connection_id = var.ext_service_peering_connection_id
}
You can also use mod operation to iterate over the two lists simultaneously (for example, when you are using older TF version that does not support for_each yet):
resource "aws_route" "ext_service_route" {
count = length(var.ext_service_destination_cidr_blocks) * length(var.route_tables)
route_table_id = element(var.route_tables, count.index + 1 % length(var.route_tables))
destination_cidr_block = element(var.ext_service_destination_cidr_blocks, count.index + 1 % length(var.ext_service_destination_cidr_blocks))
vpc_peering_connection_id = var.ext_service_peering_connection_id
}

Retrieve Subnet ids for map variables

I have created subnet as map variable for availability zone and CIDR block and
variable "public_subnets_list" {
type = map(any)
description = "Public Subnets"
default = {
"ap-south-1a" = "10.0.1.0/24"
"ap-south-1b" = "10.0.2.0/24"
}
}
This works fine for creating subnets under my custom VPC with below code
resource "aws_subnet" "public_subnet" {
depends_on = [
aws_vpc.terraform_vpc
]
for_each = tomap(var.public_subnets_list)
availability_zone = each.key
cidr_block = each.value
vpc_id = aws_vpc.terraform_vpc.id
tags = {
Name = "Public_Subnet_${each.key}"
}
}
How do I retrieve and display the subnet id created for respective AZs from output which i get from aws_subnet.public_subnet[*]
From the Terraform documentation, splat expressions do not work with resources that use the for_each argument.
To retrieve a list of subnet IDs using your Terraform configuration, you can do the following:
output "subnet_ids" {
value = [for subnet in aws_subnet.public_subnet : subnet.id]
}

Access the index of a map in for_each

I have a map that looks like this
variable "mysubnets" {
type = map(string)
default = {
"subnet1" = "10.1.0.0/24"
"subnet2" = "10.1.1.0/24"
}
}
In my module I'm trying to place subnets in different availability zones in the same vpc
data "aws_availability_zones" "azs" {
state = "available"
}
resource "aws_subnet" "test-subnets" {
for_each = var.mysubnets
cidr_block = "${each.value}"
vpc_id = aws_vpc.myvpc.id
availability_zone = data.aws_availability_zones.azs.names[index("${each.value}")]
tags = {
Name = "${each.key}"
}
}
I can get the key and value from the map no problem, but when trying to pick an availability zone I can't find how to change the value. Is there a way to get the index of a map, or create a counter for a number that increments?
Your data source is called azs, not available. So it should be:
availability_zone = data.aws_availability_zones.azs.names[index("${each.value}")]
Update:
To use index with your var.mysubnets you can do as follows:
resource "aws_subnet" "test-subnets" {
for_each = {for idx, subnet in keys(var.mysubnets):
idx => {
name = subnet
cidr = var.mysubnets[subnet]
}
}
cidr_block = each.value.cidr
vpc_id = aws_vpc.myvpc.id
availability_zone = element(data.aws_availability_zones.azs.names, each.key)
tags = {
Name = each.value.name
}
}

Terraform lunch EC2 condition based on count

Trying to lunch 1 or 2 instances based on a condition ( "single-target" )
ERROR im getting :
132: target_id = var.ec2-2
var.ec2-2 is empty tuple
Inappropriate value for attribute "target_id": string required.
the use of that variable :
resource "aws_lb_target_group_attachment" "b" {
target_id = var.ec2-2
}
outputs :
output "ec2-2_ot" {
value = aws_instance.ec2V2[*].id
description = "the value of the network module ec2-2 id"
}
variable :
variable "single-target" {
type=bool
default=false
}
main.tf :
module "network_module" {
ec2-2 = module.compute_module.ec2-2_ot
}
ec2 launch :
resource "aws_instance" "ec2V2" {
count = var.single-target ? 0 : 1
ami = var.ec2_ami
instance_type = var.ec2_type
associate_public_ip_address = true
key_name = var.key_pair
vpc_security_group_ids = [ var.vpc-sg ]
subnet_id = var.subnet-2
user_data = file("PATH/user-data.sh")
tags = {
Name = var.ec2-2_name
}
}
As the count has been already set to the resource you are trying to create, the returning result list would need to be accessed via Splat Expression mentioned in this terraform documentation.
If you need to choose specific index from the list of the result , then you can use the element function to do so
So in your case, if you need to output EC2 id it would be as follows.
output "thisisoutput" {
value = aws_instance.ec2V2[*].arn
}

Terraform fail due output for_each values

When I trying to dynamically allocate subnet ids by running this tf file:
data "aws_availability_zones" "available" {}
resource "aws_vpc" "wpl_vpc" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
tags = {
Name = "WPL-VPC"
CreatedBy = var.created_by
}
}
resource "aws_subnet" "wpl_public_subnet" {
for_each = { for index, az_name in data.aws_availability_zones.available.names : index => az_name }
vpc_id = aws_vpc.wpl_vpc.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, each.key + 10)
availability_zone = data.aws_availability_zones.available.names[each.key]
map_public_ip_on_launch = true
tags = {
Name = "WPL-PublicSubnet"
CreatedBy = var.created_by
}
}
output "wpl_public_subnet_ids" {
value = [aws_subnet.wpl_public_subnet.*.id]
}
I get an error:
Error: Unsupported attribute
on ../modules/vpc/outputs.tf line 5, in output "wpl_public_subnet_ids":
5: value = [aws_subnet.wpl_public_subnet.*.id]
This object does not have an attribute named "id".
However, everything works like charm, if I replace the output with this one:
aws_subnet.wpl_public_subnet.0.id
How could I output all subnet ids values?
Since you are using for_each, you will have a map, not a list. Thus, you have to get values first, before getting the ids. Also you don't need extra square brackets:
output "wpl_public_subnet_ids" {
value = values(aws_subnet.wpl_public_subnet)[*].id
}