I am trying to bypass a virtual_network_subnet_id
if a subnet variable is passed or not. It seems if the subnet variable is null, terraform is still trying to resolve the value if it is true and thus failing when resolving a null index. Any idea on how to pass a null value to the virtual_network_subnet_id
if the subnet variable is not present, and then if it is present obtain the subnet variable from the azurerm subnet data block?
resource "azurerm_windows_web_app" "web-app" {
for_each = var.web_app
name = each.key
resource_group_name = data.azurerm_resource_group.rg2.name
location = data.azurerm_resource_group.rg2.location
tags = merge(local.base_tags, { Component = each.value.component_tag })
service_plan_id = azurerm_service_plan.asp[each.value.asp].id
virtual_network_subnet_id = each.value.subnet != "" ? data.azurerm_subnet.vnet-subnets[each.value.subnet].id : null
site_config {
ftps_state = "FtpsOnly"
minimum_tls_version = 1.2
vnet_route_all_enabled = each.value.subnet != "" ? true : false
worker_count = each.value.worker_count
always_on = each.value.always_on
use_32_bit_worker = each.value.use_32_bit_worker
application_stack {
dotnet_version = each.value.dotnet_version
}
ip_restriction_default_action = "Deny"
dynamic "ip_restriction" {
for_each = local.vpn
content {
ip_address = ip_restriction.value["ip_address"]
action = "Allow"
priority = ip_restriction.value["priority"]
name = ip_restriction.value["name"]
}
}
dynamic "ip_restriction" {
for_each = local.azure_service_tag
content {
name = ip_restriction.value["name"]
service_tag = ip_restriction.value["service_tag"]
priority = ip_restriction.value["priority"]
}
}
#Subnets
dynamic "ip_restriction" {
for_each = local.subnets
content {
name = ip_restriction.value["subnet_name"]
virtual_network_subnet_id = data.azurerm_virtual_network.vnet.id
priority = ip_restriction.value["priority"]
}
}
}
identity {
type = "SystemAssigned"
}
lifecycle {
ignore_changes = [
identity,
app_settings,
connection_string,
https_only,
site_config
]
}
}
Error:
│ Error: Invalid index
│
│ on web_app.tf line 12, in resource "azurerm_windows_web_app" "web-app":
│ 12: virtual_network_subnet_id = each.value.subnet != "" ? data.azurerm_subnet.vnet-subnets[each.value.subnet].id : null
│ ├────────────────
│ │ data.azurerm_subnet.vnet-subnets is object with 9 attributes
│ │ each.value.subnet is null
│
│ Can't use a null value as an indexing key.
2
Answers
You can do this by wrapping
virtual_subnet_id
with thetry
function, add this func foreach.value.subnet
which is of interest to you.This code sets virtual_network_subnet_id to
null
for each.value.subnet or an empty string, to avoid the invalid index error, which is quite common.See docs for the
try function
in terrafromYou can add this below that should solve the error:
Although there is a warning
We could use this instead for the sake of not over using
try
You can use a conditional expression or a try() function in this case, I will explain both scenario’s below. However, it would have been much easier if the subnet variable you supply never contains an empty string. That way it is either null or contains a valid string. You can add a validation rule to check for the existence of empty strings in the subnet attribute.
Using the try() function
The most elegant solution can be achieved by using the try() function. When accessing the data resource with a key that does not exist, the error will be caught and it will fall back to the supplied null value.
Using a conditional expression
One can use a simple conditional expression to check if each.value.subnet is valid. By using an OR expression, one can check whether
each.value.subnet
contains either an empty string or a null value.Alternatively, one can use AND to check if the subnet does not contain an empty string or null value.
Using the can() function
The can() function is not very useful in this context, since we work with a variable that can have optional values with a default (e.g. null). When using maps inside a locals block, this function might be useful to check for the existence of an optional attribute.
Other alternatives
Alternatively one can accomplish the same by checking the type() and length() of an attribute.
When accessing an attribute, one can also use the lookup() function. Be aware that each.value.subnet still has to be a valid key. Only the try function catches errors!
The lookup() function can also be combined with the try() function.
The last alternative tries to test whether
each.value.subnet
contains at least one character. The try() function catches the error in case the lenght function tries to parse a null value.