Unable to reference vpc_id for a subnet within modules - terraform

Have a modules/network/testvpc and modules/network/subnet module configurations .
mainfolder/modules/network/testvpc/main.tf
variable "vpccidr" {type="list"}
variable "vpcname" {type="list"}
resource "aws_vpc" "customVpc" {
count = "${length(var.vpccidr)}"
cidr_block = "${element(var.vpccidr,count.index)}"
tags {
Name = "${element(var.vpcname,count.index)}"
}
mainfolder/modules/network/subnet/main.tf
variable "subcidr" {type="list"}
variable "subname" {type="list"}
resource "aws_subnet" "subnet" {
count = "${length(var.subcidr)}"
vpc_id = "${element(aws_vpc.customVpc.*.id, count.index)}"
cidr_block = "${element(var.subcidr, count.index)}"
tags {
Name = "${element(var.subname, count.index)}"
}
}
mainfolder/main.tf
module "testvpc" {
source = "./modules/network/testvpc"
vpccidr="${var.vpccidr}"
vpcname="${var.vpcname}"
}
module "subnet" {
source = "./modules/network/subnet"
subcidr = "${var.subcidr}"
subname = "${var.subname}"
}
mainfolder/var.tf
variable "vpccidr" {type="list"}
variable "vpcname" {type="list"}
variable "subcidr" {type="list"}
variable "subname" {type="list"}
mainfolder/terraform.tfvars
- vpccidr=["10.1.0.0/16","10.2.0.0/16","10.3.0.0/16"]
vpcname=["vpc-shared","vpc-sand","vpc-preprod"]
subcidr=["10.1.1.0/24","10.2.1.0/24","10.3.1.0/24"]
subname=["sub-shared","sub-sand","sub-preprod"]
-
While running terraform validate -var-file=terraform.tfvars getting the following error
Error: resource 'aws_subnet.subnet' config: unknown
resource 'data.aws_vpc.customVpc' referenced in variable
data.aws_vpc.customVpc.*.id
Is it because aws_subnet is not able to locate vpc_id since the resource aws_vpc is not created it . I am calling both the testvpc and subnet as modules in the mainfolder/main.tf . What am i missing .
Secondly is the loop in the aws_vpc and aws_subnet proper . It should create vpc-shared 10.1.0.0/16 and sub-shared within that vpc and so on

You need to use module outputs because you are trying to reference resources in a separate module. That won't work because
Modules encapsulate their resources. A resource in one module cannot directly depend on resources or attributes in other modules, unless those are exported through outputs.
So in mainfolder/modules/network/testvpc/main.tf, add an output like so
output "vpc_ids" { value=["${aws_vpc.customVpc.*.id}"] }
Then add a variable in mainfolder/modules/network/subnet/main.tf like so
variable "vpc_ids" {type="list"}
and use it within that module (instead of trying to directly reference resources from the /testvpc/main.tf module)
resource "aws_subnet" "subnet" {
count = "${length(var.subcidr)}"
vpc_id = "${element(var.vpc_ids, count.index)}"
etc, etc
}
and finally now from your mainfolder/main.tf
module "testvpc" {
source = "./modules/network/testvpc"
vpccidr="${var.vpccidr}"
vpcname="${var.vpcname}"
}
module "subnet" {
source = "./modules/network/subnet"
subcidr = "${var.subcidr}"
subname = "${var.subname}"
vpc_ids = "${module.testvpc.vpc_ids}"
}

Related

Creating subnets and assinging rout tables to them

I am new to terraform and I'm trying to create a VPC with multiple subnets and adding route tables to all those subnets in a for loop manner.
VPC: 10.207.0.0/16
There's number_of_subnets which will create subnets like this: 10.207.x.0/24
This code works fine:
variable "region" {
default = "us-east-1"
}
variable "availability_zone" {
default = "us-east-1a"
}
variable "cidr_block" {
default = "207"
}
variable "number_of_subnets" {
default = 5
}
provider "aws" {
region = var.region
}
resource "aws_vpc" "test_vpc" {
cidr_block = "10.${var.cidr_block}.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "test_vpc_${var.cidr_block}"
}
}
resource "aws_subnet" "test_subnets" {
count = var.number_of_subnets
vpc_id = aws_vpc.test_vpc.id
cidr_block = "10.${var.cidr_block}.${count.index+1}.0/24" # start from x.x.1.0/24
availability_zone = var.availability_zone
map_public_ip_on_launch = false
tags = {
Name = "test_subnet_${var.cidr_block}_${count.index+1}"
}
}
Now if I try to add this code to the bottom of the same file (everything is in one file called main.tf) to get the subnets and add route table to each:
# get all subnet IDs
data "aws_subnets" "q_subnets" {
filter {
name = "vpc-id"
values = [aws_vpc.test_vpc.id]
}
}
# add route table to all subnets
resource "aws_route_table_association" "rt_assoc_subnet" {
depends_on = [aws_subnet.test_subnets]
for_each = toset(data.aws_subnets.q_subnets.ids)
subnet_id = each.value
route_table_id = aws_route_table.test_rt.id
}
and run terraform apply it will give this error:
invalid for_each argument...
The "for_each" value depends on resource attribute that cannot be deteremined until apply,...
which doesn't make scense. First create vpc, then subnet, then get all subnets...
I also tried depends_on and didn't help.
How would I write this to make it work?
Any help is appreciated.
UPDATE1:
I tried to use aws_subnet.test_subnets.*.id instead of data and it still gives depencendy error:
variable "region" {
default = "us-east-1"
}
variable "availability_zone" {
default = "us-east-1a"
}
variable "cidr_block" {
default = "207"
}
variable "number_of_subnets" {
default = 5
}
provider "aws" {
region = var.region
}
resource "aws_vpc" "test_vpc" {
cidr_block = "10.${var.cidr_block}.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "test_vpc_${var.cidr_block}"
}
}
resource "aws_route_table" "test_rt" {
vpc_id = aws_vpc.test_vpc.id
route = []
tags = {
Name = "test_rt_${var.cidr_block}"
}
}
resource "aws_subnet" "test_subnets" {
count = var.number_of_subnets
vpc_id = aws_vpc.test_vpc.id
cidr_block = "10.${var.cidr_block}.${count.index+1}.0/24" # start from x.x.1.0/24
availability_zone = var.availability_zone
map_public_ip_on_launch = false
tags = {
Name = "test_subnet_${var.cidr_block}_${count.index+1}"
}
}
output "subnets" {
value = aws_subnet.test_subnets.*.id
}
# add route table to all subnets
resource "aws_route_table_association" "rt_assoc_subnet" {
depends_on = [aws_subnet.test_subnets]
for_each = toset(aws_subnet.test_subnets.*.id)
subnet_id = each.value
route_table_id = aws_route_table.test_rt.id
}
is there another way to pass the subnets to aws_route_table_association without getting dependency error?
Since you are using count, it is very hard to make count work with for_each. It would be better to continue using count for route table association as well. If you decide to go down that route, the only change you need is:
resource "aws_route_table_association" "rt_assoc_subnet" {
count = var.number_of_subnets
subnet_id = aws_subnet.test_subnets.*.id[count.index]
route_table_id = aws_route_table.test_rt.id
}
This will work as intended. However, if you must use for_each I would suggest defining a variable that could be used with it in all the resources you are now using count. If you really want to use for_each with the current code, then you can use the -target option [1]:
terraform apply -target=aws_vpc.test_vpc -target=aws_route_table.test_rt -target=aws_subnet.test_subnets
When running this command, this will be shown in the command output:
│ Warning: Resource targeting is in effect
│
│ You are creating a plan with the -target option, which means that the result of this plan may not represent all of the changes requested by the current
│ configuration.
│
│ The -target option is not for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform
│ specifically suggests to use it as part of an error message.
After the targeted resources are created, you could re-run terraform apply and it should create the route table associations.
[1] https://www.terraform.io/cli/commands/plan#target-address

Unable to Create Terraform Resource Group when using modules

I am optimizing my terraform code by using modules. When i create a resource group module it works perfectly well but it creates two resource groups
i.e.
Temp-AppConfiguration-ResGrp
Temp-AppServices-ResGrp
instead it should only create
Temp-AppConfiguration-ResGrp
Code Resourcegroup.tf.
resource "azurerm_resource_group" "resource" {
name = "${var.environment}-${var.name_apptype}-ResGrp"
location = var.location
tags = {
environment = var.environment
}
}
output "resource_group_name" {
value = "${var.environment}-${var.name_apptype}-ResGrp"
}
output "resource_group_location" {
value = var.location
}
Variable.tf
variable "name_apptype" {
type = string
default = "AppServices"
}
variable "environment" {
type = string
default = "Temp"
}
variable "location" {
type = string
default = "eastus"
}
Main.tf
module "resourcegroup" {
source = "../Modules"
name_apptype = "AppConfiguration"
}
I want to pass name_apptype in main.tf when calling resource group module. So that i don't need to update variable.tf every time.
Any suggestions
where i am doing wrong. Plus i am also unable to output the value, i need it so that i could pass resource group name in the next module i want to create.
Thanks
You need to do that in the Main.tf
module "resourcegroup" {
source = "../Modules"
name_apptype = "AppConfiguration"
}
module "resourcegroup-appservices" {
source = "../Modules"
name_apptype = "AppServices"
}
These create a 2 resources groups with the values that you need, additionally you can remove the default value from the name_apptype variable.
If you want to create with the same module both resource groups you need to use count to iterate over an array of names

Referencing outputs from a for_each module

I have a module which has a variable defined using for_each, and its output is as below:
output "nic_ids" {
value = [for x in azurerm_network_interface.nic : x.id]
}
nic_ids = [
"/subscriptions/*****/resourceGroups/test-rg/providers/Microsoft.Network/networkInterfaces/test-nic-1",
"/subscriptions/*****/resourceGroups/test-rg/providers/Microsoft.Network/networkInterfaces/test-nic-2",
]
My aim is to pass above NIC ids to the VM module and have 1:1 mapping between NIC id and VM (test-nic-1 should only be attached to vm-1, test-nic-2 to vm-2 etc.)
module "vm" {
source = "*****/vm/azurerm"
version = "0.1.0"
vms = var.vms
nic_ids = module.nic[each.value.id].nic_ids
}
I am getting below error:
Error: each.value cannot be used in this context
on main.tf line 58, in module "vm":
58: nic_ids = module.nic[each.value.id].nic_ids
A reference to "each.value" has been used in a context in which it
unavailable, such as when the configuration no longer contains the value in
its "for_each" expression. Remove this reference to each.value in your
configuration to work around this error.
I used this similar question as reference.
Can you please suggest?
You could pass the above NIC id list to the VM modules with the count.
module "vm" {
source = "./modules/vms"
vm_names = ["aaa","bbb"]
nic_ids = module.nic.network_interface_nics
}
module "nic" {
source = "./modules/nic"
nic_names = ["nic1","nic2"]
}
the main.tf in the VM module:
resource "azurerm_virtual_machine" "vm-windows" {
count = length(var.vm_names)
name = var.vm_names[count.index]
resource_group_name = data.azurerm_resource_group.vm.name
location = var.location
vm_size = var.vm_size
network_interface_ids = [ var.nic_ids[count.index] ]
...
}
The output.tf in the NIC module:
output "network_interface_nics" {
description = "ids of the vm nics provisoned."
value = [for x in azurerm_network_interface.nic : x.id]
}

Pass terraform output from one file to another

I have following structure:
modules
|_ test1
| |_vpc.tf
|_test2
|_subnet.tf
I have created a vpc in test1/vpc.tf
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
I am getting vpc id in output like:
output "vpc_id" {
value = aws_vpc.main.id
}
How can I pass this id to test2/subnet.tf file? I am searching online and can't seem to find an answer for this.
Create a variable in subnet.tf:
variable "vpc_id" {
type = string
}
Then in your main terraform file where you are utilizing both of these modules, you would take the output from the vpc module and pass it to the input of the subnet module:
module "vpc" {
source = "modules/test1"
}
module "subnet" {
source = "modules/test2"
vpc_id = module.vpc.vpc_id
}

Terraform - A managed resource has not been declared in the root module

i'm trying create ec2 instance and setup load balancer using terraform but i'm facing follwing error. How to create instance and configure load balacer in a single main.tf file?
Error: Reference to undeclared resource
"aws_lb_target_group" "front-end":27: vpc_id = "${aws_vpc.terrafom-elb.id}"
A managed resource "aws_vpc" "terrafom-elb" has not been declared in the root
module.source`
code:
region = "us-east-1"
access_key = "*********************"
secret_key = "**********************"
}
resource "aws_instance" "terraform" {
ami = "ami-07ebfd5b3428b6f4d"
instance_type = "t2.micro"
security_groups = ["nodejs","default"]
tags = {
Name = "terrafom-elb"
}
}
resource "aws_lb" "front-end"{
name = "front-end-lb"
internal = false
security_groups = ["nodejs"]
}
resource "aws_lb_target_group" "front-end" {
name = "front-end"
port = 8989
protocol = "HTTP"
vpc_id = "${aws_vpc.terrafom-elb.id}"
depends_on = [aws_instance.terraform]
}
There's a typo where you're assigning the vpc_id:
vpc_id = "${aws_vpc.terrafom-elb.id}"
should be:
vpc_id = "${aws_vpc.terraform-elb.id}"
note the missing 'r' in the word 'terraform'
You can add a data structure to the top and pass VPC ID as variable:
data "aws_vpc" "selected" {
id = var.vpc_id
}
And reference it as vpc_id = data.aws_vpc.selected.id

Resources