Last Updated on 26/08/2025 by Alex
If you’ve ever had to deal with users logging into an Azure Virtual Desktop session that greets them in the wrong language, you’ll know how frustrating it can be for both them and you. Sorting out language and regional settings across a fleet of session hosts isn’t exactly thrilling, but it’s one of those things that makes a huge difference to the user experience.
In this post I’ll walk you through how to install additional languages and set the default language and regional settings for all users, baked directly into your AVD image using Azure Image Builder. It’s clean, repeatable, and avoids that dreaded “first login weirdness” users love to report. The below examples are for the English (United Kingdom) language and code is in Bicep format (feel free to convert if needed).
Prerequisites
I am going to assume that you have some knowledge of Azure Image Builder and how it works, so I am not going to run through how to step this up, but… if you want a blog post on how to configure AIB, just drop me a message or comment below.
Installing Language Packs
The first step to installing languages into your AIB image, is obviously to install the language packs, for this, I use the custom image template scripts provided by Microsoft here, obviously update the parameter to what language you wish to install, here’s my example for installing English (United Kingdom) language, I’ve added some additional write-outputs and basic error handling too:
{
name: 'FILE_InstallLanguagePacks'
type: 'File'
destination: 'C:\\AIBTemp\\Production\\Configurations\\installLanguagePacks.ps1'
sourceUri: 'https://raw.githubusercontent.com/Azure/RDS-Templates/master/CustomImageTemplateScripts/CustomImageTemplateScripts_2024-03-27/InstallLanguagePacks.ps1'
}
{
name: 'CONF_InstallUKLanguagePack'
type: 'PowerShell'
runAsSystem: true
runElevated: true
inline: [
'$ErrorActionPreference = "Stop"'
'$stepStart = Get-Date'
'Write-Output "STEP STARTED: Installing UK Language Pack at $stepStart (UTC)"'
'& "C:\\AIBTemp\\Production\\Configurations\\installLanguagePacks.ps1" -LanguageList "English (United Kingdom)"'
'$stepEnd = Get-Date'
'$duration = $stepEnd - $stepStart'
'Write-Output "STEP COMPLETED: UK Language Pack installed at $stepEnd (UTC) (Duration: $($duration.ToString()))"'
]
}
BICEPNote: Beware that the above can take some time to install; I’ve seen this range from 20 minutes to 45 minutes. This is “normal”.
Set Default Language Settings for System
Once the language pack has been installed successfully, we need to set the device to use that language as the default. For this, again, I’ll be using an additional step within Azure Image Builder:
{
name: 'CONF_SetSystemLanguage_en-GB'
type: 'PowerShell'
runAsSystem: true
runElevated: true
inline: [
'$ErrorActionPreference = "Stop"'
'$stepStart = Get-Date'
'Write-Output "STEP STARTED: Setting system language to en-GB at $stepStart (UTC)"'
'$LanguageTag = "en-gb"'
'Write-Output "Setting Win User Language List to prioritise: $LanguageTag"'
'$OldList = Get-WinUserLanguageList'
'$UserLanguageList = New-WinUserLanguageList -Language $LanguageTag'
'$UserLanguageList += $OldList | Where-Object { $_.LanguageTag -ne $LanguageTag }'
'Set-WinUserLanguageList -LanguageList $UserLanguageList -Force'
'Write-Output "Setting system preferred UI language to: $LanguageTag"'
'Set-SystemPreferredUILanguage -Language $LanguageTag'
'Write-Output "Setting system locale to: $LanguageTag"'
'Set-WinSystemLocale -SystemLocale $LanguageTag'
'Write-Output "A restart is required to set language configurations"'
'$stepEnd = Get-Date'
'$duration = $stepEnd - $stepStart'
'Write-Output "STEP COMPLETED: System language set to en-GB at $stepEnd (UTC) (Duration: $($duration.ToString()))"'
]
}
BICEPIMPORTANT: Ensure that the above $languagetag variable is set to your desired language.
Set UK Regional Settings and Default Language for Users
Now that we have our English (United Kingdom) language installed and system set to en-GB, we need a way of setting this for all users on the platform, if you’re familiar with these settings, you’ll know that these are user settings, thus installing in the users registry hive, if you’re using AD or Entra Hybrid Join you can of course using group policy to set these regional settings, but what if you’re cloud-native or just wishing to install these settings in the image itself? Introducing the Virtual Desktop Optimisation Tool or VDOT.
VDOT as the name suggests is a script provided by the RDS Team at Microsoft, it optimises your image for use with VDI, disabling particular scheduled tasks, services, removing AppX packages etc. But the item that we’re particularly interested in is that it can apply user-based registry settings, it does this by reading the DefaultUserSettings.Json during the script execution, loads the default registry hive and applies those settings.
So now that we have a way of doing this, let’s use Azure Image Builder, to download VDOT, add the additional UK based regional registry settings, here’s the code to do this:
{
name: 'CONF_RunVDOTOptimisations'
type: 'PowerShell'
runAsSystem: true
runElevated: true
inline: [
'$ErrorActionPreference = "Stop"'
'[Console]::OutputEncoding = [System.Text.Encoding]::UTF8'
'$PSDefaultParameterValues["Out-File:Encoding"] = "UTF8"'
'$stepStart = Get-Date'
'Write-Output "STEP STARTED: Running VDOT Optimization Tool Preparation at $stepStart (UTC)"'
'$vdotZipUrl = "https://github.com/The-Virtual-Desktop-Team/Virtual-Desktop-Optimization-Tool/archive/refs/heads/main.zip"'
'$vdotTempDir = "C:\\AIBTemp\\Production\\Configurations"'
'$vdotZipPath = Join-Path $vdotTempDir "VDOT.zip"'
'$vdotExtractedPath = Join-Path $vdotTempDir "Virtual-Desktop-Optimization-Tool-main"'
'$defaultUserSettingsPath = Join-Path $vdotExtractedPath "2009\\ConfigurationFiles\\DefaultUserSettings.JSON"'
'New-Item -ItemType Directory -Path $vdotTempDir -Force | Out-Null'
'Write-Output "Downloading VDOT zip..."'
'Invoke-WebRequest -Uri $vdotZipUrl -OutFile $vdotZipPath'
'Write-Output "Extracting zip..."'
'Expand-Archive -Path $vdotZipPath -DestinationPath $vdotTempDir -Force'
'Write-Output "Unblocking files..."'
'Get-ChildItem -Path $vdotExtractedPath -Recurse | Unblock-File'
'if (Test-Path $defaultUserSettingsPath) {'
' Write-Output "Appending UK locale settings to DefaultUserSettings.JSON..."'
' $json = Get-Content $defaultUserSettingsPath -Encoding UTF8 | ConvertFrom-Json'
' $appendItems = @('
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International"; KeyName = "Locale"; PropertyType = "STRING"; PropertyValue = "00000809"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International"; KeyName = "LocaleName"; PropertyType = "STRING"; PropertyValue = "en-GB"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International"; KeyName = "sCurrency"; PropertyType = "STRING"; PropertyValue = "\\u00A3"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International"; KeyName = "sShortDate"; PropertyType = "STRING"; PropertyValue = "dd/MM/yyyy"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International\\Geo"; KeyName = "Name"; PropertyType = "STRING"; PropertyValue = "GB"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International\\Geo"; KeyName = "Nation"; PropertyType = "STRING"; PropertyValue = "242"; SetProperty = "True" }'
' )'
' $json += $appendItems'
' $json | ConvertTo-Json -Depth 5 | Out-File -FilePath $defaultUserSettingsPath -Encoding UTF8 -Force'
' Write-Output "Locale entries appended to DefaultUserSettings.JSON."'
'} else {'
' Write-Output "WARNING: DefaultUserSettings.JSON not found at $defaultUserSettingsPath"'
'}'
'$vdotScript = Join-Path $vdotExtractedPath "Windows_VDOT.ps1"'
'if (Test-Path $vdotScript) {'
' Write-Output "Executing Windows_VDOT.ps1 with selected optimizations..."'
' & $vdotScript -Verbose -AcceptEula -Optimizations @('
' "Autologgers"'
' "DefaultUserSettings"'
' "LocalPolicy"'
' "NetworkOptimizations"'
' "ScheduledTasks"'
' "Services"'
' "WindowsMediaPlayer"'
' ) -AdvancedOptimizations @("Edge")'
' Write-Output "VDOT script completed."'
'} else {'
' Write-Output "ERROR: VDOT.ps1 not found at $vdotScript"'
'}'
'$stepEnd = Get-Date'
'$duration = $stepEnd - $stepStart'
'Write-Output "STEP COMPLETED: VDOT optimization finished at $stepEnd (UTC) (Duration: $($duration.ToString()))"'
]
}
BICEPAgain, this is in Bicep format, but pay special attention to lines 25 to 31, these are the registry settings needed to apply the regional settings for users. This is currently for the UK, but you can find your desired language settings references here and here.
IMPORTANT: The above example is applying more than just the UK regional settings via VDOT, it is removing Media Player, disabling services, scheduled tasks, applying recommended policies for optimisation by the VDOT team, if you wish to purely apply the language registry keys, change the -Optimizations to just ‘DefaultUserSettings’ and you may want to consider changing the append in the script to a replace.
I’d highly recommend reviewing the VDOT GitHub readme.md to determine which is best for your requirements.
UPDATE – 26/08/2025 – I noticed that the currency was not set correctly on session hosts following the above guidance, I’ve since updated the code, on line 30 ensure that you’re using the unicode for symbols, in the above example this is set to UK (£) pound symbol.
Putting it all together
Now you have the above individual steps, let’s put it all together, as there are a couple of extra reboots as well, here are the full steps:
{
name: 'CONF_LanguagePackPreInstall_ApplyWindowsUpdates'
type: 'WindowsUpdate'
}
{
name: 'CONF_LanguagePackPreInstall_RestartPreInstall'
type: 'WindowsRestart'
restartTimeout: '20m'
}
{
name: 'FILE_InstallLanguagePacks'
type: 'File'
destination: 'C:\\AIBTemp\\Production\\Configurations\\installLanguagePacks.ps1'
sourceUri: 'https://raw.githubusercontent.com/Azure/RDS-Templates/master/CustomImageTemplateScripts/CustomImageTemplateScripts_2024-03-27/InstallLanguagePacks.ps1'
}
{
name: 'CONF_InstallUKLanguagePack'
type: 'PowerShell'
runAsSystem: true
runElevated: true
inline: [
'$ErrorActionPreference = "Stop"'
'$stepStart = Get-Date'
'Write-Output "STEP STARTED: Installing UK Language Pack at $stepStart (UTC)"'
'& "C:\\AIBTemp\\Production\\Configurations\\installLanguagePacks.ps1" -LanguageList "English (United Kingdom)"'
'$stepEnd = Get-Date'
'$duration = $stepEnd - $stepStart'
'Write-Output "STEP COMPLETED: UK Language Pack installed at $stepEnd (UTC) (Duration: $($duration.ToString()))"'
]
}
{
name: 'CONF_LanguagePackPostInstall_ApplyWindowsUpdates'
type: 'WindowsUpdate'
}
{
name: 'CONF_LanguagePackPostInstall_RestartPostInstall'
type: 'WindowsRestart'
restartTimeout: '20m'
}
{
name: 'CONF_SetSystemLanguage_en-GB'
type: 'PowerShell'
runAsSystem: true
runElevated: true
inline: [
'$ErrorActionPreference = "Stop"'
'$stepStart = Get-Date'
'Write-Output "STEP STARTED: Setting system language to en-GB at $stepStart (UTC)"'
'$LanguageTag = "en-gb"'
'Write-Output "Setting Win User Language List to prioritise: $LanguageTag"'
'$OldList = Get-WinUserLanguageList'
'$UserLanguageList = New-WinUserLanguageList -Language $LanguageTag'
'$UserLanguageList += $OldList | Where-Object { $_.LanguageTag -ne $LanguageTag }'
'Set-WinUserLanguageList -LanguageList $UserLanguageList -Force'
'Write-Output "Setting system preferred UI language to: $LanguageTag"'
'Set-SystemPreferredUILanguage -Language $LanguageTag'
'Write-Output "Setting system locale to: $LanguageTag"'
'Set-WinSystemLocale -SystemLocale $LanguageTag'
'Write-Output "A restart is required to set language configurations"'
'$stepEnd = Get-Date'
'$duration = $stepEnd - $stepStart'
'Write-Output "STEP COMPLETED: System language set to en-GB at $stepEnd (UTC) (Duration: $($duration.ToString()))"'
]
}
{
name: 'REBOOT_SetSystemLanguageUK'
type: 'WindowsRestart'
restartTimeout: '20m'
}
{
name: 'CONF_RunVDOTOptimisations'
type: 'PowerShell'
runAsSystem: true
runElevated: true
inline: [
'$ErrorActionPreference = "Stop"'
'[Console]::OutputEncoding = [System.Text.Encoding]::UTF8'
'$PSDefaultParameterValues["Out-File:Encoding"] = "UTF8"'
'$stepStart = Get-Date'
'Write-Output "STEP STARTED: Running VDOT Optimization Tool Preparation at $stepStart (UTC)"'
'$vdotZipUrl = "https://github.com/The-Virtual-Desktop-Team/Virtual-Desktop-Optimization-Tool/archive/refs/heads/main.zip"'
'$vdotTempDir = "C:\\AIBTemp\\Production\\Configurations"'
'$vdotZipPath = Join-Path $vdotTempDir "VDOT.zip"'
'$vdotExtractedPath = Join-Path $vdotTempDir "Virtual-Desktop-Optimization-Tool-main"'
'$defaultUserSettingsPath = Join-Path $vdotExtractedPath "2009\\ConfigurationFiles\\DefaultUserSettings.JSON"'
'New-Item -ItemType Directory -Path $vdotTempDir -Force | Out-Null'
'Write-Output "Downloading VDOT zip..."'
'Invoke-WebRequest -Uri $vdotZipUrl -OutFile $vdotZipPath'
'Write-Output "Extracting zip..."'
'Expand-Archive -Path $vdotZipPath -DestinationPath $vdotTempDir -Force'
'Write-Output "Unblocking files..."'
'Get-ChildItem -Path $vdotExtractedPath -Recurse | Unblock-File'
'if (Test-Path $defaultUserSettingsPath) {'
' Write-Output "Appending UK locale settings to DefaultUserSettings.JSON..."'
' $json = Get-Content $defaultUserSettingsPath -Encoding UTF8 | ConvertFrom-Json'
' $appendItems = @('
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International"; KeyName = "Locale"; PropertyType = "STRING"; PropertyValue = "00000809"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International"; KeyName = "LocaleName"; PropertyType = "STRING"; PropertyValue = "en-GB"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International"; KeyName = "sCurrency"; PropertyType = "STRING"; PropertyValue = "\\u00A3"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International"; KeyName = "sShortDate"; PropertyType = "STRING"; PropertyValue = "dd/MM/yyyy"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International\\Geo"; KeyName = "Name"; PropertyType = "STRING"; PropertyValue = "GB"; SetProperty = "True" }'
' [ordered]@{ HivePath = "HKLM:\\VDOT_TEMP\\Control Panel\\International\\Geo"; KeyName = "Nation"; PropertyType = "STRING"; PropertyValue = "242"; SetProperty = "True" }'
' )'
' $json += $appendItems'
' $json | ConvertTo-Json -Depth 5 | Out-File -FilePath $defaultUserSettingsPath -Encoding UTF8 -Force'
' Write-Output "Locale entries appended to DefaultUserSettings.JSON."'
'} else {'
' Write-Output "WARNING: DefaultUserSettings.JSON not found at $defaultUserSettingsPath"'
'}'
'$vdotScript = Join-Path $vdotExtractedPath "Windows_VDOT.ps1"'
'if (Test-Path $vdotScript) {'
' Write-Output "Executing Windows_VDOT.ps1 with selected optimizations..."'
' & $vdotScript -Verbose -AcceptEula -Optimizations @('
' "Autologgers"'
' "DefaultUserSettings"'
' "LocalPolicy"'
' "NetworkOptimizations"'
' "ScheduledTasks"'
' "Services"'
' "WindowsMediaPlayer"'
' ) -AdvancedOptimizations @("Edge")'
' Write-Output "VDOT script completed."'
'} else {'
' Write-Output "ERROR: VDOT.ps1 not found at $vdotScript"'
'}'
'$stepEnd = Get-Date'
'$duration = $stepEnd - $stepStart'
'Write-Output "STEP COMPLETED: VDOT optimization finished at $stepEnd (UTC) (Duration: $($duration.ToString()))"'
]
}
{
name: 'CONF_VDOTOptimisations_RestartPostConfig'
type: 'WindowsRestart'
restartTimeout: '20m'
}
BICEPUPDATE – 26/08/2025 – I noticed that the currency was not set correctly on session hosts following the above guidance, I’ve since updated the code, on line 30 ensure that you’re using the unicode for symbols, in the above example this is set to UK (£) pound symbol.
Additionally, I also have noticed that applying the language pack fails with an error of ‘partially installed’ which is essentially a timeout IF other updates are pending, so I’ve updated the above code so it runs Windows Updates and reboots prior to this.
The Results
Once deployed, users logging in for the first time will see English (United Kingdom) set as their language and regional format — no extra steps needed. Like so:




Bonus Content – GPO Method
But, we want to set the regional settings via group policy…. okay, I hear you, here’s the group policy settings to apply the regional settings (it won’t install language packs or set them as the default), here you go:


Any tips? Do you have an easier method? Would love to hear from you!
Pingback: AVD Community Newsletter – 25th July 2025 – AVD Community