Terraform foreach on azure nics (map) - azure

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.

Related

How to create conditional vnet based on address space that I give in terraform

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.

How to loop correctly in terraform for_each?

Objective: Loop through azure subnets via terraform.
Code That I use:
Main.tf:
resource "azurerm_network_security_group" "nsg" {
name = "nsg-vnet-hub-${var.env}-indoundDNS"
location = azurerm_resource_group.rg[0].location
resource_group_name = azurerm_resource_group.rg[0].name
tags = {
environment = "${var.env}"
costcentre = "12345"
}
}
resource "azurerm_monitor_diagnostic_setting" "nsg" {
for_each = var.subnets
name = lower("${each.key}-diag")
target_resource_id = azurerm_network_security_group.nsg[each.key].id
storage_account_id = azurerm_storage_account.storeacc.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.logws.id
dynamic "log" {
for_each = var.nsg_diag_logs
content {
category = log.value
enabled = true
retention_policy {
enabled = false
}
}
}
}
My root module variable.tf :
variable "subnets" {
type = map(object({
name = string
}))
default = {
"s1" = { name = "dns_snet"},
"s2" = { name = "common_snet"},
"s3" = { name = "gw_snet"},
"s4" = { name = "data_snet"}
}
}
Problem I am facing:
Error:
network_security_group_id = azurerm_network_security_group.nsg[each.key].id
│ ├────────────────
│ │ azurerm_network_security_group.nsg is object with 7 attributes
│ │ each.key is "s3"
│
│ The given key does not identify an element in this collection value
Just updated this post, now I get error as above. I am referring to below documentation
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group
You have only a single instance of azurerm_network_security_group.nsg. Thus there is nothing to iterate over. To fix your error, it should be:
target_resource_id = azurerm_network_security_group.nsg.id

Terraform error while fetching value of subnet in oci

I have this code where I am trying to fetch the id of subnet via subnet name:
The code looks like this :
resource "oci_containerengine_node_pool" "node_pool" {
for_each = var.nodepools
cluster_id = oci_containerengine_cluster.cluster[0].id
compartment_id = var.compartment_id
depends_on = [oci_containerengine_cluster.cluster]
kubernetes_version = var.cluster_kubernetes_version
name = each.value["name"]
node_config_details {
placement_configs {
availability_domain = var.availability_domain
subnet_id = oci_core_subnet.snet-workers[each.value.subnet_name].id
}
size = each.value["size"]
}
node_shape = each.value["node_shape"]
node_shape_config {
#Optional
memory_in_gbs = each.value["memory"]
ocpus = each.value["ocpus"]
}
node_source_details {
image_id = each.value["image_id"]
source_type = "IMAGE"
}
ssh_public_key = file(var.ssh_public_key_path)
}
My subnet code looks like:
resource "oci_core_subnet" "snet-workers" {
cidr_block = lookup(var.subnets["snet-workers"], "subnet_cidr")
compartment_id = var.compartment_id
vcn_id = oci_core_virtual_network.base_vcn.id
display_name = lookup(var.subnets["snet-workers"], "display_name")
dns_label = lookup(var.subnets["snet-workers"], "dns_label")
prohibit_public_ip_on_vnic = true
security_list_ids = [oci_core_security_list.private_worker_nodes.id]
route_table_id = oci_core_route_table.rt-nat.id
}
variables looks like:
variable "subnets" {
description = "List of subnets to create for an environment"
type = map(object({
subnet_cidr = string
display_name = string
dns_label = string
}))
}
my tfvars looks like
nodepools = {
np1 = {
name = "np1"
size = 3
ocpus = 8
memory = 120
image_id = "test"
node_shape = "VM.Standard2.8"
subnet_name = "snet-worker1"
}
np2 = {
name = "np2"
size = 2
ocpus = 8
memory = 120
image_id = "test"
node_shape = "VM.Standard2.8"
subnet_name = "snet-worker2"
}
}
But in terraform plan I am getting error as
Error: Invalid index
│
│ on ../modules/oke/oke.tf line 39, in resource "oci_containerengine_node_pool" "node_pool":
│ 39: subnet_id = oci_core_subnet.snet-workers[each.value.subnet_name].id
│ ├────────────────
│ │ each.value.subnet_name is "snet-worker2"
│ │ oci_core_subnet.snet-workers is object with 22 attributes
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│ on ../modules/oke/oke.tf line 39, in resource "oci_containerengine_node_pool" "node_pool":
│ 39: subnet_id = oci_core_subnet.snet-workers[each.value.subnet_name].id
│ ├────────────────
│ │ each.value.subnet_name is "snet-worker1"
│ │ oci_core_subnet.snet-workers is object with 22 attributes
│
│ The given key does not identify an element in this collection value.
Can someone help
The following:
subnet_id = oci_core_subnet.snet-workers[each.value.subnet_name].id
would only work if you had used for_each while creating oci_core_subnet.snet-workers. Since you are not using for_each, it should be:
subnet_id = oci_core_subnet.snet-workers.id
UPDATE
To keep using original version:
resource "oci_core_subnet" "snet-workers" {
for_each = var.subnets
cidr_block = each.value["subnet_cidr"]
compartment_id = var.compartment_id
vcn_id = oci_core_virtual_network.base_vcn.id
display_name = leach.value[""display_name"]
dns_label = each.value["dns_label"]
prohibit_public_ip_on_vnic = true
security_list_ids = [oci_core_security_list.private_worker_nodes.id]
route_table_id = oci_core_route_table.rt-nat.id
}
You have to define a "oci_core_subnet" for each entry in "subnets" variable. Something like this:
resource "oci_core_subnet" "snet-workers" {
for_each = var.subnets
cidr_block = each.value.subnet_cidr
...
}

How to reference count index in another module?

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
}

Terraform using count.index with tags

using terraform, i'm trying to include the count in the tags of my resource using count.index, but getting this error :
Error: Incorrect attribute value type
│
│ on ..\modules\sn\ressources.tf line 16, in resource "aws_subnet" "prod_sn":
│ 16: tags = var.sn_tags[count.index]
│ ├────────────────
│ │ count.index is a number, known only after apply
│ │ var.sn_tags is a list of string, known only after apply
│
│ Inappropriate value for attribute "tags": map of string required.
vars.tf
variable "sn_tags" {
type = list (string)
default = ["aa", "bb"]
}
ressources.tf
resource "aws_subnet" "prod_sn" {
count = length(var.sn_cidr)
vpc_id = var.vpc_id
cidr_block = var.sn_cidr[count.index]
availability_zone = data.aws_availability_zones.azs.names[count.index]
tags = var.sn_tags[count.index]
}
main.tf
# Create Public Subnet on availability_zone "3a"
module "publicSn-a" {
source = "../modules/sn"
vpc_id = module.vpc.vpcId
sn_cidr = ["10.0.1.0/24", "10.0.2.0/24"]
sn_tags = ["prodPublicA","prodPublicB"]
}
Your issue is that each loop iteration is trying to pass a string type to the tags parameter. If you break it down to just a single resource without the count (using the first element for now) then your current code is basically this:
resource "aws_subnet" "prod_sn" {
vpc_id = var.vpc_id
cidr_block = "10.0.1.0./24"
availability_zone = "eu-west-1a" # Note may not be this but the data source and the index will at least resolve to a single string AZ
tags = "prodPublicA"
}
If we look at the documentation for the aws_subnet resource we can see that the tags parameter wants a map, not a string as the error implies.
You could fix this by changing your list(string) variable into a list(map) so instead you have something like this instead:
variable "sn_tags" {
type = list(map)
}
and
# Create Public Subnet on availability_zone "3a"
module "publicSn-a" {
source = "../modules/sn"
vpc_id = module.vpc.vpcId
sn_cidr = ["10.0.1.0/24", "10.0.2.0/24"]
sn_tags = [
{
name = "prodPublicA"
},
{
name = "prodPublicB"
},
]
}
Alternatively if you just want to add a Name tag to all the subnets and don't want more flexibility with the tags instead you could rework it like this:
variable "sn_names" {
type = list(string)
}
resource "aws_subnet" "prod_sn" {
count = length(var.sn_cidr)
vpc_id = var.vpc_id
cidr_block = var.sn_cidr[count.index]
availability_zone = data.aws_availability_zones.azs.names[count.index]
tags = {
Name = var.sn_names[count.index]
}
}
and call it like so:
# Create Public Subnet on availability_zone "3a"
module "publicSn-a" {
source = "../modules/sn"
vpc_id = module.vpc.vpcId
sn_cidr = ["10.0.1.0/24", "10.0.2.0/24"]
sn_names = ["prodPublicA","prodPublicB"]
}

Resources