Terraform: creation of resources with nested loop - terraform

I have created a bunch of VPC endpoints in one AWS account and need to share them with other VPCs across different accounts. Break down of my terraform code is provided below.
Created the Route53 private hosted zones (Resource#1 for easy reference) as below
resource "aws_route53_zone" "private" {
for_each = local.vpc_endpoints
name = each.value.phz
vpc {
vpc_id = var.vpc_id
}
lifecycle {
ignore_changes = [vpc]
}
tags = {
Name = each.key
}
}
Created vpc association authorizations (Resource#2). The VPC ID, to which the endpoints are to be shared is passed as a variable as shown below.
resource "aws_route53_vpc_association_authorization" "example" {
for_each = local.vpc_endpoints
vpc_id = var.vpc
zone_id = aws_route53_zone.private[each.key].zone_id
}
Finally, I have created the VPC associations (Resource#3)
resource "aws_route53_zone_association" "target" {
for_each = aws_route53_vpc_association_authorization.example
provider = aws.target-account
vpc_id = aws_route53_vpc_association_authorization.example[each.key].vpc_id
zone_id = aws_route53_vpc_association_authorization.example[each.key].zone_id
}
Everything works fine for the first VPC (vpc-A). But now I need to share the same hosted zones (Resource#1) with a different VPC (vpc-B) and more VPCs in the future. This means I need to repeat the creation of "aws_route53_vpc_association_authorization" (Resource#2) for all new VPCs as well, ideally looping through a list of VPC IDs.
But I am unable to it as nested for_each loop is not supported. Tried other options like count + for_each etc., but nothing help.
Could you provide some guidance on how to achieve this?

Related

How do I get vpc_id so I can create a VPC subnet in Terraform?

I am creating an AWS VPC with a single public subnet in a brand-new Terraform project, consisting only of a main.tf file. In that file I am using two resource blocks, aws_vpc and aws_subnet. The second resource must be attached to the first using the vpc_id attribute. The value of this attribute is created only upon apply, so it cannot be hard-coded. How do I get the ID of the resource I just created, so I can use it in the subsequent block?
resource "aws_vpc" "my_vpc" {
cidr_block = "102.0.0.0/16"
tags = {
Name = "My-VPC"
}
}
resource "aws_subnet" "my_subnet" {
vpc_id = # what goes here?
cidr_block = "102.0.0.0/24"
tags = {
Name = "My-Subnet"
}
}
The docs give the example of data.aws_subnet.selected.vpc_id for vpc_id. The value of this appears to depend on two other blocks, variable and data. I am having a hard time seeing how to wire these up to my VPC. I tried copying them directly from the docs. Upon running terraform plan I get the prompt:
var.subnet_id
Enter a value:
This is no good; I want to pull the value from the VPC I just created, not enter it at the command prompt. Where do I specify that the data source is the resource that I just created in the previous code block?
I have heard that people create a separate file to hold Terraform variables. Is that what I should to do here? It seems like it should be so basic to get an ID from one resource and use it in the next. Is there a one-liner to pass along this information?
You can just call the VPC in the subnet block by referencing Terraform's pointer. Also, doing this tells Terraform that the VPC needs to be created first and destroyed second.
resource "aws_vpc" "my_vpc" {
cidr_block = "102.0.0.0/16"
tags = {
Name = "My-VPC"
}
}
resource "aws_subnet" "my_subnet" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "102.0.0.0/24"
tags = {
Name = "My-Subnet"
}
}
I want to pull the value from the VPC I just created,
You can't do this. You can't dynamically populate variables from data sources. But you could use local instead:
locals {
subnet_id = data.aws_subnet.selected.id
}
and refer to it as local.subnet_id.

How to create subnets inside virtual network and security rules inside nsg using loop concept in terraform

I’m trying to create network security group with multiple security rules in one script and virtual network along with five subnets in one script.
For that, I have referred azurerm_virtual_network and azurerm_subnet_network_security_group_association documentations.
The above documentation contains the code with hardcode values. But I want to use loop concept to create subnets inside virtual network, security rules inside network security group and then associate each subnet with network security group.
Thanks in advance for the help !
In order to "loop" you can use the for_each = var.value method and instead of placing the values within the Main.tf file, you can instead use a .tfvars file to loop through the # of resources.
As this is quite advanced, you would be better off dissecting/reusing something that's already available. Take a look at the Azurerm subnet modules from Claranet, available at the modules page on the Terraform website (and there are a ton more to explore!). Here's how you would define the nsgs, vnet and subnets in the locals, at a glance:
locals {
network_security_group_names = ["nsg1", "nsg2", "nsg3"]
vnet_cidr = "10.0.1.0/24"
subnets = [
{
name = "subnet1"
cidr = ["10.0.1.0/26"]
service_endpoints = ["Microsoft.Storage", "Microsoft.KeyVault", "Microsoft.ServiceBus", "Microsoft.Web"]
nsg_name = local.network_security_group_names[0]
vnet_name = module.azure-network-vnet.virtual_network_name
},
{
name = "subnet2"
cidr = ["10.0.1.64/26"]
service_endpoints = ["Microsoft.Storage", "Microsoft.KeyVault", "Microsoft.ServiceBus", "Microsoft.Web"]
nsg_name = local.network_security_group_names[2]
vnet_name = module.azure-network-vnet.virtual_network_name
}
]
}

Terraform: Reference multiple resources without naming each of them

I am creating multiple subnets via a variable:
variable "private_subnets" {
description = "Private Subnets"
default = ["10.0.0.0/20", "10.0.32.0/20"]
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = element(var.private_subnets, count.index)
availability_zone = element(var.availability_zones, count.index)
count = length(var.private_subnets)
}
I count how many subnets I have listed in my Var and then create a subnet for each. The problem is so far I can only figure out how to reference them by each individual index:
subnets = [ aws_subnet.private[0].id, aws_subnet.private[1].id ]
What is the correct way to do this? I tried a similar element() and count section to the ECS network config where I'm referencing this, but it isn't working.
I am not sure if this is what you are after, but you can try:
subnets = aws_subnet.private[*].id
This syntax is called splat expressions

terraform - passing vpc_id parameter from different VPC's to several subnets

I'm new to terraform and trying the following:
I created 3 aws VPC's:
#VPC creation
enter codresource "aws_vpc" "new_vpc" {
cidr_block = var.new_vpc[count.index]
count = 3
tags = {
Name = var.vpc_name[count.index]
count = 3
}
}
The variables.tf is as following:
#varibales for the vpc
#=======================
variable "new_vpc" {
type = list
}
variable "vpc_name" {
type = list
}
the terraform.tfvars is :
new_vpc=["10.0.0.0/16" , "10.0.0.0/17" , "10.0.0.0/18"]
vpc_name=["DEV_VPC" , "UAT_VPC" , "PROD_VPC" ]
all the VPC's created successfully
the next step is to create 3 subnets and each subnet need to be assign to a different VPC.
in oder to create a subnet the following paramaters are required : vpc_id + cidr_block
need your advice for the required vpc_id parameter - how to pass each subnet different vpc_id (as i mentioned above 3 vpc's created)
10x a lot
Since you used count to create vpc you can also use count to create subnets.
resource "aws_subnet" "You_subnet_name" {
count = 3
vpc_id = aws_vpc.new_vpc[count.index].id
}
You don't need to specify the cidr, the vpc id is enough.
You need however to specify the cidr_block part to alocate ip range for your subnet.
If you want to use all the ip space in the vpc for 1 subnet you can simply do:
resource "aws_subnet" "You_subnet_name" {
count = 3
vpc_id = aws_vpc.new_vpc[count.index].id
cidr_block = aws_vpc.new_vpc[count.index].cidr_block
}

Terraform EKS tagging

I am having this issue of Terraform EKS tagging and don't seem to find workable solution to tag all the VPC subnets when a new cluster is created.
To provide some context: We have one AWS VPC where we deployment several EKS cluster into the subnets. We do not create VPC or subnets are part of the EKS cluster creation. Therefore, the terraform code creating a cluster doesn't get to tag existing subnets and VPC. Although EKS will add the required tags, they are automatically removed next time we run terraform apply on the VPC.
My attempt to workaround is to provide a terraform.tfvars file within the VPC to as follows:
eks_tags =
[
"kubernetes.io/cluster/${var.cluster-1}", "shared",
"kubernetes.io/cluster/${var.cluster-2}", "shared",
"kubernetes.io/cluster/${var.cluster-2}", "shared",
]
Then within the VPC and subnets resources, we do something like
resource "aws_vpc" "demo" {
cidr_block = "10.0.0.0/16"
tags = "${
map(
${var.eks_tags}
)
}"
}
However, the above does not seem to work. I have tried various Terraform 0.11 functions from https://www.terraform.io/docs/configuration-0-11/interpolation.html but not of them help.
Has anyone ben able to resolve this issue?
The idea that we always create new VPC and subnet for every EKS cluster is wrong. Obviously, the has to be a way to tag existing VPC and subnet resources using Terraform?
You can now use the aws provider ignore_tags attribute so that the tags made with the aws_ec2_tag resource do not get removed next time the VPC module is applied.
For example the provider becomes:
provider "aws" {
profile = "terraform"
region = "us-west-1"
// This is necessary so that tags required for eks can be applied to the vpc without changes to the vpc wiping them out.
// https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging
ignore_tags {
key_prefixes = ["kubernetes.io/"]
}
}
You can then leverage the aws_ec2_tag resource like so in your EKS module without worrying about the tag getting removed next time the VPC module is applied.
/**
Start of resource tagging logic to update the provided vpc and its subnets with the necessary tags for eks to work
The toset() function is actually multiplexing the resource block, one for every item in the set. It is what allows
for setting a tag on each of the subnets in the vpc.
*/
resource "aws_ec2_tag" "vpc_tag" {
resource_id = data.terraform_remote_state.vpc.outputs.vpc_id
key = "kubernetes.io/cluster/${var.cluster_name}"
value = "shared"
}
resource "aws_ec2_tag" "private_subnet_cluster_tag" {
for_each = toset(data.terraform_remote_state.vpc.outputs.private_subnets)
resource_id = each.value
key = "kubernetes.io/cluster/${var.cluster_name}"
value = "shared"
}
resource "aws_ec2_tag" "public_subnet_cluster_tag" {
for_each = toset(data.terraform_remote_state.vpc.outputs.public_subnets)
resource_id = each.value
key = "kubernetes.io/cluster/${var.cluster_name}"
value = "shared"
}
/**
These tags have been decoupled from the eks module and moved to the more appropirate vpc module.
*/
resource "aws_ec2_tag" "private_subnet_tag" {
for_each = toset(data.terraform_remote_state.vpc.outputs.private_subnets)
resource_id = each.value
key = "kubernetes.io/role/internal-elb"
value = "1"
}
resource "aws_ec2_tag" "public_subnet_tag" {
for_each = toset(data.terraform_remote_state.vpc.outputs.public_subnets)
resource_id = each.value
key = "kubernetes.io/role/elb"
value = "1"
}
In our case we have separate scripts to provision VPC and networking resources there we are not adding EKS specific tags.
For EKS cluster provisioning we have separate scripts which will auto update/add tags on cluster.
So on VPC scripts in provider.tf file we add below condition so that scripts will not remove these tags and everything works properly.
provider "aws" {
region = "us-east-1"
ignore_tags {
key_prefixes = ["kubernetes.io/cluster/"]
}
}
This problem will always exist when there are 2 pieces of code with different state files trying to act on the same resource.
One way to solve this is to re-import the VPC resource into your VPC state file everytime you apply your EKS terraform code. This will import your tags as well. Same goes with subnets, but it is a manual and tedious process in the long run.
terraform import aws_vpc.test_vpc vpc-a01106c2
Ref: https://www.terraform.io/docs/providers/aws/r/vpc.html
Cheers!

Resources