Reputation: 1
I want to automatically mount an Azure File Share to Azure Virtual Desktop using Terraform and PowerShell Script at the provisioning time. Currently, I am using the CustomExtention PowerShell Script to mount the Azure File Share. However, I cannot achieve the goal, the UNC Path of the Azure File Share is not mounted automatically. I must access the Session Host and mount it by cmd or PowerShell. Below is my code.
Terraform Code:
locals {
registration_token = azurerm_virtual_desktop_host_pool_registration_info.registrationinfo.token
shutdown_command = "shutdown -r -t 10"
exit_code_hack = "exit 0"
commandtorun = "New-Item -Path HKLM:/SOFTWARE/Microsoft/RDInfraAgent/AADJPrivate"
powershell_command = "${local.commandtorun}; ${local.shutdown_command}; ${local.exit_code_hack}"
}
resource "azurerm_resource_group" "rg-avd" {
name = var.resource_group_name
location = var.location
}
# Create AVD workspace
resource "azurerm_virtual_desktop_workspace" "workspace" {
name = "AVD-${var.prefix}-Workspace"
resource_group_name = azurerm_resource_group.rg-avd.name
location = azurerm_resource_group.rg-avd.location
friendly_name = "AVD ${var.prefix} Workspace"
description = "AVD ${var.prefix} Workspace"
depends_on = [azurerm_virtual_desktop_host_pool.hostpool, azurerm_virtual_desktop_application_group.desktop_application_group]
}
resource "time_rotating" "avd_token" {
rotation_days = 30
}
# Create AVD host pool
resource "azurerm_virtual_desktop_host_pool" "hostpool" {
resource_group_name = azurerm_resource_group.rg-avd.name
location = azurerm_resource_group.rg-avd.location
name = "${var.prefix}${var.hostpool_name}"
friendly_name = "${var.prefix} Host Pool"
validate_environment = true
custom_rdp_properties = "targetisaadjoined:i:1;drivestoredirect:s:*;audiomode:i:0;videoplaybackmode:i:1;redirectclipboard:i:1;redirectprinters:i:1;devicestoredirect:s:*;redirectcomports:i:1;redirectsmartcards:i:1;usbdevicestoredirect:s:*;enablecredsspsupport:i:1;redirectwebauthn:i:1;use multimon:i:1;enablerdsaadauth:i:1"
description = "${var.prefix} Terraform HostPool"
type = "Pooled"
maximum_sessions_allowed = 10
load_balancer_type = "BreadthFirst" #[BreadthFirst DepthFirst]
depends_on = [
azurerm_resource_group.rg-avd
]
}
resource "azurerm_virtual_desktop_host_pool_registration_info" "registrationinfo" {
hostpool_id = azurerm_virtual_desktop_host_pool.hostpool.id
expiration_date = time_rotating.avd_token.rotation_rfc3339
depends_on = [
azurerm_virtual_desktop_host_pool.hostpool
]
}
# Create AVD DAG
resource "azurerm_virtual_desktop_application_group" "desktop_application_group" {
resource_group_name = azurerm_resource_group.rg-avd.name
host_pool_id = azurerm_virtual_desktop_host_pool.hostpool.id
location = azurerm_resource_group.rg-avd.location
type = "Desktop"
name = "${var.prefix}-desktop-application-group"
friendly_name = "${var.prefix} Desktop AppGroup"
description = "AVD ${var.prefix} Application Group"
depends_on = [azurerm_virtual_desktop_host_pool.hostpool, azurerm_virtual_machine_extension.network_file_share]
}
# Associate Workspace and DAG
resource "azurerm_virtual_desktop_workspace_application_group_association" "workspace-desktop_application_group" {
application_group_id = azurerm_virtual_desktop_application_group.desktop_application_group.id
workspace_id = azurerm_virtual_desktop_workspace.workspace.id
}
resource "azurerm_network_security_group" "nsg" {
name = "avd_rdp_nsg"
location = azurerm_resource_group.rg-avd.location
resource_group_name = azurerm_resource_group.rg-avd.name
security_rule {
name = "allow_rdp_sg"
priority = 900
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "3389"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "allow_azure_file"
priority = 901
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "445"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "allow_https"
priority = 300
direction = "Outbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "allow_http"
priority = 301
direction = "Outbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
# Create Network Interface for virtual machines
resource "azurerm_network_interface" "avd_vm_nic" {
count = var.rdsh_count
name = "AVD-${var.prefix}-${count.index + 1}-nic"
resource_group_name = azurerm_resource_group.rg-avd.name
location = azurerm_resource_group.rg-avd.location
ip_configuration {
name = "nic${count.index + 1}_config"
subnet_id = var.subnet_id
private_ip_address_allocation = "Dynamic"
}
depends_on = [
azurerm_resource_group.rg-avd,
azurerm_virtual_desktop_host_pool_registration_info.registrationinfo
]
}
resource "azurerm_network_interface_security_group_association" "association" {
count = var.rdsh_count
depends_on = [ azurerm_network_security_group.nsg, azurerm_network_interface.avd_vm_nic ]
network_interface_id = azurerm_network_interface.avd_vm_nic.*.id[count.index]
network_security_group_id = azurerm_network_security_group.nsg.id
}
# Create Virtual Machines for AVD
resource "azurerm_windows_virtual_machine" "avd_session_host" {
count = var.rdsh_count
name = "AVD-${var.prefix}-${count.index + 1}"
resource_group_name = azurerm_resource_group.rg-avd.name
location = azurerm_resource_group.rg-avd.location
size = var.vm_size
network_interface_ids = ["${azurerm_network_interface.avd_vm_nic.*.id[count.index]}"]
provision_vm_agent = true
admin_username = var.local_admin_username
admin_password = var.local_admin_password
os_disk {
name = "avd-${lower(var.prefix)}-${count.index + 1}"
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsDesktop"
offer = "Windows-11"
sku = "win11-22h2-avd"
version = "latest"
}
identity {
type = "SystemAssigned"
}
depends_on = [
azurerm_resource_group.rg-avd,
azurerm_network_interface.avd_vm_nic
]
}
# Register VM with Azure Virtual Desktop
resource "azurerm_virtual_machine_extension" "vm_avd_associate" {
depends_on = [
azurerm_windows_virtual_machine.avd_session_host
]
count = var.rdsh_count
name = "Microsoft.PowerShell.DSC"
virtual_machine_id = azurerm_windows_virtual_machine.avd_session_host.*.id[count.index]
publisher = "Microsoft.Powershell"
type = "DSC"
type_handler_version = "2.73"
settings = <<-SETTINGS
{
"modulesUrl": "https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration_01-19-2023.zip",
"ConfigurationFunction": "Configuration.ps1\\AddSessionHost",
"Properties" : {
"hostPoolName" : "${azurerm_virtual_desktop_host_pool.hostpool.name}",
"aadJoin": true
}
}
SETTINGS
protected_settings = <<PROTECTED_SETTINGS
{
"properties": {
"registrationInfoToken": "${local.registration_token}"
}
}
PROTECTED_SETTINGS
}
#Join VM to domain
resource "azurerm_virtual_machine_extension" "AADLoginForWindows" {
depends_on = [
azurerm_windows_virtual_machine.avd_session_host,
azurerm_virtual_machine_extension.vm_avd_associate
]
count = var.rdsh_count
name = "AADLoginForWindows"
virtual_machine_id = azurerm_windows_virtual_machine.avd_session_host.*.id[count.index]
publisher = "Microsoft.Azure.ActiveDirectory"
type = "AADLoginForWindows"
type_handler_version = "1.0"
auto_upgrade_minor_version = true
}
/* resource "azurerm_virtual_machine_extension" "addaadjprivate" {
depends_on = [
azurerm_virtual_machine_extension.AADLoginForWindows
]
count = var.rdsh_count
name = "AADJPRIVATE"
virtual_machine_id = azurerm_windows_virtual_machine.avd_session_host.*.id[count.index]
publisher = "Microsoft.Compute"
type = "CustomScriptExtension"
type_handler_version = "1.10"
settings = <<SETTINGS
{
"commandToExecute": "powershell.exe -Command \"${local.powershell_command}\""
}
SETTINGS
} */
resource "azurerm_virtual_machine_extension" "network_file_share" {
depends_on = [
azurerm_virtual_machine_extension.AADLoginForWindows ]
count = var.rdsh_count
name = "enable-network-discovery"
virtual_machine_id = azurerm_windows_virtual_machine.avd_session_host.*.id[count.index]
publisher = "Microsoft.Compute"
type = "CustomScriptExtension"
type_handler_version = "1.10"
settings = <<SETTINGS
{
"commandToExecute": "powershell -encodedCommand ${textencodebase64(file(var.network_fileshare_config), "UTF-16LE")}"
}
SETTINGS
}
PowerShellScript:
# Set Network Discovery rule
Set-NetFirewallRule -DisplayGroup "Network Discovery" -Enabled True
Get-NetFirewallRule -DisplayGroup 'Network Discovery' | Set-NetFirewallRule -Profile 'Public' -Enabled true
Enable-NetFirewallRule -DisplayGroup "File and Printer Sharing"
Invoke-Expression -Command "net use `"\\avddevfileshare.file.core.windows.net\tm-prog`" /user:`"localhost\avddevfileshare`" `"password`" /persistent:`"yes`" /global"
New-Item -ItemType Directory -Path "C:\tm_prog" | Out-Null
Copy-Item -Path "\\avddevfileshare.file.core.windows.net\tm-prog\*" -Destination "C:\tm_prog" -Recurse -Force
# Create Shortcut
$batchScriptContent = @"
@echo off
net use `"\\avddevfileshare.file.core.windows.net\tm-data`" /user:`"localhost\avddevfileshare`" `"password`" /persistent:`"yes`" /global
echo Set oWS = WScript.CreateObject("WScript.Shell") > CreateShortcut.vbs
echo sLinkFile = "%ALLUSERSPROFILE%\Desktop\tangoMediaCG.lnk" >> CreateShortcut.vbs
echo Set oLink = oWS.CreateShortcut(sLinkFile) >> CreateShortcut.vbs
echo oLink.TargetPath = "C:\tm_prog\prog\win\tangoMediaCG.exe" >> CreateShortcut.vbs
echo oLink.Save >> CreateShortcut.vbs
cscript CreateShortcut.vbs
del CreateShortcut.vbs
"@
# Define the path for the batch script
$batchScriptPath = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\CreateShortcut.bat"
$batchScriptContent | Out-File -FilePath $batchScriptPath -Encoding ASCII
Start-Process -FilePath $batchScriptPath -Verb RunAs -PassThru
$gpPath = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System"
# Set the script execution policy
Set-ItemProperty -Path $gpPath -Name "EnableScriptBlockLogging" -Value 1
Set-ItemProperty -Path $gpPath -Name "EnableScripts" -Value 1
Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy Unrestricted -Force
# Register the script to run at startup
$taskAction = New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c `"$batchScriptPath`""
$taskTrigger = New-ScheduledTaskTrigger -AtLogon
Register-ScheduledTask -Action $taskAction -Trigger $taskTrigger -TaskName "CreateShortcutTask" -User "LocalAdmin" -Password "password" -Force -RunLevel Highest -Force
New-Item -Path HKLM:/SOFTWARE/Microsoft/RDInfraAgent/AADJPrivate; shutdown -r -t 10; exit 0
I tried to edit the PowerShell Script but looks like it does not work. I expect to have the persistent unassigned drive letter mounted UNC Path of the Azure File Share globally that anyone can access it without prompting the credentials.
Upvotes: 0
Views: 457
Reputation: 7820
Mounting Azure File Storage to Azure Virtual Desktop Session Host using Terraform and PowerShell Script.
To mount Azure File Storage
to Azure Virtual Desktop
, you can use the following Terraform code to mount the Azure File share
I created a .ps1 file named 'fileshare.ps1' in the same directory to run the PowerShell script
in the Terraform code, as shown in the screenshot below.
Here is the updated Terraform code.
resource "azurerm_windows_virtual_machine" "avd_vm" {
name = "demo-avd"
resource_group_name = data.azurerm_resource_group.example.name
location = data.azurerm_resource_group.example.location
size = "Standard_DS2_v2"
network_interface_ids = [azurerm_network_interface.nic.id]
provision_vm_agent = true
admin_username = "Venkat"
admin_password = "Welcome@123$"
os_disk {
name = "avd-os"
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsDesktop"
offer = "Windows-10"
sku = "20h2-evd"
version = "latest"
}
}
resource "azurerm_virtual_machine_extension" "example" {
name = "sample-hostname"
virtual_machine_id = azurerm_windows_virtual_machine.avd_vm.id
publisher = "Microsoft.Powershell"
type = "DSC"
type_handler_version = "2.73"
settings = <<SETTINGS
{
"commandToExecute": "powershell fileshare.ps1"
}
SETTINGS
tags = {
environment = "Production"
}
}
Terraform apply
After running the above terraform code, Extension
has been provisioned successfully.
Reference: https://learn.microsoft.com/en-us/azure/developer/terraform/configure-azure-virtual-desktop
Upvotes: 0