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)
}
}