Hello,
This post is a continuation of an effort that has a previous post (Completed) that can be found here.
I have attempted to follow the guidance on the posting etiquette via the Guide to asking for help in this forum. It was written right around the same time as my first post
My goal:
To build a Landing Zone for compute workloads. The Landing Zone contains a vnet, subnets, NSG, Route tables, Peering back to the hub, Key Vault, Recovery Service Vault, Compute with ADE, etc.
Thanks to the help from @apparentlymart I was able to accomplish this using a variable map object that includes specifics on the workloads that will use in the Landing Zone.
This post is about the next steps of the effort. I would like to include the provisioning of Azure Key Vaults based on bool variable type. I have added this into the code shown below
Using the following structure I am able to use for_each to deploy resources groups and resources based on the workload I wish to deploy. Except for azurerm_virtual_machine_extension because I am running into an issue using map keys from the for_each build of azurerm_key_vault in the settings - (Optional) The settings passed to the extension, these are specified as a JSON object in a string.
for each workload where workload_kv in var.workloads is set to true
In my variables.tf:
#####------[Workloads]------#####
variable "workloads" {
type = map(object({
workload_name = string
workload_tags = map(string)
workload_subnet_address_prefix = list(string)
vms = map(object({
hostname = string
ip_address = string
vm_size = string
}))
workload_kv = bool
lb_frontend_private_ip_address = string
lb_backend_vms = map(object({
hostname = string
}))
}))
default = {
}
}
Reaching the VM values during a for_each required an inverted variable map to be created in the locals section.
In my locals.tf:
locals {
####-----[Workload VMs]-----####
workload_vms = merge([
for wlk, wl in var.workloads : tomap({
for vmk, vm in wl.vms : "${wlk}:${vmk}" => {
vms = vm
hostname = vm.hostname
ip_address = vm.ip_address
vm_size = vm.vm_size #workload = wl; vm_key = vmk
workload_key = wlk
workload_name = wl.workload_name
workload_tags = wl.workload_tags
workload_subnet_address_prefix = wl.workload_subnet_address_prefix
workload_kv = wl.workload_kv
lb_frontend_private_ip_address = wl.lb_frontend_private_ip_address
}
})
]...)
}
Azure Key Vault built from for_each
#####------[Key Vault Vault]------#####
resource "azurerm_key_vault" "workload" {
provider = azurerm.target_subcription
for_each = { for i, item in var.workloads : i => item if item.workload_kv == true }
name = var.name_string_randomizer ? replace("${local.name_prefix_landing_zone_short}", "${var.landing_zone}", "${each.value.workload_name}${random_string.randomizer.id}kv") : replace("${local.name_prefix_landing_zone_short}", "${var.landing_zone}", "${each.value.workload_name}kv")
resource_group_name = one([for item in azurerm_resource_group.workload : item.name if can(regex("${each.value.workload_name}", item.name))])
location = one([for item in azurerm_resource_group.workload : item.location if can(regex("${each.value.workload_name}", item.name))])
tenant_id = data.azurerm_client_config.context.tenant_id
enabled_for_deployment = true
enabled_for_disk_encryption = true
enabled_for_template_deployment = true
purge_protection_enabled = false
soft_delete_retention_days = 7
sku_name = "standard"
network_acls {
bypass = "AzureServices"
default_action = "Allow"
}
tags = merge(each.value.workload_tags, local.calculated_tags, local.function_kv)
lifecycle {
prevent_destroy = true
}
}
I have tried this:
###----[Enable VM Extension - AzureDiskEncryption]----###
resource "azurerm_virtual_machine_extension" "workload_ade" {
provider = azurerm.target_subcription
for_each = local.workload_vms
name = "AzureDiskEncryption"
publisher = "Microsoft.Azure.Security"
type = "AzureDiskEncryption"
type_handler_version = "2.2"
auto_upgrade_minor_version = false
virtual_machine_id = azurerm_windows_virtual_machine.workload[each.key].id
settings = <<SETTINGS
{
"EncryptionOperation": "EnableEncryption",
"KeyEncryptionAlgorithm": "RSA-OAEP",
"KeyVaultURL": "${azurerm_key_vault.workload[each.key.workload_key].vault_uri}",
"KeyVaultResourceId": "${azurerm_key_vault.workload[each.key.workload_key].id}",
"VolumeType": "All"
}
SETTINGS
tags = merge(each.value.workload_tags, local.calculated_tags, local.function_vm_extension)
depends_on = [
azurerm_network_interface_security_group_association.workload,
azurerm_network_interface_application_security_group_association.workload
]
lifecycle {
prevent_destroy = true
}
}
The terraform code is currently being run in an Azure DevOps Pipeline and I am seeing the following error:
Starting: Validate
==============================================================================
Task : Terraform
Description : Execute terraform commands to manage resources on AzureRM, Amazon Web Services(AWS) and Google Cloud Platform(GCP)
Version : 3.209.19
Author : Microsoft Corporation
Help : [Learn more about this task](https://aka.ms/AAf0uqr)
==============================================================================
C:\agent\_work\_tool\terraform\1.3.7\x64\terraform.exe validate
β·
β Error: Unsupported attribute
β
β on ..\modules\compute-landing-zone\c8-virtual-machines-workload.tf line 156, in resource "azurerm_virtual_machine_extension" "workload_ade":
β 156: "KeyVaultURL": "${azurerm_key_vault.workload[each.key.workload_key].vault_uri}",
β βββββββββββββββββ
β β each.key is a string
β
β Can't access attributes on a primitive-typed value (string).
β΅
β·
β Error: Unsupported attribute
β
β on ..\modules\compute-landing-zone\c8-virtual-machines-workload.tf line 157, in resource "azurerm_virtual_machine_extension" "workload_ade":
β 157: "KeyVaultResourceId": "${azurerm_key_vault.workload[each.key.workload_key].id}",
β βββββββββββββββββ
β β each.key is a string
β
β Can't access attributes on a primitive-typed value (string).
β΅
##[error]Error: The process 'C:\agent\_work\_tool\terraform\1.3.7\x64\terraform.exe' failed with exit code 1
Finishing: Validate
Based on how the βSETTINGSβ is being used the error makes sense to me because SETTINGS
is the delimiter/identifier for the βheredocβ style string literal from what I have read so far.
I also tried using the jsonencode fucntion:
###----[Enable VM Extension - AzureDiskEncryption]----###
resource "azurerm_virtual_machine_extension" "workload_ade" {
provider = azurerm.target_subcription
for_each = local.workload_vms
name = "AzureDiskEncryption"
publisher = "Microsoft.Azure.Security"
type = "AzureDiskEncryption"
type_handler_version = "2.2"
auto_upgrade_minor_version = false
virtual_machine_id = azurerm_windows_virtual_machine.workload[each.key].id
settings = jsonencode({
EncryptionOperation = "EnableEncryption"
KeyEncryptionAlgorithm = "RSA-OAEP"
KeyVaultURL = azurerm_key_vault.workload[each.key.workload_key].vault_uri
KeyVaultResourceId = azurerm_key_vault.workload[each.key.workload_key].id
VolumeType = "All"
})
tags = merge(each.value.workload_tags, local.calculated_tags, local.function_vm_extension)
depends_on = [
azurerm_network_interface_security_group_association.workload,
azurerm_network_interface_application_security_group_association.workload
]
lifecycle {
prevent_destroy = true
}
}
That results in the same error from what I can tell
Starting: Validate
==============================================================================
Task : Terraform
Description : Execute terraform commands to manage resources on AzureRM, Amazon Web Services(AWS) and Google Cloud Platform(GCP)
Version : 3.209.19
Author : Microsoft Corporation
Help : [Learn more about this task](https://aka.ms/AAf0uqr)
==============================================================================
C:\agent\_work\_tool\terraform\1.3.7\x64\terraform.exe validate
β·
β Error: Unsupported attribute
β
β on ..\modules\compute-landing-zone\c8-virtual-machines-workload.tf line 155, in resource "azurerm_virtual_machine_extension" "workload_ade":
β 155: KeyVaultURL = azurerm_key_vault.workload[each.key.workload_key].vault_uri
β βββββββββββββββββ
β β each.key is a string
β
β Can't access attributes on a primitive-typed value (string).
β΅
β·
β Error: Unsupported attribute
β
β on ..\modules\compute-landing-zone\c8-virtual-machines-workload.tf line 156, in resource "azurerm_virtual_machine_extension" "workload_ade":
β 156: KeyVaultResourceId = azurerm_key_vault.workload[each.key.workload_key].id
β βββββββββββββββββ
β β each.key is a string
β
β Can't access attributes on a primitive-typed value (string).
β΅
##[error]Error: The process 'C:\agent\_work\_tool\terraform\1.3.7\x64\terraform.exe' failed with exit code 1
Finishing: Validate
Using other code like PowerShell I can create variables on the fly that could be used in a situation such as this where a property in a for_each can be converted into a useable string. I have been looking for a way to do this in Terraform but have not been able to find one.
In Summary I am trying to create Azure Key Vault per workload using for_each and then point Azure Virtual Machine extensions to the workload appropriate Key Vault for that workload.
How can I take a key or value map from a for_each build and store it in a string that can be used in a βSETTINGSβ block?
I greatly appreciate any help or guidance on this anyone can provide.