Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions quickstart/101-azure-virtual-desktop-azapi/afstorage.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Create a Resource Group for Storage
resource "azurerm_resource_group" "rg_storage" {
location = var.deploy_location
name = var.rg_stor
}

# Generate a random string (consisting of four characters)
resource "random_string" "random" {
length = 4
upper = false
special = false
}

# Create a File Storage Account using AzAPI
resource "azapi_resource" "storage" {
type = "Microsoft.Storage/storageAccounts@2023-05-01"
name = "stor${random_string.random.id}"
location = azurerm_resource_group.rg_storage.location
parent_id = azurerm_resource_group.rg_storage.id

body = {
kind = "FileStorage"
sku = {
name = "Premium_LRS"
}
properties = {}
}

response_export_values = ["properties", "id"]

# Storage API returns many defaulted properties
lifecycle {
ignore_changes = [body]
}
}

# Create File Share using AzAPI
resource "azapi_resource" "fs_share" {
type = "Microsoft.Storage/storageAccounts/fileServices/shares@2023-05-01"
name = "fslogix"
parent_id = "${azapi_resource.storage.id}/fileServices/default"

body = {
properties = {}
}
}

# Azure built-in roles - keep as azurerm
data "azurerm_role_definition" "storage_role" {
name = "Storage File Data SMB Share Contributor"
}

resource "azurerm_role_assignment" "af_role" {
count = var.enable_ad_integration ? 1 : 0
scope = azapi_resource.storage.id
role_definition_id = data.azurerm_role_definition.storage_role.id
principal_id = azuread_group.aad_group[0].id
}
190 changes: 190 additions & 0 deletions quickstart/101-azure-virtual-desktop-azapi/host.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
locals {
registration_token = azurerm_virtual_desktop_host_pool_registration_info.registrationinfo.token
}

resource "random_string" "AVD_local_password" {
count = var.rdsh_count
length = 16
special = true
min_special = 2
override_special = "*!@#?"
}

resource "azurerm_resource_group" "rg" {
name = var.rg
location = var.resource_group_location
}

# Create Network Interfaces using AzAPI
resource "azapi_resource" "avd_vm_nic" {
count = var.rdsh_count
type = "Microsoft.Network/networkInterfaces@2024-01-01"
name = "${var.prefix}-${count.index + 1}-nic"
location = azurerm_resource_group.rg.location
parent_id = azurerm_resource_group.rg.id

body = {
properties = {
ipConfigurations = [
{
name = "nic${count.index + 1}_config"
properties = {
privateIPAllocationMethod = "Dynamic"
subnet = {
id = azapi_resource.vnet.output.properties.subnets[0].id
}
}
}
]
}
}

# Retry on transient delete errors (Azure holds NIC for 180s after VM deletion)
retry = {
error_message_regex = ["NicReservedForAnotherVm", "InUseSubnetCannotBeDeleted", "OperationNotAllowed"]
interval_seconds = 30
max_interval_seconds = 180
}

# Azure returns allocated IP and additional defaulted fields
lifecycle {
ignore_changes = [body]
}

# NICs must be destroyed before NSG association is removed
depends_on = [azapi_update_resource.nsg_assoc]
}

# Create Windows Virtual Machines using AzAPI
resource "azapi_resource" "avd_vm" {
count = var.rdsh_count
type = "Microsoft.Compute/virtualMachines@2024-03-01"
name = "${var.prefix}-${count.index + 1}"
location = azurerm_resource_group.rg.location
parent_id = azurerm_resource_group.rg.id

body = {
properties = {
hardwareProfile = {
vmSize = var.vm_size
}
osProfile = {
computerName = "${var.prefix}-${count.index + 1}"
adminUsername = var.local_admin_username
adminPassword = var.local_admin_password
windowsConfiguration = {
provisionVMAgent = true
}
}
storageProfile = {
osDisk = {
name = "${lower(var.prefix)}-${count.index + 1}"
caching = "ReadWrite"
createOption = "FromImage"
managedDisk = {
storageAccountType = "Standard_LRS"
}
}
imageReference = {
publisher = "MicrosoftWindowsDesktop"
offer = "Windows-10"
sku = "win10-22h2-avd"
version = "latest"
}
}
networkProfile = {
networkInterfaces = [
{
id = azapi_resource.avd_vm_nic[count.index].id
}
]
}
}
}

depends_on = [azapi_resource.avd_vm_nic]

retry = {
error_message_regex = ["SkuNotAvailable", "OperationNotAllowed", "AllocationFailed"]
interval_seconds = 60
max_interval_seconds = 300
}

# Write-only adminPassword and Azure-defaulted VM properties
lifecycle {
ignore_changes = [body]
}
}

# Domain Join Extension using AzAPI
resource "azapi_resource" "domain_join" {
count = var.enable_ad_integration ? var.rdsh_count : 0
type = "Microsoft.Compute/virtualMachines/extensions@2024-03-01"
name = "${var.prefix}-${count.index + 1}-domainJoin"
location = azurerm_resource_group.rg.location
parent_id = azapi_resource.avd_vm[count.index].id

body = {
properties = {
publisher = "Microsoft.Compute"
type = "JsonADDomainExtension"
typeHandlerVersion = "1.3"
autoUpgradeMinorVersion = true
settings = {
Name = var.domain_name
OUPath = var.ou_path
User = "${var.domain_user_upn}@${var.domain_name}"
Restart = "true"
Options = "3"
}
protectedSettings = {
Password = var.domain_password
}
}
}

# Write-only protectedSettings not returned by GET
lifecycle {
ignore_changes = [body]
}
}

# DSC Extension for AVD agent using AzAPI
resource "azapi_resource" "vmext_dsc" {
count = var.rdsh_count
type = "Microsoft.Compute/virtualMachines/extensions@2024-03-01"
name = "${var.prefix}${count.index + 1}-avd_dsc"
location = azurerm_resource_group.rg.location
parent_id = azapi_resource.avd_vm[count.index].id

body = {
properties = {
publisher = "Microsoft.Powershell"
type = "DSC"
typeHandlerVersion = "2.73"
autoUpgradeMinorVersion = true
settings = {
modulesUrl = "https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration_1.0.02714.342.zip"
configurationFunction = "Configuration.ps1\\AddSessionHost"
properties = {
HostPoolName = azurerm_virtual_desktop_host_pool.hostpool.name
}
}
protectedSettings = {
properties = {
registrationInfoToken = local.registration_token
}
}
}
}

# Write-only protectedSettings not returned by GET
lifecycle {
ignore_changes = [body]
}

depends_on = [
azapi_resource.avd_vm,
azurerm_virtual_desktop_host_pool.hostpool
]
}
21 changes: 21 additions & 0 deletions quickstart/101-azure-virtual-desktop-azapi/loganalytics.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
resource "azurerm_resource_group" "shared" {
name = var.rg_shared_name
location = var.deploy_location
}

# Creates Log Analytics Workspace using AzAPI
resource "azapi_resource" "law" {
type = "Microsoft.OperationalInsights/workspaces@2023-09-01"
name = "log${random_string.random.id}"
location = azurerm_resource_group.shared.location
parent_id = azurerm_resource_group.shared.id

body = {
properties = {
sku = {
name = "PerGB2018"
}
retentionInDays = 30
}
}
}
77 changes: 77 additions & 0 deletions quickstart/101-azure-virtual-desktop-azapi/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Resource group for AVD service objects
resource "azurerm_resource_group" "sh" {
name = var.rg_name
location = var.resource_group_location
}

# Create AVD workspace using AzAPI
resource "azapi_resource" "workspace" {
type = "Microsoft.DesktopVirtualization/workspaces@2024-04-03"
name = var.workspace
location = azurerm_resource_group.sh.location
parent_id = azurerm_resource_group.sh.id

body = {
properties = {
friendlyName = "${var.prefix} Workspace"
description = "${var.prefix} Workspace"
}
}
}

# Create AVD host pool - keep as azurerm (registration token not easily available via azapi)
resource "azurerm_virtual_desktop_host_pool" "hostpool" {
resource_group_name = azurerm_resource_group.sh.name
location = azurerm_resource_group.sh.location
name = var.hostpool
friendly_name = var.hostpool
validate_environment = true
custom_rdp_properties = "audiocapturemode:i:1;audiomode:i:0;"
description = "${var.prefix} Terraform HostPool"
type = "Pooled"
maximum_sessions_allowed = 16
load_balancer_type = "DepthFirst"
}

resource "azurerm_virtual_desktop_host_pool_registration_info" "registrationinfo" {
hostpool_id = azurerm_virtual_desktop_host_pool.hostpool.id
expiration_date = coalesce(var.rfc3339, timeadd(timestamp(), "23h"))
}

# Create AVD Application Group using AzAPI
resource "azapi_resource" "dag" {
type = "Microsoft.DesktopVirtualization/applicationGroups@2024-04-03"
name = "${var.prefix}-dag"
location = azurerm_resource_group.sh.location
parent_id = azurerm_resource_group.sh.id

body = {
properties = {
friendlyName = "Desktop AppGroup"
description = "AVD application group"
hostPoolArmPath = azurerm_virtual_desktop_host_pool.hostpool.id
applicationGroupType = "Desktop"
}
}

depends_on = [azurerm_virtual_desktop_host_pool.hostpool, azapi_resource.workspace]
}

# Associate Workspace and DAG - update workspace with application group reference
resource "azapi_update_resource" "ws_dag_association" {
type = "Microsoft.DesktopVirtualization/workspaces@2024-04-03"
name = var.workspace
parent_id = azurerm_resource_group.sh.id

body = {
properties = {
applicationGroupReferences = [azapi_resource.dag.id]
}
}

lifecycle {
ignore_changes = [body]
}

depends_on = [azapi_resource.workspace, azapi_resource.dag]
}
Loading
Loading