How to use for_each loop terraform? - amazon-web-services

I'm trying to create aws vpc connection module. And can not figure out how to use for_each loop (like map, set of stings) for my var.env. For each var.env == "stage" use one peer-id, for each var.env != "stage" use another peer-id.
Here is my variables and main files:
variables.tf
variable "peer-id" {
type = string
}
variable "env" {
type = string
}
main.tf
resource "aws_vpc_peering_connection_accepter" "remote" {
vpc_peering_connection_id = var.peer-id
auto_accept = true
tags = {
Name = "stage"
}
lifecycle {ignore_changes = [tags]}
}
resource "aws_vpc_peering_connection_options" "accepter" {
for_each = toset(["stage"])
vpc_peering_connection_id = "var.peer-id"
accepter {
allow_remote_vpc_dns_resolution = true
}
requester {
allow_remote_vpc_dns_resolution = true
}
depends_on = [aws_vpc_peering_connection_accepter.remote]
}
resource "aws_vpc_peering_connection_options" "accepter-external" {
count = var.env != "dev" ? 1 : 0
vpc_peering_connection_id = var.peer-id
accepter {
allow_remote_vpc_dns_resolution = true
}
depends_on = [aws_vpc_peering_connection_accepter.remote]
}
data "aws_route_tables" "private" {
count = var.env != "dev" ? 1 : 0
vpc_id = aws_vpc_peering_connection_accepter.remote.vpc_id
filter {
name = "tag:Tier"
values = ["Private"]
}
}
data "aws_route_tables" "private_dev" {
count = var.env == "dev" ? 1 : 0
vpc_id = aws_vpc_peering_connection_accepter.remote.peer_vpc_id
filter {
name = "tag:Tier"
values = ["Private"]
}
}
resource "aws_route" "requster_route" {
for_each = toset(data.aws_route_tables.private.ids)
route_table_id = data.aws_route_tables.private.0.ids
destination_cidr_block = "192.168.0.0/16"
vpc_peering_connection_id = var.peer-id
depends_on = [data.aws_route_tables.private]
}
resource "aws_route" "requster_route_dev" {
for_each = toset(data.aws_route_tables.private.ids)
route_table_id = data.aws_route_tables.private_dev.0.ids
destination_cidr_block = "192.168.0.0/16"
vpc_peering_connection_id = var.peer-id
depends_on = [data.aws_route_tables.private_dev]
}
Also tried with set of strings, there are no errors but i don't think that i do it correctly.
Error: Missing resource instance key
│
│ on main.tf line 128, in resource "aws_route" "requster_route":
│ 128: for_each = toset(data.aws_route_tables.private.ids)
│
│ Because data.aws_route_tables.private has "count" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ data.aws_route_tables.private[count.index]

Related

Terraform append string to for_each each.key

I want to append some text to the Name tag for each resource created. I want the name to be the 'key' name + "a string"
resource "aws_vpc_peering_connection" "commerce_vpc_pc" {
for_each = local.requester_vpcs
peer_vpc_id = data.aws_vpc.tf_commerce_vpc.id
vpc_id = each.value.vpc_id
auto_accept = true
tags = {
Name = [each.key]+"_pc_commerce"
}
}
This give the error:
│ Error: Invalid operand
│
│ on main.tf line 34, in resource "aws_vpc_peering_connection" "commerce_vpc_pc":
│ 34: Name = [each.key]+"-to-commerce_vpc"
│
│ Unsuitable value for right operand: a number is required.
which makes sense. However is it possible to append some text to the each.key key?
resource "aws_vpc_peering_connection" "commerce_vpc_pc" {
for_each = local.requester_vpcs
peer_vpc_id = data.aws_vpc.tf_commerce_vpc.id
vpc_id = each.value.vpc_id
auto_accept = true
tags = {
Name = "${each.key}_pc_commerce"
}
}
See this another example of provisioning a customer gateway.
################################################################################
# Customer Gateways
################################################################################
resource "aws_customer_gateway" "this" {
for_each = var.customer_gateways
bgp_asn = each.value["bgp_asn"]
ip_address = each.value["ip_address"]
device_name = lookup(each.value, "device_name", null)
type = "ipsec.1"
tags = merge(
{ Name = "${var.name}-${each.key}" },
var.tags,
var.customer_gateway_tags,
)
}

"Error: Invalid count argument" error when trying to find the routing table' s id from the subnet ids to add new route entries

I am trying to update the routing tables of the subnets in VPC A and VPC B to include a route to a VPC peering end-point. This is my terraform code.
main.tf
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 3.14.2"
for_each = local.vpc_list
name = each.key
cidr =each.value.vpc_cidr
azs = each.value.vpc_azs
public_subnets = each.value.vpc_public_subnets
private_subnets = each.value.vpc_private_subnets
enable_nat_gateway = each.value.vpc_enable_nat_gateway
enable_vpn_gateway = each.value.vpc_enable_vpn_gateway
tags = each.value.vpc_tags
public_subnet_tags = each.value.vpc_public_subnet_tags
private_subnet_tags = each.value.vpc_private_subnet_tags
}
resource "aws_vpc_peering_connection" "vpc_peering_conn" {
peer_owner_id = data.aws_caller_identity.current.account_id
peer_vpc_id = module.vpc["vpcB"].vpc_id
vpc_id = module.vpc["vpcA"].vpc_id
auto_accept = true
tags = {
Name = "VPC Peering between ${module.vpc["vpcA"].name} and ${module.vpc["vpcB"].name}."
}
}
data "aws_route_tables" "vpcA_public_subnet_rts" {
depends_on = [ module.vpc ]
vpc_id = module.vpc["vpcA"].vpc_id
filter {
name = "tag:Subnet"
values = ["*public*"]
}
}
resource "aws_route" "route_vpcA" {
count = length(data.aws_route_tables.vpcA_public_subnet_rts.ids)
route_table_id = tolist(data.aws_route_tables.vpcA_public_subnet_rts.ids)[count.index]
destination_cidr_block = "10.10.11.0/24"
vpc_peering_connection_id = aws_vpc_peering_connection.vpc_peering_conn.id
}
data "aws_route_tables" "vpcB_private_subnet_rts" {
depends_on = [ module.vpc ]
vpc_id = module.vpc["vpcB"].vpc_id
filter {
name = "tag:Subnet"
values = ["*private*"]
}
}
resource "aws_route" "route_vpcB" {
count = length(data.aws_route_tables.vpcB_private_subnet_rts.ids)
route_table_id = tolist(data.aws_route_tables.vpcB_private_subnet_rts.ids)[count.index]
destination_cidr_block = "10.10.10.0/24"
vpc_peering_connection_id = aws_vpc_peering_connection.vpc_peering_conn.id
}
locals.tf
locals {
vpc_list = {
"vpcA" = {
vpc_cidr = "10.10.10.0/24",
vpc_azs = ["ap-southeast-1a"],
vpc_public_subnets = ["10.10.10.0/25"],
vpc_private_subnets = ["10.10.10.128/25"],
vpc_enable_nat_gateway = false,
vpc_enable_vpn_gateway = false,
vpc_tags = {
Name= "VPC A"
Terraform = "true"
Environment = "1st VPC"
Facing= "public and private"
},
vpc_public_subnet_tags = {
Subnet = "vpcA_public_subnet"
},
vpc_private_subnet_tags = {
Subnet = "vpcA_private_subnet"
},
},
"vpcB" = {
vpc_cidr = "10.10.11.0/24",
vpc_azs = ["ap-southeast-1b"],
vpc_public_subnets = [],
vpc_private_subnets = ["10.10.11.0/24"],
vpc_enable_nat_gateway = false,
vpc_enable_vpn_gateway = false,
vpc_tags = {
Name= "VPC B"
Terraform = "true"
Environment = "2nd VPC"
Facing= "private"
},
vpc_public_subnet_tags = {
Subnet = "vpcB_public_subnet"
},
vpc_private_subnet_tags = {
Subnet = "vpcB_private_subnet"
},
},
}
}
locals {
routing_table = {
route_peer_con_vpcA = {
vpc_id = module.vpc["vpcA"].vpc_id
route = {
route_peer_to_vpcB = {
cidr_block = "10.10.11.0/24"
}
}
}
route_peer_con_vpcB = {
vpc_id = module.vpc["vpcB"].vpc_id
route = {
route_peer_to_vpcA = {
cidr_block = "10.10.10.0/24"
}
}
}
}
}
When I run the terraform plan or apply I am getting the below error. Does anyone knows how to address the issue or Is there a better way to achieve what I want?
I saw this post "terraform: data.aws_subnet, value of 'count' cannot be computed" but am not sure how to refer to the output of the subnet' s id for the routing table id.
Thanks.
➜ 01-tf-deploy terraform apply --auto-approve
data.aws_region.current: Reading...
data.aws_caller_identity.current: Reading...
data.aws_region.current: Read complete after 0s [id=ap-southeast-1]
data.aws_ami.amzlinux2: Reading...
data.aws_ami.amzlinux2: Read complete after 1s [id=ami-0c802847a7dd848c0]
data.aws_caller_identity.current: Read complete after 1s [id=500295128231]
╷
│ Error: Invalid count argument
│
│ on main.tf line 70, in resource "aws_route" "route_vpcA":
│ 70: count = length(data.aws_route_tables.vpcA_public_subnet_rts.ids)
│
│ The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count
│ depends on.
╵
╷
│ Error: Invalid count argument
│
│ on main.tf line 88, in resource "aws_route" "route_vpcB":
│ 88: count = length(data.aws_route_tables.vpcB_private_subnet_rts.ids)
│
│ The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count
│ depends on.
╵

How to set nat_gateway_id in aws_route_table

main.tf
module "vpc" {
source = "../modules/aws/vpc"
env_prefix = "prod"
environment = "production"
env_name = "test"
vpc_cidr = "10.1.0.0/16"
public_subnet_cidrs = {
test-prod-nat = {
subnet = "10.1.15.0/24"
name = "test-prod-nat"
az = "ap-northeast-1a"
}
}
}
nat.tf
resource "aws_nat_gateway" "private" {
for_each = var.public_subnet_cidrs
allocation_id = aws_eip.nat_gateway.id
subnet_id = aws_subnet.public[each.key].id
tags = merge(
local.tags,
{
Name = format("%s_%s_%s", var.env_prefix, var.env_name, "nat-gateway")
}
)
lifecycle {
prevent_destroy = false
}
}
route_table.tf
/**
* for private subnet
*/
resource "aws_route_table" "private" {
vpc_id = aws_vpc.dandori.id
tags = merge(
local.tags,
{
Name = format("%s_%s", var.env_prefix, var.env_name)
}
)
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = [
for v in aws_nat_gateway.private : v.id
]
}
lifecycle {
prevent_destroy = false
}
}
When I run the terraform plan after creating the above tf file, I get the following Error
【Error】
╷
│ Error: Incorrect attribute value type
│
│ on ../modules/aws/vpc/route_table.tf line 55, in resource "aws_route_table" "private":
│ 55: nat_gateway_id = [
│ 56: for v in aws_nat_gateway.private : v.id
│ 57: ]
│ ├────────────────
│ │ aws_nat_gateway.private is object with 1 attribute "test-prod-nat"
│
│ Inappropriate value for attribute "nat_gateway_id": string required.
route_table.tf and nat.tf will be files in the module
I'm trying to set the nat_gateway_id in route_table.tf using the for loop method, but I can't set it correctly as shown in the Error message.
What should I do to solve this problem?
Please give me some advice.
If you want to create a route table for each aws_nat_gateway.private, then it should be:
resource "aws_route_table" "private" {
for_each = aws_nat_gateway.private
vpc_id = aws_vpc.dandori.id
tags = merge(
local.tags,
{
Name = format("%s_%s", var.env_prefix, var.env_name)
}
)
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = each.value["id"]
}
lifecycle {
prevent_destroy = false
}
}

Terraform AWS Can't import aws_vpc module

I'm trying to use a vpc module i made for aws in a top module.
My tree is as follows:
.
├── dev.vars.json
├── modules
│ └── vpc
│ ├── README.md
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
├── outputs.tf
├── variables.tf
└── main.tf
the "vpc" module works fine, I'm trying to use that module in my main.tf file on the root folder like this:
$ cat main.tf
module "dev_vpc" {
source = "./modules/vpc"
}
my variables:
variable "vpc" {
type = object({
name = string
})
}
my outputs.tf
# VPC
output "vpc_id" {
description = "The ID of the VPC"
value = module.vpc.vpc_id
}
...
and my dev.vars.json:
{
"vpc": {
"name": "development-vpc"
},
}
Once i got the vpc in "modules/vpc" working, I want to use it on the top main.tf file, but when i run apply (after init) i get:
$ terraform plan -var-file dev.vars.json
╷
│ Error: Missing required argument
│
│ on main.tf line 1, in module "dev_vpc":
│ 1: module "dev_vpc" {
│
│ The argument "vpc" is required, but no definition was found.
the main.tf in modules/vpc:
provider "aws" {
region = local.region
}
locals {
region = "us-east-1"
}
################################################################################
# VPC Module
################################################################################
resource "aws_vpc" "dev_vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "2.66.0"
name = var.vpc.name
cidr = "10.0.0.0/16"
azs = ["${local.region}a", "${local.region}b", "${local.region}c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_ipv6 = true
enable_nat_gateway = false
single_nat_gateway = true
public_subnet_tags = {
Name = "overridden-name-public"
}
tags = {
Owner = "user"
Environment = "dev"
}
vpc_tags = {
Name = "vpc-name"
}
}
I haven't been able to figure out how to fix this.
Many thanks!
davidcsi
It ended up being that i used a terraform from terraform's github, and there's a lot of dependencies that wouldn't work.
my final vpc code is:
$ cat main.tf
provider "aws" {
region = "${var.region}"
}
/*==== The VPC ======*/
resource "aws_vpc" "vpc" {
cidr_block = "${var.vpc_cidr}"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.environment}-vpc"
Environment = "${var.environment}"
}
}
$ cat subnets.tf
/* Internet gateway for the public subnet */
resource "aws_internet_gateway" "ig" {
vpc_id = "${aws_vpc.vpc.id}"
tags = {
Name = "${var.environment}-igw"
Environment = "${var.environment}"
}
}
/* Elastic IP for NAT */
resource "aws_eip" "nat_eip" {
vpc = true
depends_on = [aws_internet_gateway.ig]
}
/* NAT */
resource "aws_nat_gateway" "nat" {
allocation_id = "${aws_eip.nat_eip.id}"
subnet_id = "${element(aws_subnet.public_subnet.*.id, 0)}"
depends_on = [aws_internet_gateway.ig]
tags = {
Name = "nat"
Environment = "${var.environment}"
}
}
/* Public subnet */
resource "aws_subnet" "public_subnet" {
vpc_id = "${aws_vpc.vpc.id}"
count = "${length(var.public_subnets_cidr)}"
cidr_block = "${element(var.public_subnets_cidr, count.index)}"
availability_zone = "${element(var.availability_zones, count.index)}"
map_public_ip_on_launch = true
tags = {
Name = "${var.environment}-${element(var.availability_zones, count.index)}- public-subnet"
Environment = "${var.environment}"
}
}
/* Private subnet */
resource "aws_subnet" "private_subnet" {
vpc_id = "${aws_vpc.vpc.id}"
count = "${length(var.private_subnets_cidr)}"
cidr_block = "${element(var.private_subnets_cidr, count.index)}"
availability_zone = "${element(var.availability_zones, count.index)}"
map_public_ip_on_launch = false
tags = {
Name = "${var.environment}-${element(var.availability_zones, count.index)}-private-subnet"
Environment = "${var.environment}"
}
}
/* Routing table for private subnet */
resource "aws_route_table" "private" {
vpc_id = "${aws_vpc.vpc.id}"
tags = {
Name = "${var.environment}-private-route-table"
Environment = "${var.environment}"
}
}
/* Routing table for public subnet */
resource "aws_route_table" "public" {
vpc_id = "${aws_vpc.vpc.id}"
tags = {
Name = "${var.environment}-public-route-table"
Environment = "${var.environment}"
}
}
resource "aws_route" "public_internet_gateway" {
route_table_id = "${aws_route_table.public.id}"
destination_cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.ig.id}"
}
resource "aws_route" "private_nat_gateway" {
route_table_id = "${aws_route_table.private.id}"
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = "${aws_nat_gateway.nat.id}"
}
/* Route table associations */
resource "aws_route_table_association" "public" {
count = "${length(var.public_subnets_cidr)}"
subnet_id = "${element(aws_subnet.public_subnet.*.id, count.index)}"
route_table_id = "${aws_route_table.public.id}"
}
resource "aws_route_table_association" "private" {
count = "${length(var.private_subnets_cidr)}"
subnet_id = "${element(aws_subnet.private_subnet.*.id, count.index)}"
route_table_id = "${aws_route_table.private.id}"
$ cat security_groups.tf
/*==== VPC's Default Security Group ======*/
resource "aws_security_group" "default" {
name = "${var.environment}-default-sg"
description = "Default security group to allow inbound/outbound from the VPC"
vpc_id = "${aws_vpc.vpc.id}"
depends_on = [aws_vpc.vpc]
ingress {
from_port = "0"
to_port = "0"
protocol = "-1"
self = true
}
egress {
from_port = "0"
to_port = "0"
protocol = "-1"
self = "true"
}
tags = {
Environment = "${var.environment}"
}
}
$ cat outputs.tf
output "vpc_id" {
value = "${aws_vpc.vpc.id}"
}
cat variables.tf
variable "region" {
description = "AWS Deployment region.."
default = "us-east-1"
}
variable "vpc_cidr" {
description = "CIDR to assign to this VPC"
default = "10.0.0.0/16"
}
variable "environment" {
description = "On what environment is this running?"
default = "dev"
}
variable "availability_zones" {
description = "On what environment is this running?"
default = [
"us-east-1a",
"us-east-1b",
"us-east-1c"
]
}
variable "public_subnets_cidr" {
description = "public_subnets_cidr"
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
variable "private_subnets_cidr" {
description = "On what environment is this running?"
default = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
}
This doesn't give me any issues when using it as a module.

Terraform : Getting AWS subnetID's for a given vpc failing, no results

I am working on creating an ElasticSearch cluster using Terraform. I am not able to get the subnet-ids for a given VPC, with aws_subnet_ids. The return is always null. What am I doing wrong?
Code :
provider "aws" {
region = "eu-central-1"
shared_credentials_file = "${pathexpand("~/.aws/credentials")}"
}
variable "domain" {
default = "tf-test"
}
data "aws_vpc" "selected" {
tags = {
Name = var.vpc
}
}
data "aws_subnet_ids" "selected" {
vpc_id = "${data.aws_vpc.selected.id}"
}
resource "aws_elasticsearch_domain" "es" {
domain_name = "${var.domain}"
elasticsearch_version = "6.3"
cluster_config {
instance_type = "m4.large.elasticsearch"
}
vpc_options {
subnet_ids = [
"${data.aws_subnet_ids.selected.ids[0]}",
"${data.aws_subnet_ids.selected.ids[1]}",
]
}
Output :
terraform plan :
on main.tf line 55, in resource "aws_elasticsearch_domain" "es":
55: "${data.aws_subnet_ids.selected.ids[0]}",
This value does not have any indices.
Error: Invalid index
on main.tf line 56, in resource "aws_elasticsearch_domain" "es":
56: "${data.aws_subnet_ids.selected.ids[1]}",
This value does not have any indices.
Update
If I print the subnet id's without index, I am getting them :
Solved :
subnet_id = "${element(module.vpc.public_subnets, 0)}"
VPC created in this manner :
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "2.6.0"
name = var.vpc_name
cidr = var.vpc_cidr
azs = data.aws_availability_zones.available.names
private_subnets = [var.public_subnet_1, var.public_subnet_2, var.public_subnet_3]
public_subnets = [var.private_subnet_1, var.private_subnet_2, var.private_subnet_3]
enable_nat_gateway = var.enable_nat_gateway
single_nat_gateway = var.single_nat_gateway
enable_dns_hostnames = var.enable_dns_hostname
public_subnet_tags = {
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"
"name" = var.public_subnet_name
}
private_subnet_tags = {
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/internal-elb" = "1"
"name" = var.private_subnet_name
}
}