I have deployed two subnets by using count.index then I need to referece the id of subnet in subnet_route_table_association module. Can any one advise me what is the correct way to do that?
here is my code
/application/demo/main.tf
module “subnet_association” {
source = “…/…/Modules/subnet_association”
subid = var.subid
subnet_id = module.subnet.subnet_id
route_table_id = module.route_table.route_table_id
}
Modules/subnet/main.tf
resource "azurerm_subnet" "module-spoke-subnet" {
count = var.subnet_count
name = element(var.subnet_name, count.index)
resource_group_name = var.resource_group_name
virtual_network_name = var.virtual_network_name
address_prefixes = [var.subnet_address[count.index]]
enforce_private_link_endpoint_network_policies = true
enforce_private_link_service_network_policies = true
}
variable "resource_group_name" {
}
variable "virtual_network_name" {
}
Modules/subnet/output.tf
output "subnet_id" {
value = azurerm_subnet.module-spoke-subnet.*.id
}
Modules/subnet_association/main.tf
resource "azurerm_subnet_route_table_association" "module-subnet-association" {
subnet_id = var.subnet_id
route_table_id = var.route_table_id
}
variable "subnet_id" {
}
variable "route_table_id" {
}
and I`m getting the error
Error: Incorrect attribute value type
│
│ on ..\..\modules\Subnet_association\main.tf line 20, in resource "azurerm_subnet_route_table_association" "module-subnet-association":
│ 20: subnet_id = var.subnet_id
│ ├────────────────
│ │ var.subnet_id is tuple with 1 element
│
│ Inappropriate value for attribute "subnet_id": string required.
Output subnet_id is a list of elements. Hence, if you want to create association for each subnet try below:
resource "azurerm_subnet_route_table_association" "module-subnet-association" {
count = length(module.subnet[*].subnet_id)
subnet_id = module.subnet[count.index]. subnet_id
route_table_id = var.route_table_id
}
Or if you are creating only one subnet try below:
resource "azurerm_subnet_route_table_association" "module-subnet-association" {
subnet_id = module.subnet[0]. subnet_id
route_table_id = var.route_table_id
}
Related
I'm trying to convert those variables in main.tf to terraform.tfvars and variables.tf ( declarer the variables in terraform.tfvars and variables.tf ).
i want to create map ( map is required because I'm using for each ) using couple of network interfaces name and virtual machine names and loop on them when creating new vms .
main.tf
here i create the variables in the main.tf but as i wrote i want to declarer them in the variables.tf file
variable "nics" {
type = map(any)
default = {
nic3 = {
name = "ubuntutest3"
}
nic4 = {
name = "ubuntutest4"
}
}
}
variable "vms" {
description = "Virtual Machines"
type = map(any)
default = {
vm3 = {
name = "ubuntutest3"
size = "Standard_DS1_v2"
nic = "nic3"
}
vm4 = {
name = "ubuntutest4"
size = "Standard_DS1_v2"
nic = "nic4"
}
}
}
// VNICs
resource "azurerm_network_interface" "nics" {
for_each = var.nics
name = each.value.name
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "${each.value.name}-conf"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
}
}
and when I'm creating vms there is line for
network_interface_ids = [azurerm_network_interface.nics[each.value.nic].id, ]
when i tried to declarer those variables in variable.tf file and terraform.tfvars i got error massage
│ Error: Invalid index
│
│ on main.tf line 259, in resource "azurerm_virtual_machine" "vms":
│ 259: network_interface_ids = [azurerm_network_interface.nics[each.value.nic].id, ]
│ ├────────────────
│ │ azurerm_network_interface.nics is object with 2 attributes
│ │ each.value.nic is "network_interface_2"
│
│ The given key does not identify an element in this collection value.
how should i declarer on vm names and nic names in variables.tf and create the names i want to use in terraform.tfvars and use them I'm main.tf ?
EDITED
I tried like this
terraform.tfvars
nics = {
nic_names = ["nic2,nic3"]
}
variables.tf
variable "nics" {
type = object({
nic_names = list(string)
})
}
got error
│ Error: Unsupported attribute
│
│ on main.tf line 272, in resource "azurerm_network_interface" "nics":
│ 272: name = each.value.nic_names
│ ├────────────────
│ │ each.value is list of string with 1 element
│
│ This value does not have any attributes.
Your error has nothing to do with tfvars used instead of variables. The error states explicitly that when you create a VM, each.value.nic at this particular iteration is network_interface_2, while in nics map you only have keys nic3 and nic4.
As for the second part, you have probably wanted to type:
nic_names = ["nic2","nic3"]
instead of:
nic_names = ["nic2,nic3"]
The second is only one string in a list, the first is two element-list.
EDIT
To show directly how #ITBYD should set his files into working solution:
terraform.tfvars:
nics = {
nic_names = ["nic2","nic3"]
}
main.tfvars:
variable "nics" {
type = object({
nic_names = list(string)
})
}
# [...]
resource "azurerm_network_interface" "nics" {
for_each = toset(var.nics["nic_names"])
name = each.value
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "${each.value}-conf"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
}
}
But I would not use list, only map instead just like I wrote earlier.
I'm trying to create multiplane vms using for each function in terraform.
Resource Group
resource "azurerm_resource_group" "rg" {
name = "${var.prefix}-rg"
location = "east us 2"
tags = var.tags
}
VNET
resource "azurerm_virtual_network" "vnet" {
name = "${var.prefix}-network-1"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
tags = var.tags
}
Subnet
resource "azurerm_subnet" "subnet" {
name = "${var.prefix}-network-subnet-1"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.2.0/24"]
}
Variables for NICS
variable "nics" {
type = map
default = {
nic3 = {
name = "ubuntu-test-3"
}
nic4 = {
name = "ubuntu-test-4"
}
}
}
NICS
resource "azurerm_network_interface" "nics" {
for_each = var.nics
name = each.value.name
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "${each.value.name}-conf-1"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
}
tags = var.tags
}
Variables for VMS
variable "vms" {
description = "Virtual Machines"
type = map
default = {
vm3 = {
name = "ubuntu-test-3"
size = "Standard_DS1_v2"
}
vm4 = {
name = "ubuntu-test-4"
size = "Standard_DS1_v2"
}
}
}
and the block for the VM ( not completed - i wrote only the section that i have issue with )
resource "azurerm_virtual_machine" "vms" {
for_each = var.vms
name = each.value.name
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
vm_size = each.value.size
tags = var.tags
network_interface_ids = [
azurerm_network_interface.nics[each.value].id,
]
The issue is with this section
network_interface_ids = [
azurerm_network_interface.nics[each.value].id,
]
I'm getting ERROR
│ Error: Invalid index
│
│ on main.tf line 247, in resource "azurerm_virtual_machine" "vms":
│ 247: azurerm_network_interface.nics[each.value].id,
│ ├────────────────
│ │ azurerm_network_interface.nics is object with 2 attributes
│ │ each.value is object with 2 attributes
│
│ The given key does not identify an element in this collection value: string required.
Also tried with
network_interface_ids = [
azurerm_network_interface.nics[each.key].id,
]
and got ERROR
│ Error: Invalid index
│
│ on main.tf line 249, in resource "azurerm_virtual_machine" "vms":
│ 249: azurerm_network_interface.nics[each.key].id,
│ ├────────────────
│ │ azurerm_network_interface.nics is object with 2 attributes
│ │ each.key is "vm3"
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│ on main.tf line 249, in resource "azurerm_virtual_machine" "vms":
│ 249: azurerm_network_interface.nics[each.key].id,
│ ├────────────────
│ │ azurerm_network_interface.nics is object with 2 attributes
│ │ each.key is "vm4"
│
│ The given key does not identify an element in this collection value
What I'm doing wrong ?
Replicated the same scenario and able to create resources.
Made couple of changes for the existing code base provided
Added **nic = "nic" value at vms block
Updated network_interface_ids = [azurerm_network_interface.nics[each.value.nic].id,]
Here is the code snippet.
Step1:
Main tf code as below
provider "azurerm" {
features {}
}
variable "prefix" {
default = "rg_swarna"
}
resource "azurerm_resource_group" "rg" {
name = "${var.prefix}-rg"
location = "West US"
// tags = var.tags
}
resource "azurerm_virtual_network" "vnet" {
name = "${var.prefix}-network-1"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
// tags = var.tags
}
resource "azurerm_subnet" "subnet" {
name = "${var.prefix}-network-subnet-1"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.2.0/24"]
}
resource "azurerm_network_interface" "nics" {
for_each = var.nics
name = each.value.name
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "${each.value.name}-conf-1"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
}
//tags = var.tags
}
resource "azurerm_virtual_machine" "vms" {
for_each = var.vms
name = each.value.name
vm_size = "Standard_DS1_v2"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
network_interface_ids = [azurerm_network_interface.nics[each.value.nic].id,]
storage_os_disk {
name = "myosdisk${each.value.name}"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
os_profile {
computer_name = "TestDemo"
admin_username = "azureuser"
admin_password = "*****#123"
}
os_profile_linux_config {
disable_password_authentication = false
}
}
Step2:
variable tf file
variable "allowed_subnet_ids" {
type = list(string)
description = "access"
}
variable "nics" {
type = map
default = {
nic3 = {
name = "ubun3"
}
nic4 = {
name = "ubun4"
}
}
}
variable "vms" {
description = "VM"
type = map
default = {
vm3 = {
name = "ubun3"
size = "Standard_DS1_v2"
nic = "nic3"
}
vm4 = {
name = "ubuntu4"
size = "Standard_DS1_v2"
nic = "nic4"
}
}
}
variable "allowed_ips" {
type = list(string)
description = "IP addresses"
}
variable "sku" {
type = string
description = "SKU"
}
variable "resource_group_name" {
type = string
description = "resource_group_name"
}
variable "location" {
type = string
description = "location"
}
Step3:
terraform plan
terraform apply -auto-approve
Here are the reference screenshots
Here is the output from above code
In order for this to work, you would need to modify the variable for VMs slightly:
variable "vms" {
description = "Virtual Machines"
type = map
default = {
vm3 = {
name = "ubuntu-test-3"
size = "Standard_DS1_v2"
nic = "nic3"
}
vm4 = {
name = "ubuntu-test-4"
size = "Standard_DS1_v2"
nic = "nic4"
}
}
}
Then, in the VM resource block:
resource "azurerm_virtual_machine" "vms" {
for_each = var.vms
name = each.value.name
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
vm_size = each.value.size
tags = var.tags
network_interface_ids = [
azurerm_network_interface.nics[each.value.nic].id,
]
}
Alternatively, you could try with resource chaining with for_each [1], but then you would have to refactor the resource block a bit:
resource "azurerm_virtual_machine" "vms" {
for_each = azurerm_network_interface.nics
name = each.value.name
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
vm_size = var.vm_size # or set it to be equal to "Standard_DS1_v2"
tags = var.tags
network_interface_ids = [
each.value.id,
]
}
Then, you would also have to define a new variable called vm_size:
variable "vm_size" {
type = string
description = "VM size."
default = "Standard_DS1_v2"
}
In the second case, you could remove the variable vms completely.
[1] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each#chaining-for_each-between-resources
Objective: Trying to create azure vnet where address space differ based on environment
Code I am trying:
Variable.tf:
variable "vnet_address_space" {
type = map(any)
default = {
"Dev" = ["xx.xx.0.0/24","xx.xx.0.0/24","xx.xx.0.0/24","xx.xx.0.0/20"]
"Stage" = ["xx.xx.0.0/24","xx.xx.0.0/24","xx.xx.0.0/24","xx.xx.0.0/20"]
"Prod" = ["xx.xx.0.0/24","xx.xx.0.0/24","xx.xx.0.0/24","xx.xx.0.0/20"]
}
}
Main.tf: (updated)
resource "azurerm_virtual_network" "vnet" {
name = var.hub_vnet_name
location = azurerm_resource_group.rg[0].location
resource_group_name = azurerm_resource_group.rg[0].name
for_each = {for k,v in var.vnet_address_space: k=>v if k == "Dev"}
address_space = var.vnet_address_space.Dev
dns_servers = var.dns_servers
tags = {
environment = "${var.env}"
costcentre = "14500"
}
dynamic "ddos_protection_plan" {
for_each = local.if_ddos_enabled
content {
id = azurerm_network_ddos_protection_plan.ddos[0].id
enable = false
}
}
}
However It did not work as intended
Error I get:
on main.tf line 85, in resource "azurerm_virtual_network" "vnet":
│ 85: address_space = [var.vnet_address_space]
│ ├────────────────
│ │ var.vnet_address_space is a map of dynamic, known only after apply
│
│ Inappropriate value for attribute "address_space": element 0: string required.
resource "azurerm_public_ip" "pip_bastion_hub" {
name = var.public_ip_for_bastion_host_hub
location = var.Location
resource_group_name = var.resource_group_hub
allocation_method = "Static"
sku = "Standard"
}
resource "azurerm_bastion_host" "bastion-host_hub" {
name = var.bastion_host_name_for_hub
location = var.Location
resource_group_name = var.resource_group_hub
sku = "Standard"
ip_connect_enabled = "true"
ip_configuration {
name = "configuration"
subnet_id = azurerm_subnet.hub_subnet1.id
public_ip_address_id = azurerm_public_ip.pip_bastion_hub.id
}
depends_on = [azurerm_public_ip.pip_bastion_hub,azurerm_resource_group.rg_spoke2]
}
│ Error: Unsupported argument
│
│ on main.tf line 137, in resource "azurerm_bastion_host" "bastion-host_hub":
│ 137: sku = "Standard"
│
│ An argument named "sku" is not expected here.
You have to upgrade your azurem provider. Sku was added in version 2.88.
I'm completely new to terraform and I'm trying to learn and write a TF code to automate Azure VM deployment. I'm trying to cover each parts as modules (except rg) rather than keeping it in a single main.tf file.
My intention is to create 1 vnet (TESTVNET) and create multiple subnets in same Vnet, where I can define the subnet name and address in my tfvars file.
I'm able to reach till creation on VNet, but cant loop through the defined subnets
Please go through my code. File Main.tf
resource "azurerm_resource_group" "resource_group" {
name = var.RGname
location = var.RGlocation
}
module "VNET" {
source = "./Modules/NetworkConfig"
name = var.VNETname
address_space = var.address_space
location = var.RGlocation
resource_group_name = azurerm_resource_group.resource_group.name
}
module "SUBNETS" {
source = "./Modules/SubnetConfig"
Subnetlist = var.Subnetlist
virtual_network_name = module.VNET.vnet_name
resource_group_name = azurerm_resource_group.resource_group.name
depends_on = [azurerm_resource_group.resource_group, module.VNET.vnet]
}
Variables.tf (of main)
variable "RGlocation" {
}
variable "RGname" {
}
variable "VNETname" {
}
variable "address_space" {
}
variable "Subnetlist" {
type = map(object({
name = list(string)
address = list(string)
}))
}
File main.tfvars
RGlocation = "westus"
RGname = "TEST-RG1"
VNETname = "TEST-VNET-01"
address_space = "10.0.0.0/16"
Subnetlist = {
"list" = {
name = ["TESTSUBNET","TESTSUBNET1","TESTSUBNET2"]
address = ["10.0.1.0/24","10.0.2.0/24","10.0.3.0/24"]
}
}
File Subnets.tf (module)
resource "azurerm_subnet" "SUBNETS" {
for_each=var.Subnetlist
name=each.value.name
address_prefixes=each.value.address
resource_group_name = var.resource_group_name
virtual_network_name = var.virtual_network_name
}
File variable.tf (subnet module)
variable "resource_group_name" {
}
variable "virtual_network_name" {
}
variable "Subnetlist" {
type = map(object({
name = list(string)
address = list(string)
}))
}
Below if the error that I'm getting
╷
│ Error: Incorrect attribute value type
│
│ on Modules\SubnetConfig\Subnet.tf line 3, in resource "azurerm_subnet" "SUBNETS":
│ 3: name=each.value.name
│ ├────────────────
│ │ each.value.name is list of string with 3 elements
│
│ Inappropriate value for attribute "name": string required.
Could anyone please tell me how to resolve it? Also, please do let me know if this is not the right approach.
The way you iterate over Subnetlist is incorrect - you only get the value of "list" key, ending up with a bundle of subnets instead of individual items. Make it a map of individual subnet objects instead:
variable "Subnetlist" {
type = map(object({
name = string
address = string
}))
}
Then pass it in tfvars like:
Subnetlist = {
"s1" = { name = "TESTSUBNET", address = "10.0.1.0/24" },
"s2" = { name = "TESTSUBNET1", address = "10.0.2.0/24" },
"s3" = { name = "TESTSUBNET2", address = "10.0.3.0/24" }
}
Finally consume it in the module like this:
resource "azurerm_subnet" "SUBNETS" {
for_each = var.Subnetlist
name = each.value.name
address_prefixes = each.value.address
resource_group_name = var.resource_group_name
virtual_network_name = var.virtual_network_name
}