How to use dynamic block?

How can I use dynamic block storage_image_reference in azurerm_virtual_machine resource?
local.image will provide { "id" = ".." } or { "publisher" = "..", "offer" = ".." ,"sku" = ".." version" = ".."} map depends on input variables.
Now I want to use it in single resource-block to be able to se select custom image(referenced with id var) or azure image (referenced with publish,offer,sku,version vars)

variable "custom_image_name" {
  type = string
  default = ""
}
variable "custom_image_rg" {
  type = string
  default = ""
}

variable "azure_image_urn" {
  type = string
  default = "Canonical:UbuntuServer:18.04-LTS:latest"
}

data "azurerm_image" "image" {
  name                = var.custom_image_name
  resource_group_name = var.custom_image_rg
  count = var.custom_image_name != "" && var.custom_image_rg != "" ? 1 : 0
}

locals {
  image = var.custom_image_name != "" && var.custom_image_rg != "" ? zipmap(["id"], [data.azurerm_image.image[0].id]) : zipmap(["publisher","offer","sku","version"],split(":",var.azure_image_urn))
}


resource "azurerm_virtual_machine" "vm" {
  ...
  storage_image_reference {   
    for i in local.image
      i.key => i.value
   }
}

Thanks

Hello Lukas,

storage_image_reference is a nested block with required attributes. As a result, the required attribute labels must be declared explicitly.

resource "azurerm_virtual_machine" "vm" {
  ...
  storage_image_reference {   
    offer = local.image.offer
    sku = local.image.sku
    publisher = local.image.publisher
   }
}

I don’t know if there are plans to support iterating over an arbitrary list of attributes and setting their values in the future but it’s something to consider!

Hello,
Oh, bad news…
Duplicating resource/module just for switching image… 99% of code will be same.

What about this?

resource "azurerm_virtual_machine" "vm" {
  ...
  storage_image_reference {   
    offer = var.custom_image_name == "" && var.custom_image_rg == "" 
 ? local.offer : null
    sku = var.custom_image_name == "" && var.custom_image_rg == "" 
 ? local.sku: null
    publisher = var.custom_image_name == "" && var.custom_image_rg == "" 
 ? local.publisher : null
   version = var.custom_image_name == "" && var.custom_image_rg == "" 
 ? local.version : null
   
    id = var.custom_image_name != "" && var.custom_image_rg != "" 
 ? data.azurerm_image.image[0].id : null

   }
}

Will it work?
Thanks

Hello @lukasmrtvy,

Did you figure this out? I have a very similar need. I am investigating using dynamic blocks so I can specify whether I am using my custom image or one from the marketplace (https://www.terraform.io/docs/configuration/expressions.html#dynamic-blocks)

Thanks

Writing the configuration so that the argument is null in situations where it shouldn’t be set is indeed the intended approach here. Setting a resource argument value to null is equivalent to not setting it at all, so you can use that to dynamically decide (based on arbitrary expressions) whether the argument should be set.

In recent versions of Terraform (new since this topic was originally opened) there is a new function try which can help write more concisely this sort of projection from a data structure where attributes might not always be present:

  storage_image_reference {
    offer     = try(local.image.offer, null)
    sku       = try(local.image.sku, null)
    publisher = try(local.image.publisher, null)
  }

Because the expression null can never fail to evaluate, the above concisely handles the situation where one of the attributes isn’t set on local.image (which would normally be an error), using null as the result instead.

If local.image were instead local.images, a list of objects, then the fully-general form of this accepting an arbitrary number of image objects would be like this:

  dynamic "storage_image_reference" {
    for_each = local.images
    content {
      offer     = try(storage_image_reference.value.offer, null)
      sku       = try(storage_image_reference.value.sku, null)
      publisher = try(storage_image_reference.value.publisher, null)
    }
  }