Referencing resource instances created by "for_each" in Terraform - terraform

I'm testing the "for_each" resource attribute now available in Terraform 12.6 but can't manage to reference created instances in other resources.
azure.tf
variable "customers" {
type = map(object({name=string}))
}
resource "azurerm_storage_account" "provisioning-datalake" {
for_each = var.customers
name = "mydatalake${each.key}"
resource_group_name = "${azurerm_resource_group.provisioning-group.name}"
location = "${azurerm_databricks_workspace.databricks.location}"
account_kind = "StorageV2"
account_tier = "Standard"
account_replication_type = "GRS"
is_hns_enabled = true
enable_advanced_threat_protection = true
tags = {
environment = var.environment
customer = each.value.name
}
}
resource "azurerm_key_vault_secret" "key-vault-datalake-secret" {
for_each = var.customers
name = "mydatalake-shared-key-${each.key}"
value = azurerm_storage_account.provisioning-datalake[each.key].primary_access_key
key_vault_id = azurerm_key_vault.key-vault.id
tags = {
environment = var.environment
customer = each.value.name
}
}
build.tfvars
environment = "Build"
customers = {
dev = {
name = "Development"
},
int = {
name = "Integration"
},
stg = {
name = "Staging"
}
}
I expect "keyvault-datalake-secret" entries to be created with the matching keys of the generated "provisioning-datalake" resources.
But when I run terraform plan --var-file=build.tfvars, I get the following error:
Error: Invalid index
on azure.tf line 45, in resource "azurerm_key_vault_secret" "key-vault-datalake-secret":
45: value = azurerm_storage_account.provisioning-datalake[each.key].primary_access_key
|----------------
| azurerm_storage_account.provisioning-datalake is object with 52 attributes
| each.key is "stg"
The given key does not identify an element in this collection value.
Error: Invalid index
on azure.tf line 45, in resource "azurerm_key_vault_secret" "key-vault-datalake-secret":
45: value = azurerm_storage_account.provisioning-datalake[each.key].primary_access_key
|----------------
| azurerm_storage_account.provisioning-datalake is object with 52 attributes
| each.key is "int"
The given key does not identify an element in this collection value.
Error: Invalid index
on azure.tf line 45, in resource "azurerm_key_vault_secret" "key-vault-datalake-secret":
45: value = azurerm_storage_account.provisioning-datalake[each.key].primary_access_key
|----------------
| azurerm_storage_account.provisioning-datalake is object with 52 attributes
| each.key is "dev"
The given key does not identify an element in this collection value.

Bug corrected in Terraform 0.12.7

Related

Terraform expression for_each invalid index

HI Guys and happy new year to all
I got an issue to gatter the token generate by the bloc ressource below which have an iteration with an for_each loop.
my varibale map is :
variable "wvd_hostpool" {
description = "Please provide the required information to create a WVD hostpool."
type = map(any)
default = {
hp-azcan-weu-wvd-01 = {
"name" = "hp-azcan-weu-wvd-01"
"type" = "Personal"
"load_balancer_type" = "DepthFirst"
"personal_desktop_assignment_type" = "Automatic"
"maximum_sessions_allowed" = 16
"expiration_date" = "2022-02-10T18:46:43Z"
"friendly_name" = "Canary"
"description" = "Dedicated to canary deployments."
"location" = "westeurope"
"vm_count" = 1
"vm_size" = "Standard_F4s_v2"
"vm_prefix" = "AZWEUHP01TST"
"validate_environment" = "true"
},
hp-azprd-weu-wvd-01 = {
"name" = "hp-azprd-weu-wvd-01"
"type" = "Pooled"
"load_balancer_type" = "DepthFirst"
"personal_desktop_assignment_type" = "Automatic"
"maximum_sessions_allowed" = 16
"expiration_date" = "2022-02-10T18:46:43Z"
"friendly_name" = "desktop"
"description" = "Dedicated to medium workload type (Microsoft Word, CLIs, ...)."
"location" = "westeurope"
"vm_count" = 1
"vm_size" = "Standard_F4s_v2"
"vm_prefix" = "AZWEUHP01WKT"
"validate_environment" = "false"
},
the ressource bloc :
resource "azurerm_virtual_desktop_host_pool" "wvd_hostpool" {
for_each = var.wvd_hostpool
name = each.value.name
location = each.value.location
custom_rdp_properties = "audiocapturemode:i:1;audiomode:i:0;"
resource_group_name = data.azurerm_resource_group.avd_rg.name
validate_environment = each.value.validate_environment
type = each.value.type
load_balancer_type = each.value.load_balancer_type
friendly_name = each.value.friendly_name
description = each.value.description
personal_desktop_assignment_type = each.value.personal_desktop_assignment_type
maximum_sessions_allowed = each.value.maximum_sessions_allowed
registration_info {
expiration_date = each.value.expiration_date
}
}
I would get the value of the token generate under registration_info to save it to a key vault for reuse or export it to an output but has you can see I getting an error with invalid index. I speding 2 day without sucess at this could you help me please ?
resource "azurerm_key_vault_secret" "wvd_registration_info" {
for_each = var.wvd_hostpool
name = each.value.name
value = azurerm_virtual_desktop_host_pool.wvd_hostpool[each.value.name].registration_info.0.token
key_vault_id = azurerm_key_vault.wvd_key_vault.id
depends_on = [azurerm_role_assignment.wvd_sp]
}
the same result
Error: Invalid index
│
│ on security.tf line 115, in resource "azurerm_key_vault_secret" "wvd_registration_info":
│ 115: value = azurerm_virtual_desktop_host_pool.wvd_hostpool[each.value.name].registration_info[0].token
│ ├────────────────
│ │ azurerm_virtual_desktop_host_pool.wvd_hostpool is object with 3 attributes
│ │ each.value.name is "hp-azprd-weu-wvd-02"
│
│ The given key does not identify an element in this collection value: the collection has no elements
If you specify a map as a for_each attribute, Terraform will use its keys as identifiers for the resources which will be created. This means that if you want to reference a another resource created using a for_each, you have to use they keys from the map, or each.key in your example:
resource "azurerm_key_vault_secret" "wvd_registration_info" {
for_each = var.wvd_hostpool
name = each.value.name
value = azurerm_virtual_desktop_host_pool.wvd_hostpool[each.key].registration_info.token
key_vault_id = azurerm_key_vault.wvd_key_vault.id
depends_on = [azurerm_role_assignment.wvd_sp]
}

In terraform using Azure, is it possible to create a key vault secret and reference that secret in the same file on the same run?

I am attempting to generate a random username and password, store them in key vault and then immediately (after they are stored) retrieve them and use them as variables in the sql server creation.
Considering this code:
resource "random_string" "username" {
length = 24
special = true
override_special = "%#!"
}
resource "random_password" "password" {
length = 24
special = true
override_special = "%#!"
}
# # Create KeyVault Secret
resource "azurerm_key_vault_secret" "sql-1-username" {
name = "sql-server-1-username"
value = random_string.username.result
key_vault_id = azurerm_key_vault.key_vault.id
tags = merge(local.common_tags, tomap({"type" = "key-vault-secret-username"}), tomap({"resource" = azurerm_mssql_server.sql-server_1.name}))
depends_on = [azurerm_key_vault.key_vault]
}
resource "azurerm_key_vault_secret" "sql-1-password" {
name = "sql-server-1-password"
value = random_password.password.result
key_vault_id = azurerm_key_vault.key_vault.id
tags = merge(local.common_tags, tomap({"type" = "key-vault-secret-password"}), tomap({"resource" = azurerm_mssql_server.sql-server_1.name}))
depends_on = [azurerm_key_vault.key_vault]
}
data "azurerm_key_vault_secret" "sql-server-1-username" {
name = "sql-server-1-username"
key_vault_id = azurerm_key_vault.key_vault.id
}
data "azurerm_key_vault_secret" "sql-server-1-password" {
name = "sql-server-1-password"
key_vault_id = azurerm_key_vault.key_vault.id
}
resource "azurerm_mssql_server" "sql-server_1" {
name = "${local.resource-name-prefix}-sql-server-1"
resource_group_name = local.resource-group-name
location = var.resource-location
version = "12.0"
administrator_login = data.azurerm_key_vault_secret.sql-server-1-username.value
administrator_login_password = data.azurerm_key_vault_secret.sql-server-1-password.value
tags = merge(local.common_tags, tomap({"type" = "mssql-server"}))
}
When running this via terraform I get:
│ Error: KeyVault Secret "sql-server-1-username" <<<KEY VAULT>>> does not exist
│
│ with data.azurerm_key_vault_secret.sql-server-1-username,
│ on sql-server.tf line 31, in data "azurerm_key_vault_secret" "sql-server-1-username":
│ 31: data "azurerm_key_vault_secret" "sql-server-1-username" {
│
╵
╷
│ Error: KeyVault Secret "sql-server-1-password" <<<KEY VAULT>>> does not exist
│
│ with data.azurerm_key_vault_secret.sql-server-1-password,
│ on sql-server.tf line 36, in data "azurerm_key_vault_secret" "sql-server-1-password":
│ 36: data "azurerm_key_vault_secret" "sql-server-1-password" {
│
and I understand because at run time terraform is trying to evaluate that secret but it hasn't been created.
My question is, is there a way to define a value, store it as a key vault secret and then upon completion of that azurerm_key_vault_secret resource being complete, retrieve that value?
As a work around, I've put lifecycle blocks with ignore_change for the username and password values on both the key vault secret resources and the sql server. That should give me the same values in key vault being used as the username/password for the sql server, but that feels like the wrong solution.
What would be the better way?
When using data.azurerm_key_vault_secret.* in azurerm_mssql_server it doesn't consider dependency , so instead of creating the keyvault secret it creates the sqlserver first as it doesn't have any dependencies on the resources created by the file thats the reason you get the error .
For solution , If you are creating the keyvault secret in the same file then instead of using data blocks , you can directly reference the value for administrator_login and administrator_login_password with azurerm_key_vault_secret.sql-1-username.value and azurerm_key_vault_secret.sql-1-password.value.
Your Code will be like below:
resource "random_string" "username" {
length = 24
special = true
override_special = "%#!"
}
resource "random_password" "password" {
length = 24
special = true
override_special = "%#!"
}
# # Create KeyVault Secret
resource "azurerm_key_vault_secret" "sql-1-username" {
name = "sql-server-1-username"
value = random_string.username.result
key_vault_id = azurerm_key_vault.key_vault.id
tags = merge(local.common_tags, tomap({"type" = "key-vault-secret-username"}), tomap({"resource" = azurerm_mssql_server.sql-server_1.name}))
depends_on = [azurerm_key_vault.key_vault]
}
resource "azurerm_key_vault_secret" "sql-1-password" {
name = "sql-server-1-password"
value = random_password.password.result
key_vault_id = azurerm_key_vault.key_vault.id
tags = merge(local.common_tags, tomap({"type" = "key-vault-secret-password"}), tomap({"resource" = azurerm_mssql_server.sql-server_1.name}))
depends_on = [azurerm_key_vault_secret.sql-1-username]
}
resource "azurerm_mssql_server" "sql-server_1" {
name = "${local.resource-name-prefix}-sql-server-1"
resource_group_name = local.resource-group-name
location = var.resource-location
version = "12.0"
administrator_login = azurerm_key_vault_secret.sql-1-username.value
administrator_login_password = azurerm_key_vault_secret.sql-1-password.value
tags = merge(local.common_tags, tomap({"type" = "mssql-server"}))
depends_on = [azurerm_key_vault_secret.sql-1-password]
}

Finding Ways to Merge Resource Tags

Hello Terraform Experts,
I inherited some old Terraform code for deploying resources to Azure. One of the main components that I see in most of the modules is to merge the Resource Group tags with additional tags that go on individual resources. The Resource Group tags are outputs as a map of tags. For example:
output "resource_group_tags_map" {
value = { for r in azurerm_resource_group.this : r.name => r.tags }
description = "map of rg tags."
}
and then a resource like vnets merges the RG tags with additional specific tags for the vnet given the name of the RG in a variable.
# merge Resource Group tags with Tags for VNET
# this is going to break if we change RGs
locals {
tags = merge(var.net_additional_tags, data.azurerm_resource_group.this.tags)
This works just fine if we can set the resource group in a single variable. It assumes that the resource(s) being deployed will go into one RG. However, this is not the case anymore and we somehow need to build in a way for any RG to be chosen when deploying a resource. The code below shows how the original concept works.
locals {
tags = merge(var.net_additional_tags, data.azurerm_resource_group.this.tags)
# - Virtual Network
# -
resource "azurerm_virtual_network" "this" {
for_each = var.virtual_networks
name = each.value["name"]
location = data.azurerm_resource_group.this.location
resource_group_name = var.resource_group_name
address_space = each.value["address_space"]
dns_servers = lookup(each.value, "dns_servers", null)
tags = local.tags
}
looking for help therefore to work around this. Say we create 100 vnets and each one of them goes into a different RG, we couldn't create 100 different resource group variables to capture that as it would become too cumbersome.
Here is my example with Key Vault
resource "azurerm_key_vault" "this" {
for_each = var.key_vaults
name = each.value["name"]
location = each.value["location"]
resource_group_name = each.value["resource_group_name"]
sku_name = each.value["sku_name"]
access_policy = var.access_policies
enabled_for_deployment = each.value["enabled_for_deployment"]
enabled_for_disk_encryption = each.value["enabled_for_disk_encryption"]
enabled_for_template_deployment = each.value["enabled_for_template_deployment"]
enable_rbac_authorization = each.value["enable_rbac_authorization"]
purge_protection_enabled = each.value["purge_protection_enabled"]
soft_delete_retention_days = each.value["soft_delete_retention_days"]
tags = merge(each.value["tags"], )
In the tags argument, we need to somehow merge the tags entered for this instance of Key Vault with the resource group tags that the user chose to place the key vault in. I thought of something like this, but clearly the syntax is wrong.
merge(each.value["tags"], data.azurerm_resource_group[each.key][each.value["resource_group_name"].tags)
Thanks for your input.
UPDATE:
│ Error: Invalid index
│
│ on Modules\keyvault\main.tf line 54, in resource "azurerm_key_vault" "this":
│ 54: tags = merge(each.value["tags"], data.azurerm_resource_group.this["${each.value.resource_group_name}"].tags)
│ ├────────────────
│ │ data.azurerm_resource_group.this is object with 1 attribute "keyvault1"
│ │ each.value.resource_group_name is "Terraform1"
│
│ The given key does not identify an element in this collection value.
Solution code posted below using a map and locals.
SOLUTION
Variables.tf
variable "key_vaults" {
description = "Key Vaults and their properties."
type = map(object({
name = string
location = string
resource_group_name = string
sku_name = string
tenant_id = string
enabled_for_deployment = bool
enabled_for_disk_encryption = bool
enabled_for_template_deployment = bool
enable_rbac_authorization = bool
purge_protection_enabled = bool
soft_delete_retention_days = number
tags = map(string)
}))
default = {}
}
# soft_delete_retention_days numeric value can be between 7 and 90. 90 is default
Main.tf for KeyVault module
data "azurerm_resource_group" "this" {
# read from local variable, index is resource_group_name
for_each = local.rgs_map
name = each.value.name
}
# use data azurerm_client_config to get tenant_id, not from config
data "azurerm_client_config" "current" {}
# -
# - Setup key vault
# - transform variables to locals to make sure the correct index will be used: resource group name and key vault name
locals {
rgs_map = {
for n in var.key_vaults :
n.resource_group_name => {
name = n.resource_group_name
}
}
kvs_map = {
for n in var.key_vaults :
n.name => {
name = n.name
location = n.location
resource_group_name = n.resource_group_name
sku_name = n.sku_name
tenant_id = data.azurerm_client_config.current.tenant_id # n.tenant_id
enabled_for_deployment = n.enabled_for_deployment
enabled_for_disk_encryption = n.enabled_for_disk_encryption
enabled_for_template_deployment = n.enabled_for_template_deployment
enable_rbac_authorization = n.enable_rbac_authorization
purge_protection_enabled = n.purge_protection_enabled
soft_delete_retention_days = n.soft_delete_retention_days
tags = merge(n.tags, data.azurerm_resource_group.this["${n.resource_group_name}"].tags)
}
}
}
resource "azurerm_key_vault" "this" {
for_each = local.kvs_map # use local variable, other wise keyvault1 will be used in stead of kv-eastus2-01 as index
name = each.value["name"]
location = each.value["location"]
resource_group_name = each.value["resource_group_name"]
sku_name = each.value["sku_name"]
tenant_id = each.value["tenant_id"]
enabled_for_deployment = each.value["enabled_for_deployment"]
enabled_for_disk_encryption = each.value["enabled_for_disk_encryption"]
enabled_for_template_deployment = each.value["enabled_for_template_deployment"]
enable_rbac_authorization = each.value["enable_rbac_authorization"]
purge_protection_enabled = each.value["purge_protection_enabled"]
soft_delete_retention_days = each.value["soft_delete_retention_days"]
tags = each.value["tags"]
}

Terraform: The given key does not identify an element in this collection value

Trying to implement the VNET Integration for my azure app service slots. Here we are able to create multiple slots with the for_each and all the slots should integreate with the same Vnet/Subnet (vnet integration)
resource "azurerm_app_service_slot" "app_service_slot_template" {
for_each = {for sl in "${var.app_service_slots}" : sl.name => sl }
app_service_name = "${var.mandatory_prefix}-${var.app_service_name}"
resource_group_name = "${var.resource_group_name}"
app_service_plan_id = "${data.azurerm_app_service_plan.service_plan.id}"
location = "${var.resource_location}"
https_only = true
name = "${each.value["name"]}"
dynamic "site_config" {
for_each = "${var.site_config}"
content {
min_tls_version = lookup(site_config.value, "min_tls_version", null)
python_version = lookup(site_config.value, "python_version", null)
java_version = lookup(site_config.value, "java_version", null)
always_on = lookup(site_config.value, "always_on", null)
app_command_line = lookup(site_config.value, "app_command_line", null)
dotnet_framework_version = lookup(site_config.value, "dotnet_framework_version", null)
}
}
dynamic "connection_string" {
for_each = "${each.value["connection_strings"]}"
content {
name = "${connection_string.value["name"]}"
type = "${connection_string.value["type"]}"
value = "${connection_string.value["value"]}"
}
}
app_settings = "${merge(each.value["app_settings"], local.additional_app_settings)}"
}
# Get the Id of the subnet
data "azurerm_subnet" "azurerm_subnet_template" {
name = "${var.subnet_name}"
virtual_network_name = "${var.virtual_network_name}"
resource_group_name = "${var.vnet_resource_group_name}"
}
# Create Vnet Integration (which is failing The given key does not identify an element in this collection value.)
resource "azurerm_app_service_virtual_network_swift_connection" "azureapp_vnet_integration_for_slot" {
count = length("${var.app_service_slots}")
app_service_id = "${azurerm_app_service_slot.app_service_slot_template["${count.index}"].id}"
subnet_id = "${data.azurerm_subnet.azurerm_subnet_template.id}"
depends_on = [azurerm_app_service_slot.app_service_slot_template]
}
All the above working fine except the vnet integration, The error I'm getting is
on .terraform/modules/app_service/main.tf line 239, in resource
"azurerm_app_service_virtual_network_swift_connection"
"azureapp_vnet_integration_for_slot": 239: app_service_id =
"${azurerm_app_service_slot.app_service_slot_template["${count.index}"].id}"
|----------------
| azurerm_app_service_slot.app_service_slot_template is object with 1 attribute "stage"
| count.index is 0
The given key does not identify an element in this collection value.
Googling leads me to this github issue https://github.com/terraform-providers/terraform-provider-azurerm/issues/5675
Where there are lots of discussions reg its the terraform bug, Not sure Am I hitting this bug ? Please note that my terraform version is v0.13.4
If is it a bug with my terraform version, is there any workaround to this without upgrading the version ? because upgrading the tf version is a big process for us.
Update 1:
updated the for_each instead of count for Vnet Integration part
resource "azurerm_app_service_virtual_network_swift_connection" "azureapp_vnet_integration_for_slot" {
for_each = {for sl in "${azurerm_app_service_slot.app_service_slot_template}" : sl.name => sl }
//count = length("${var.app_service_slots}")
//app_service_id = "${azurerm_app_service_slot.app_service_slot_template["${count.index}"].id}"
app_service_id = "${azurerm_app_service_slot.app_service_slot_template.*.id}"
subnet_id = "${data.azurerm_subnet.azurerm_subnet_template.id}"
depends_on = [azurerm_app_service_slot.app_service_slot_template]
}
Now the Error coming as
Error: Incorrect attribute value type
on .terraform/modules/generic_app_service/main.tf line 240, in
resource "azurerm_app_service_virtual_network_swift_connection"
"azureapp_vnet_integration_for_slot": 240: app_service_id =
"${azurerm_app_service_slot.app_service_slot_template.*.id}"
|----------------
| azurerm_app_service_slot.app_service_slot_template is object with 1 attribute "stage"
Inappropriate value for attribute "app_service_id": string required.
Error: Unsupported attribute
on .terraform/modules/generic_app_service/main.tf line 240, in
resource "azurerm_app_service_virtual_network_swift_connection"
"azureapp_vnet_integration_for_slot": 240: app_service_id =
"${azurerm_app_service_slot.app_service_slot_template.*.id}"
This object does not have an attribute named "id".
Upate 2:
resource "azurerm_app_service_virtual_network_swift_connection" "azureapp_vnet_integration_for_slot" {
for_each = {for sl in "${azurerm_app_service_slot.app_service_slot_template}" : sl.name => sl }
app_service_id = "${each.value.id}"
subnet_id = "${data.azurerm_subnet.azurerm_subnet_template.id}"
depends_on = [azurerm_app_service_slot.app_service_slot_template]
}
This works perfectly on the Terraform plan, however during the apply it failed with the below error
Error: Error parsing App Service Resource ID
on .terraform/modules/generic_app_service/main.tf line 235, in
resource "azurerm_app_service_virtual_network_swift_connection"
"azureapp_vnet_integration_for_slot": 235: resource
"azurerm_app_service_virtual_network_swift_connection"
"azureapp_vnet_integration_for_slot" {
I can confirm from the plan output the app_service_id is getting the correct resource id for the azure slot but don't know why it is complaining about App Service Resource ID

terraform apply from not generated resource

I'm trying to create a list of maps from my list of subnet names so I've created the following:
created a variable named subnet_names of type list of strings
created a null resource block to create a list of maps from this list, like this:
resource "null_resource" "subnet_mapping" {
count = "${length(var.subnet_names)}"
triggers = {
name = "${element(var.subnet_names, count.index)}"
number = "${count.index}"
}
}
if I only execute this block I have my list of maps correctly but when I try to use this list of maps with a dynamic block this is not working.
resource "azurerm_virtual_network" "virtual_network" {
address_space = "${var.cidr_network_range}"
location = "${var.location}"
name = "${var.virtual_network_resource_name}"
resource_group_name = "${var.resource_group_name}"
count = "${length(var.subnet_names)}"
dynamic "subnet"{
for_each = [for s in null_resource.subnet_mapping: {
name = s.name
prefix = cidrsubnet(element(var.cidr_network_range, 0),8 ,s.number)
}]
content {
name = subnet.value.name
address_prefix = subnet.value.prefix
}
}
depends_on = [null_resource.subnet_mapping]
}
resource "null_resource" "subnet_mapping" {
count = "${length(var.subnet_names)}"
triggers = {
name = "${element(var.subnet_names, count.index)}"
number = "${count.index}"
}
}
resource "azurerm_resource_group" "virtual_network_group" {
location = "${var.location}"
name = "${var.resource_group_name}"
}
it should be valid, but I still don't have the output of the null_resource so it fails
dynamic "subnet"{
for_each = [for s in null_resource.subnet_mapping: {
name = s.name
prefix = cidrsubnet(element(var.cidr_network_range, 0),8 ,s.number)
}]
content {
name = subnet.value.name
address_prefix = subnet.value.prefix
}
}
depends_on = [null_resource.subnet_mapping]
}
my error message:
Error: Unsupported attribute
on main.tf line 10, in resource "azurerm_virtual_network" "virtual_network":
10: name = s.name
This object does not have an attribute named "name".
Error: Unsupported attribute
on main.tf line 10, in resource "azurerm_virtual_network" "virtual_network":
10: name = s.name
This object does not have an attribute named "name".
Error: Unsupported attribute
on main.tf line 10, in resource "azurerm_virtual_network" "virtual_network":
10: name = s.name
This object does not have an attribute named "name".
Error: Unsupported attribute
on main.tf line 10, in resource "azurerm_virtual_network" "virtual_network":
10: name = s.name
This object does not have an attribute named "name".
Error: Unsupported attribute
on main.tf line 11, in resource "azurerm_virtual_network" "virtual_network":
11: prefix = cidrsubnet(element(var.cidr_network_range, 0),8 ,s.number)
This object does not have an attribute named "number".
Error: Unsupported attribute
on main.tf line 11, in resource "azurerm_virtual_network" "virtual_network":
11: prefix = cidrsubnet(element(var.cidr_network_range, 0),8 ,s.number)
This object does not have an attribute named "number".
Error: Unsupported attribute
on main.tf line 11, in resource "azurerm_virtual_network" "virtual_network":
11: prefix = cidrsubnet(element(var.cidr_network_range, 0),8 ,s.number)
This object does not have an attribute named "number".
Error: Unsupported attribute
on main.tf line 11, in resource "azurerm_virtual_network" "virtual_network":
11: prefix = cidrsubnet(element(var.cidr_network_range, 0),8 ,s.number)
This object does not have an attribute named "number".
From the message, I'm not clear how many Vnets do you want to create, because your code is a little confusing. For your issue with the null_resource, I don't think it's a good choice, I recommend the locals.
Here I assume you only want to create one Vnet with serial subnets and use a list to store the names of the subnets, then the example core code below:
locals {
subnets = [for sname in var.subnet_names: {
name = sname
# the index begin from 0, so you need to add 1
number = index(var.subnet_names, sname) + 1
}]
}
resource "azurerm_virtual_network" "virtual_network" {
address_space = "${var.cidr_network_range}"
location = "${var.location}"
name = "${var.virtual_network_resource_name}"
resource_group_name = "${var.resource_group_name}"
dynamic "subnet"{
for_each = [for s in local.subnets: {
name = s.name
prefix = cidrsubnet(var.cidr_network_range, 8 , s.number)
}]
content {
name = subnet.value.name
address_prefix = subnet.value.prefix
}
}
}
I solved this way, I've created a variable named
variable "subnets" {
type = list(map(string))
description = "A list of maps of names and network addresses bits for subnets that will be created inside this network (this is parallel to subnet_prefixes array)."
}
resource "azurerm_virtual_network" "virtual_network" {
address_space = var.cidr_network_range
location = var.location
name = var.virtual_network_resource_name
resource_group_name = var.resource_group_name
dynamic "subnet"{
for_each = [for subnet in var.subnets: {
name = subnet.name
prefix = cidrsubnet(element(var.cidr_network_range, 0),8 ,subnet.number)
}]
content {
name = subnet.value.name
address_prefix = subnet.value.prefix
}
}
}
resource "azurerm_resource_group" "virtual_network_group" {
location = var.location
name = var.resource_group_name
}
This way I already created the map, I would like to have created it automatically as I posted before but it seems to not be very common in terraform way.
In Charles Xu example the mapping will be created based on a sequence (1,2,3) and I would like to use it always in different ways like (21, 24, 26)
So the only solution I could find was to force the map before.
Anyway, thank you everyone

Resources