Terraform: unable to use a module subnet in a module Virtual Machine Scale Set

96 views Asked by At

Thanks to some help, I've now managed to modulise both vnet and subnet deployment using terraform.

I've added in a few items since, but at the moment I'm working on trying to deploy a bunch of Virtual Machine Scale Sets (One Linux + One Windows x 3 regions = 6 VMSS in total).

I've tried deploying one so far, but I can't seem to set the ipconfig_subnet. I could hardcode the entire subnet in, but I want several people to use this on difference subscriptions - so that won't help me. I wanted to try import the subnet into the module for VMSS, but I seem to be hitting a brick wall.

Here's what I have so far:

```
**main.tf**
```

module "azure_vnet" {
source              = "./modules/vnet"
name                = var.vnet_name
location            = var.location
resource_group_name = var.vnet_rg 
vnet_address_space =  var.vnet_address_space
subnets             = var.subnets
}

module "azure_subnets" {
source              = "./modules/subnets"
subnets             = var.subnets
resource_group_name = var.vnet_rg 
virtual_network_name = module.azure_vnet.vnet_name
depends_on          = [ module.azure_vnet ]
}

module "vmss_linux" {
source = "./modules/vmss-linux"
name      = format("vmss-%s-lin-uks",var.vmss_lin_uks_name)
resource_group_name  = var.vmss_lin_uks_rgs
location = var.vmss_lin_uks_location
network_interface_name = var.vmss_lin_uks_network_interface_name
network_interface_primary = true
ipconfig_name = var.vmss_lin_uks_ipconfig_name
ipconfig_primary = true
ipconfig_subnet = azurerm_subnet.subnet.name == "sm_uks_vms"
depends_on = [ module.azure_vnet, module.azure_subnets ]
}
```


```
**variable.tfvars**
```
```
#Virtual Networks and Subnets for UKS
location = "uksouth"
vnet_rg = "rg-uks-compute"
vnet_address_space = ["10.0.0.0/16", "10.1.0.0/24"]
vnet_name = "vnet-uks-01"

subnets = {
    uks_vms = {
    name = "sm_uks_vms",
    address_prefix = ["10.0.2.0/24"], 
service_endpoints = ["Microsoft.Containerinstance/containerGroups", 
"Microsoft.KeyVault", "Microsoft.Sql", "Microsoft.Storage", 
"Microsoft.Web"]
}
  uks_storage = {
    name = "snet_uks_storage", 
    address_prefix = ["10.1.2.0/24"], 
service_endpoints = ["Microsoft.AzureActiveDirectory", 
"Microsoft.Sql", "Microsoft.Storage", "Microsoft.Web"] 
},
uks_db      = {
    name = "snet_uks_db", 
    address_prefix =["10.1.3.0/24"], 
service_endpoints = ["Microsoft.Sql", "Microsoft.Storage"] 
},
uks_functions = {
    name = "snet_uks_functions", 
    address_prefix = ["10.1.4.0/24"],
service_endpoints = ["Microsoft.AzureActiveDirectory", 
"Microsoft.Storage", "Microsoft.Web"] 
},
uks_projects  = {
    name = "snet_uks_projects", 
    address_prefix = ["10.1.5.0/24"], 
service_endpoints = ["Microsoft.AzureActiveDirectory", 
"Microsoft.Storage", "Microsoft.Web"] 
},
}
vmss_lin_uks_name = "projects"
vmss_lin_uks_rgs = "rg-uks-compute"
vmss_lin_uks_location = "uksouth"
vmss_lin_uks_network_interface_name = "vmss_lin_uks_ni_01"
vmss_lin_uks_ipconfig_name = "vmss_lin_ipconfig_uks_01"
#vmss_lin_uks_ipconfig_subnet = "snet_uks_projects"
```


```
**virtual network 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.vnet_address_space

    dynamic "subnet" {
        for_each = var.subnets
        content {
            name                 = subnet.value.name
            address_prefix       = subnet.value.address_prefix[0] 
            #security_group       = subnet.value.security_group
        }
    }
}
output "vnet_name" {
value       = azurerm_virtual_network.vnet.name
}
output "snet_name" {
value = azurerm_virtual_network.subnet.name
}
```


```
**vnet - variables.tf**
```
```
variable "name" {
description = "The name of the virtual network."
type        = string
}

variable "location" {
description = "The Azure Region in which the virtual network should exist."
type        = string
}

variable "resource_group_name" {
description = "The name of the resource group in which the virtual network should be created."
type        = string
}

variable "vnet_address_space" {

type        = list(string)
}

variable "subnets" {
description = "The configurations for the subnets to be created within this virtual network."
type        = map(any)
}
```
```
**vnet - output.tf**
```
```
output "name" {
value = azurerm_virtual_network.vnet.name
}

output "subnet" {
value = azurerm_virtual_network.subnet.name  
}

output "id" {
value = azurerm_virtual_network.vnet.id
}
```


``` 
**subnet module**
```
```
**subnet - 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_prefix 
service_endpoints    = each.value.service_endpoints 
}
output "snet_name" {
value = azurerm_subnet.subnet.name
}
```
```
**subnet - variable.tf**
```
```
variable "subnets" {
description = "The configurations for the subnets to be created."
type        = map(any)
}

variable "resource_group_name" {
description = "The name of the resource group in which the subnets should be created."
type        = string
}

variable "virtual_network_name" {
description = "The name of the virtual network in which the subnets should be created."
type        = string
}
```
```
**subnet - output.tf**
```
output "data" {
value = tomap({for s, id in azurerm_subnet.subnet: s =>id })
}

```
**VMSS module**
```
```
**main.tf**
```
```
resource "azurerm_linux_virtual_machine_scale_set" "vmss_linux" {
name                             = var.name
resource_group_name              = var.resource_group_name
location                         = var.location
sku                              = var.sku
instances                        = var.instances
admin_username                   = "vmss_admin"
admin_password                   = "Pa55w0rd9g9g" #Bad practice - look to randomise or redact?
disable_password_authentication  = false

source_image_reference {
publisher  = "Canonical"
offer      = "UbuntuServer"
sku        = "22.04-LTS"
version    = "latest"
}

network_interface {
name     = var.network_interface_name
primary  = var.network_interface_primary

ip_configuration {
  name       = var.ipconfig_name
  primary    = var.ipconfig_primary
  subnet_id  = var.ipconfig_subnet
  }
}

os_disk {
storage_account_type  = "Standard_LRS"
caching               = "ReadWrite"
}
data_disk {
storage_account_type = "Standard_LRS"
caching              = "ReadWrite"
disk_size_gb         = 10
lun                  = 10
}

}
```
```
**variable.tf**
```
```
variable "name" {}
variable "resource_group_name" {}
variable "location" {}
variable "sku" {
default = "Standard_B1s"
}
variable "instances" {
default = 3
}



variable "network_interface_name" {}
variable "network_interface_primary" {}

variable "ipconfig_name" {}
variable "ipconfig_primary" {}
variable "ipconfig_subnet" {}
```

When I try to deploy it, I get the following error message:

```
Error: Reference to undeclared resource

on modules\vnet\main.tf line 20, in output "snet_name":
20:   value = azurerm_virtual_network.subnet.name

A managed resource "azurerm_virtual_network" "subnet" has not been declared in module.azure_vnet.

Error: Reference to undeclared resource

on modules\vnet\output.tf line 6, in output "subnet":
6: value = azurerm_virtual_network.subnet.name

A managed resource "azurerm_virtual_network" "subnet" has not been declared in module.azure_vnet.
```

I have tried changing the main.tf to:

```
module.azure_subnets.subnets.name - returns "azure_subnets is an object"
module.azure_vnet.subnets.name - returns "azure_vnet is an object"
azurerm_virtual_network.vnet.subnet - returns "azurerm_virtual_network is an object"
azurerm_subnet.subnet.name - returns "azurerm_subnet is an object"
```

Any help would be greatly appreciated. Thanks!

1

There are 1 answers

0
theangrytech On

I've managed to get this working for now - message as it may be.

I've changed the main.tf where it calls on module "vmss_linux" to the following:

ipconfig_subnet = "${data.azurerm_subscription.current.id}/resourceGroups/${var.vmss_lin_uks_rgs}/providers/Microsoft.Network/virtualNetworks/${var.vmss_lin_uks_ipconfig_name}/subnets/${var.vmss_lin_uks_ipconfig_subnet}"

Where it's called on a variable, they're set in the variables.tfvar file, and currently provide the subnetID that matches the 10 segement's it's looking for. It also allows other users to re-use my TF code, which is what I'm aiming for.

If anyone has a cleaner way, feel free to comment with it and I'll give it a go later on.