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"
}
}
Related
I'm running terraform that creates 4 Subnets , 2 of the subnets are public and the name starts with "public".
Subnet code
Private subnet
resource "aws_subnet" "private-subnet-az-a" {
availability_zone = "us-east-1a"
vpc_id = aws_vpc.vpc-homework2.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = false
}
resource "aws_subnet" "private-subnet-az-b" {
availability_zone = "us-east-1b"
vpc_id = aws_vpc.vpc-homework2.id
cidr_block = "10.0.2.0/24"
map_public_ip_on_launch = false
}
## Public subnet
resource "aws_subnet" "public-subnet-az-a" {
availability_zone = "us-east-1a"
vpc_id = aws_vpc.vpc-homework2.id
cidr_block = "10.0.3.0/24"
map_public_ip_on_launch = true
}
resource "aws_subnet" "public-subnet-az-b" {
availability_zone = "us-east-1b"
vpc_id = aws_vpc.vpc-homework2.id
cidr_block = "10.0.4.0/24"
map_public_ip_on_launch = true
}
When creating Load Balancer I need to attach it both public sunsets - i have tries the For as you can see in the example, but it is not working
## Create lb code ; [for subnet in aws_subnet.public-[*].id : subnet]
resource "aws_lb" "nlb" {
name = "nlb-web"
internal = false
load_balancer_type = "network"
subnets = [for subnet in aws_subnet.public-[*].id : subnet]
}
You can't construct such a loop. The proper way of doing this is to create a map and use for_each to create your subnets:
variable "subnets" {
default = {
private-subnet-az-a = {
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = false
availability_zone = "us-east-1a"
}
private-subnet-az-a = {
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = false
availability_zone = "us-east-1b"
}
# and so on
}
}
resource "aws_subnet" "subnet" {
for_each = var.subnets
availability_zone = each.value.availability_zone
vpc_id = aws_vpc.vpc-homework2.id
cidr_block = each.value.cidr_block
map_public_ip_on_launch = each.value.map_public_ip_on_launch
}
resource "aws_lb" "nlb" {
name = "nlb-web"
internal = false
load_balancer_type = "network"
subnets = [for key, subnet in aws_subnet.subnet : subnet.id if length(regexall("public.*", key)) > 0]
}
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.
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)
I'm new on terraform and I'm trying to create an EKS cluster with terraform 0.14.2 . I'm trying to create it on a existing VPC passing the subnet_ids and VPC ID, but I don't know how to pass private and public subnets to the EKS cluster resource:
resource "aws_eks_cluster" "cluster" {
enabled_cluster_log_types = []
name = var.cluster_name
role_arn = aws_iam_role.cluster.arn
version = var.eks_version
vpc_config {
subnet_ids = var.priv_subnet_id
security_group_ids = []
endpoint_private_access = "true"
endpoint_public_access = "true"
}
}
And my vars are:
priv_subnet_id = {
pre = [ "subnet-XXX", "subnet-XXX", "subnet-XXX"]
}
Could you guide me the best way to do it?
Thanks
It's really strange that you will have a list of subnet ids as a variable...
Most of the time the subnets are resources we create, like this:
resource "aws_subnet" "example1" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
}
resource "aws_subnet" "example2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
}
resource "aws_eks_cluster" "cluster" {
enabled_cluster_log_types = []
name = var.cluster_name
role_arn = aws_iam_role.cluster.arn
version = var.eks_version
vpc_config {
subnet_ids = [aws_subnet.example1.id, aws_subnet.example2.id]
security_group_ids = []
endpoint_private_access = "true"
endpoint_public_access = "true"
}
}
If they are existing resources, then you want to get them using a data source:
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet_ids
data "aws_subnet_ids" "example" {
vpc_id = var.vpc_id
}
resource "aws_eks_cluster" "cluster" {
enabled_cluster_log_types = []
name = var.cluster_name
role_arn = aws_iam_role.cluster.arn
version = var.eks_version
vpc_config {
subnet_ids = data.aws_subnet_ids.example.ids
security_group_ids = []
endpoint_private_access = "true"
endpoint_public_access = "true"
}
}
In the very strange case that you have to absolutely have them as a variable...
it could be something like:
variable "subnet_ids" {
type = list(string)
default = ["subnet-X", "subnet-Y", "subnet-Z"]
}
resource "aws_eks_cluster" "cluster" {
enabled_cluster_log_types = []
name = var.cluster_name
role_arn = aws_iam_role.cluster.arn
version = var.eks_version
vpc_config {
subnet_ids = var.subnet_ids
security_group_ids = []
endpoint_private_access = "true"
endpoint_public_access = "true"
}
}
I have multiple subnets created with for each that I am trying to get associated with a route table. Below is the code I have, error, and what I have tried.
locals {
az_names = data.aws_availability_zones.azs.names
pub_sub_ids = aws_subnet.public.*.id
}
resource "aws_route_table_association" "main" {
for_each = var.public_sub_cidr
subnet_id = local.pub_sub_ids[each.key]
route_table_id = aws_route_table.main.id
}
resource "aws_subnet" "public" {
for_each = { for index, az_name in local.az_names : index => az_name }
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, each.key + 1)
availability_zone = local.az_names[each.key]
map_public_ip_on_launch = true
tags = {
Name = "${var.vpc_tags}-PubSubnet"
}
}
Error: Unsupported attribute
on vpc.tf line 3, in locals:
3: pub_sub_ids = aws_subnet.public.*.id
This object does not have an attribute named "id".
I believe this should be working. Any advice on this error and getting these public subnets to attach to the route table would be helpful.
UPDATE
I made some changes and removed the local variable 'pub_sub_ids' and also changed 'aws_route_table_association" "main" to
resource "aws_route_table_association" "main" {
for_each = var.public_sub_cidr
subnet_id = each.key
route_table_id = aws_route_table.main.id
}
Now I am getting an error
Error: Error creating route table association: InvalidSubnetID.NotFound: The `subnet ID '' does not exist`
It says the subnet does not exist even though I see it in the Console. Any advice would be appreciated in associating these public subnets to the route table.
subnet_id in aws_route_table_association should be subnet id, not subnet CIDR.
Since aws_route_table is not given, I made my own to verify the setup. Thus you could do the following:
resource "aws_route_table_association" "main" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.main.id
}
And below is the full code I used for verification:
provider "aws" {
# your data
}
data "aws_availability_zones" "azs" {
state = "available"
}
locals {
az_names = data.aws_availability_zones.azs.names
}
variable "vpc_cidr" {
default = "10.0.0.0/16"
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
}
resource "aws_subnet" "public" {
for_each = {for index, az_name in local.az_names: index => az_name}
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, each.key + 1)
availability_zone = local.az_names[each.key]
map_public_ip_on_launch = true
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "main"
}
}
resource "aws_route_table" "main" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
}
resource "aws_route_table_association" "main" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.main.id
}