I’m trying out Terraform, and I’m trying to setup an environment from scratch to play around with it.
I’ve seen some ideas around using modules, so I’m trying to modulise both my vnet and snets, based on my tfvars file.
I’ve hit a snag and wondered if someone could either direct me to where I can get the answer, or point out the likely obvious mistake I’ve made.
My modules look like this:
Modules:
vnet main.tf:
resource “azurerm_virtual_network” “vnet” {
name = var.name
location = var.location
resource_group_name = var.resource_group_name
address_space = var.address_space
dynamic “subnet” {
for_each = toset(var.subnets)
content {
name = subnet.value.name
address_prefix = subnet.value.address_prefix
security_group = subnet.value.security_group
}
}
}
snet main.tf:
resource “azurerm_subnet” “subnet” {
for_each = var.subnets
name = each.value.name
resource_group_name = var.resource_group_name
virtual_network_name = var.virtual_network_name
address_prefixes = each.value.address_prefixes
service_endpoints = each.value.service_endpoints
dynamic “delegation” {
for_each = toset(each.value.delegation)
content {
name = delegation.value.name
service_delegation {
name = delegation.value.service_delegation
actions = delegation.value.service_delegation_actions
}
}
}
}
Main.tf
And the main.tf where I use the modules looks like:
module “vnet_uks” {
source = “./modules/vnet”
name = var.vnetuksname
location = var.vnetukslocation
resource_group_name = var.vnetuksrg
address_space = var.uksaddress_space
subnets = var.subnets
}
module “subnets” {
source = “./modules/subnets”
subnets = var.subnets
resource_group_name = var.resource_group_name
virtual_network_name = module.vnet_uks.name
depends_on = [ module.vnet_uks ]
}
and the tfvars file section for this looks like:
#Virtual Networks and Subnets for UKS
vnetukslocation = “uksouth”
vnetuksrg = “rg-uks-compute”
uksaddress_space = [“10.0.0.0/16”, “10.1.0.0/24”]
vnetuksname = “vnet-uks-01”
subnets = {
uks_vms = {name = “snet_uks_vms”, address_prefix = [“10.1.1.0/24”], enforce_private_link_endpoint_policies = false, delegations = , service_endpoints = [“Microsoft.AzureActiveDirectory”, “Microsoft.KeyVault”, “Microsoft.Sql”, “Microsoft.Storage”, “Microsoft.Web”] }
}
When I run a tfplan, I get the following error:
Error: Invalid function argument
on modules\vnet\main.tf line 9, in resource “azurerm_virtual_network” “vnet”:
9: for_each = toset(var.subnets)
├────────────────
│ while calling toset(v)
│ var.subnets is object with 5 attributes`
Invalid value for “v” parameter: cannot convert object to set of any single type.
I’m not sure what else to do - I’ve tried replacing the toset with tomap, but that wants every variable declaring separately.
I'm trying to create this so that the subnets will be added to a virtual network each time an environment is created - I've got about 3-5 of these to create, so I'm trying to automate it so that I don't have to manually add each subnet and vnet into the main.tf separately.
Could someone help point me in the right direction? Or a cleaner way of doing this?
Thanks
Your issue arises from the way you're trying to iterate over the
subnets
object. Thetoset()
function is for converting lists to sets, andvar.subnets
is an object (map). We should usefor_each
directly with maps. However, since you're using nested map structures, we need to make some adjustments in Vnet, Subnet and tfvars files.My file structure:
main.tf:
variables.tf:
terraform.tfvars:
modules/subnets/main.tf:
modules/vnet/main.tf:
modules/vnet/variable.tf:
modules/subnets/variable.tf:
Output: