Dynamic Conditional Block - Missing required argument

I’ve got a custom module that I have created to build an Azure VM using azurerm_windows_virtual_machine and want to have a dynamic donditional block depending on if var.vm_image_custom_id is supplied.

If var.vm_image_custom_id is supplied, I want it to use ‘source_image_id’ if it isn’t I want to use a block ‘source_image_reference’ which is populated with publisher, offer etc.

Here is the code;

resource "azurerm_windows_virtual_machine" "main" {
  tags                     = merge(var.tags)
  enable_automatic_updates = "false"
  name                     = upper("${var.vm_name}")
  resource_group_name      = data.azurerm_resource_group.main.name
  location                 = data.azurerm_resource_group.main.location
  license_type             = var.license_type
  size                     = var.vm_size
  admin_username           = var.vm_username
  admin_password           = var.vm_password
  patch_mode               = "Manual"
  zone                     = var.vm_availability_zone
  network_interface_ids = [
    azurerm_network_interface.main.id,
  ]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = var.vm_os_disk_type
  }

  source_image_id = var.vm_image_custom_id != null ? var.vm_image_custom_id : null

  dynamic "source_image_reference" {
    for_each = var.vm_image_custom_id == null ? toset([1]) : toset([])
    content {
      publisher  = var.vm_image_publisher 
      offer      = var.vm_image_offer 
      sku        = var.vm_image_sku 
      version    = var.vm_image_version
    }

}

It works if I supply an var.vm_image_custom_id but doesn’t if I don’t, this is the error I get;

It think my syntax is wrong but I can’t get my head around it :frowning:

Hi,

I noticed you are using toset() function in your for_each:

I think this might be the reason for your error, could you test it this way?

for_each = var.vm_image_custom_id == null ? [1] : []

Daniel

Yeah I tried that before using toSet but got the same error.

Does the syntax look right to you?

Yeah, it does.

Could you share here the four variables block you have in that dynamic block?

Yes of course, this is the variables.tf for the custom module

variable "vm_image_custom_id" {
  description = "Specify a custom image instead of a market place image (Optional)"
  default = null
}

variable "vm_image_publisher" {
  description = "Market Place - The image publisher of the Virtual machine e.g. MicrosoftWindowsServer"
  default = null
}

variable "vm_image_offer" {
  description = "Market Place - The image offer of the Virtual machine e.g. WindowsServer"
  default = null
}

variable "vm_image_sku" {
  description = "Market Place - The image sku of the Virtual machine e.g. 2019-Datacenter"
  default = null
}

Then I am calling the module from main.tf

module "corporate-servers" {
  source                        = "../../modules/tf_azurerm_virtual_machine"
  for_each                      = var.corporate-servers
  rg_name                       = var.rg_name
  vm_name                       = each.value["vm_name"]
  license_type                  = var.license_type
  data_subnet_name              = each.value["data_subnet_name"]
  data_vnet                     = var.data_vnet
  data_vnet_rg                  = var.data_vnet_rg
  vm_size                       = each.value["vm_size"]
  vm_password                   = var.vm_admin_password
  vm_availability_zone          = each.value["vm_availability_zone"]
  vm_image_publisher            = var.vm_image_publisher
  vm_image_offer                = var.vm_image_offer
  vm_image_sku                  = var.vm_image_sku
  vm_image_custom_id            = each.value["vm_image_custom_id"]
  enable_accelerated_networking = var.enable_accelerated_networking
  vm_require_data_disk          = each.value["vm_require_data_disk"]
  vm_data_disk_size             = each.value["vm_data_disk_size"]
  vm_ou_path                    = var.vm_ou_path
  vm_add_to_domain_password     = var.vm_domain_password
  tags                          = var.common_tags

  depends_on = [
    azurerm_resource_group.main,
  ]
  
}

This is the terrafrom.tfvars

rg_name = "rg-corp-servers"

corporate-servers = {
  SVR1 = {
    vm_name                          = "SVR1"
    data_subnet_name                 = "subnet-common"
    vm_availability_zone             = "1"
    vm_size                          = "Standard_B2ms"
    vm_require_data_disk             = false
    vm_data_disk_size                = null
    vm_image_custom_id               = "/subscriptions/xxxxsubscriptionidxxxx/resourceGroups/rg-image/providers/Microsoft.Compute/images/live-server-image"
  }
  
  SVR2 = {
      vm_name                          = "SVR2"
      data_subnet_name                 = "subnet-common"
      vm_availability_zone             = "1"
      vm_size                          = "Standard_D4s_v3"
      vm_require_data_disk             = true
      vm_data_disk_size                = "1024"
      vm_image_publisher               = "MicrosoftWindowsServer" 
      vm_image_offer                   = "WindowsServer"
      vm_image_sku                     =  "2019-Datacenter"
      vm_image_custom_id               = null
    } 

}

Thanks again for your help with this!

Don’t you have variables.tf for root level in your code?