Dynamically getting values from Terraform output and pass them by index - amazon-web-services

Terraform greenhorn here. I want to output and dynamically add only the first 2 subnets in the vpc_zone_identifier below. But I can't get it by index, is asking for a name.
vpc_zone_identifier = [module.subnet[0].subnet_id, module.subnet[1].subnet_id]
Here is what I tried.
Any suggestion is appreciated.
resource "aws_subnet" "this" {
vpc_id = var.vpc_id
cidr_block = var.cidr_block_subnet
map_public_ip_on_launch = var.map_public_ip_on_launch
tags = var.tags
}
module "subnet" {
source = "./aws_modules/subnet"
for_each = {for key, value in var.subnet_settings: value.cidr_block_subnet => value}
vpc_id = module.vpc.vpc_id
cidr_block_subnet = each.key
map_public_ip_on_launch = each.value.map_public_ip_on_launch
tags = var.tags
depends_on = [module.vpc]
}
variable "subnet_settings" {
type = list(object({
cidr_block_subnet = string
map_public_ip_on_launch = bool
}))
}
subnet_settings = [
{
cidr_block_subnet = "10.1.1.0/24"
map_public_ip_on_launch = false
},
{
cidr_block_subnet = "10.1.2.0/24"
map_public_ip_on_launch = false
},
{
cidr_block_subnet = "10.1.3.0/24"
map_public_ip_on_launch = false
}
]
output "subnet_id" {
value = aws_subnet.this.id
}
resource "aws_autoscaling_group" "autosys" {
desired_capacity = var.node-count
max_size = var.node-count
min_size = var.node-count
name = var.asg-name
vpc_zone_identifier = [module.subnet[0].subnet_id, module.subnet[1].subnet_id]
launch_template {
id = aws_launch_template.autosys.id
version = "$Latest"
}
}

In a for expression, you can initialize two variables within the scope of the lambda when iterating on a list type. In this situation, the first variable will be assigned the index of the iteration. Therefore, we can modify the for_each meta-argument of your module:
module "subnet" {
source = "./aws_modules/subnet"
for_each = {for idx, subnet in var.subnet_settings: idx => subnet}
vpc_id = module.vpc.vpc_id
cidr_block_subnet = each.value.cidr_block_subnet
map_public_ip_on_launch = each.value.map_public_ip_on_launch
tags = var.tags
depends_on = [module.vpc]
}
and now the module key will be the index of the var.subnet_settings. Correspondingly, the module output namespace will now be module.<declared_name>["<index>"].<output_name>. Therefore, we can modify the value for the vpc_zone_identifier argument to have keys of string type instead of number type:
vpc_zone_identifier = [module.subnet["0"].subnet_id, module.subnet["1"].subnet_id]
and now the passed value to the argument will be the first two subnets as desired.

Related

Create aws_subnet using for_each loop in Terraform

I create resource "aws_subnet" using count expression. I'd like to use for_each instead of count, but need help with the correct syntax.
variable "privateSubnetCIDR" {
type = list(string)
default = ["10.0.1.0/24","10.0.2.0/24"]
}
data "aws_availability_zones" "availableAZ" {}
resource "aws_subnet" "privatesubnet" {
count = length(var.privateSubnetCIDR)
cidr_block = tolist(var.privateSubnetCIDR)[count.index]
vpc_id = aws_vpc.dev_vpc.id
map_public_ip_on_launch = false
availability_zone = data.aws_availability_zones.availableAZ.names[count.index]
tags = {
name = "${var.environment}-privatesubnet-${count.index + 1}"
AZ = data.aws_availability_zones.availableAZ.names[count.index]
Environment = "${var.environment}-privatesubnet"
}
}
One way to use for_each would be as follows:
resource "aws_subnet" "privatesubnet" {
for_each = toset(var.privateSubnetCIDR)
cidr_block = each.key
vpc_id = aws_vpc.dev_vpc.id
map_public_ip_on_launch = false
availability_zone = element(data.aws_availability_zones.availableAZ.names, index(var.privateSubnetCIDR, each.key))
tags = {
name = "environment-privatesubnet-${index(var.privateSubnetCIDR, each.key) + 1}"
AZ = element(data.aws_availability_zones.availableAZ.names, index(var.privateSubnetCIDR, each.key))
Environment = "environment-privatesubnet"
}
}
The simpler way would be:
resource "aws_subnet" "privatesubnet" {
for_each = {for idx, val in var.privateSubnetCIDR: idx => val}
cidr_block = each.value
vpc_id = aws_vpc.dev_vpc.id
map_public_ip_on_launch = false
availability_zone = element(data.aws_availability_zones.availableAZ.names, each.key)
tags = {
name = "environment-privatesubnet-${each.key + 1}"
AZ = element(data.aws_availability_zones.availableAZ.names, each.key)
Environment = "environment-privatesubnet"
}
}

Not able to launch one ec2 instance with count 1 and subnets(2) are created from the for_each loop

Subnets are created from the below code:
resource "aws_subnet" "Private" {
for_each = var.subnet_cidrs_private
vpc_id = aws_vpc.vpc.id
cidr_block = each.value
availability_zone = each.key
tags = {
Name = format("%s-%s-%s", var.environment, "Pri-Subnet", substr(each.key, -2, -1))
}
}
Trying to create 1 instance with count parameter.
resource "aws_instance" "visualapp" {
instance_type = "t2.micro"
ami = var.visualapp-ami
key_name = var.key_pair
vpc_security_group_ids = [aws_security_group.visualapp.id]
subnet_id = element(aws_subnet.Private[*].id, count.index)
count = 1
tags = {
Name = format("%s-%s", var.environment, "mage-varnish-${count.index + 1}")
}
}
Getting the below error:
subnet_id = element(aws_subnet.Private[*].id, count.index)
This object does not have an attribute named "id".
Variables:
variable "subnet_cidrs_private" {
default = {
eu-west-1a = "172.26.3.0/24",
eu-west-1b = "172.26.4.0/24"
}
}
Can anyone help me?.
element would be used if you had used count. Since you've used for_each you need to use values first:
subnet_id = element(values(aws_subnet.Private)[*].id, count.index)

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
}
}

how to add adding multiple aws target groups to multiple autoscaling groups using terraform

i am creating 4 target groups and 4 nlb , count is based on number of screenrecorders (01-04). Also 3 ASG one for each AZ. I want to add 4 target groups to each ASG.
for example : fw_asg_az1 should have 4 target groups attached, same way for fw_asg_az2 and fw_asg_az3 same target groups needs to be attached.
in future if i add server05 in my variable and re run terraform, newly created target group should be added to all three ASG's
variables in my main.tf
screenrecorders = [
"server01",
"server02",
"server03",
"server04"
]
variable.tf
variable "screenrecorders" {
description = "List of screen recorders"
type = list(string)
}
main.tf
resource "aws_autoscaling_group" "fw_asg_az1" {
name = "${var.deployment_prefix}-asg-${var.az1}"
availability_zones = [var.az1]
health_check_grace_period = 300
health_check_type = "ELB"
default_cooldown = 600
launch_template {
id = aws_launch_template.fw_launch_template_az1.id
version = "$Latest"
}
min_size = 1
max_size = 5
tag {
key = "Name"
value = "${var.deployment_prefix}-${var.az1}"
propagate_at_launch = true
}
target_group_arns = ["${aws_lb_target_group.tg_scr_vr.arn}"]
depends_on = [aws_lb_target_group.tg_scr_vr]
}
resource "aws_autoscaling_group" "fw_asg_az2" {
name = "${var.deployment_prefix}-asg-${var.az2}"
availability_zones = [var.az2]
health_check_grace_period = 300
health_check_type = "ELB"
default_cooldown = 600
launch_template {
id = aws_launch_template.fw_launch_template_az2.id
version = "$Latest"
}
min_size = 1
max_size = 5
tag {
key = "Name"
value = "${var.deployment_prefix}-${var.az2}"
propagate_at_launch = true
}
target_group_arns = ["${aws_lb_target_group.tg_scr_vr.arn}"]
depends_on = [aws_lb_target_group.tg_scr_vr]
}
resource "aws_autoscaling_group" "fw_asg_az3" {
name = "${var.deployment_prefix}-asg-${var.az3}"
availability_zones = [var.az3]
#availability_zones = [data.aws_availability_zones.all.names]
health_check_grace_period = 300
health_check_type = "ELB"
default_cooldown = 600
launch_template {
id = aws_launch_template.fw_launch_template_az3.id
version = "$Latest"
}
min_size = 1
max_size = 5
tag {
key = "Name"
value = "${var.deployment_prefix}-${var.az3}"
propagate_at_launch = true
}
target_group_arns = ["${aws_lb_target_group.tg_scr_vr.arn}"]
depends_on = [aws_lb_target_group.tg_scr_vr]
}
###############################################################################
# ELB Target Groups
###############################################################################
resource "aws_lb_target_group" "tg_scr_vr" {
count = length(var.screenrecorders)
name = "tg-${var.screenrecorders[count.index]}"
target_type = "instance"
protocol = "TCP"
port = "80"
vpc_id = var.vpc_id
health_check {
protocol = "TCP"
port = "traffic-port"
}
}
###############################################################################
# ELB
###############################################################################
resource "aws_lb" "lb_scr_vr" {
count = length(var.screenrecorders)
name = "lb-${var.screenrecorders[count.index]}"
load_balancer_type = "network"
internal = false
ip_address_type = "ipv4"
subnet_mapping {
subnet_id = var.az1_fw_public_subnet_id
}
subnet_mapping {
subnet_id = var.az2_fw_public_subnet_id
}
subnet_mapping {
subnet_id = var.az3_fw_public_subnet_id
}
}
error i am getting
Error: Missing resource instance key
on .terraform/modules/fw/main.tf line 147, in resource "aws_autoscaling_group" "fw_asg_az1":
147: target_group_arns = ["${aws_lb_target_group.tg_scr_vr.arn}"]
Because aws_lb_target_group.tg_scr_vr has "count" set, its attributes must
be accessed on specific instances.
For example, to correlate with indices of a referring resource, use:
aws_lb_target_group.tg_scr_vr[count.index]
i tried below as suggested in the error, still didn't work
aws_lb_target_group.tg_scr_vr[count.index]
i am new to terraform, still learning, any help from stackoverflow community is greatly appreciated.
Instead of
target_group_arns = ["${aws_lb_target_group.tg_scr_vr.arn}"]
it should be :
target_group_arns = aws_lb_target_group.tg_scr_vr[*].arn
if you want to associate all four TFs with a single ASG.

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
}