Injecting new Language Packs into an Azure Virtual Desktop or Windows 365 Golden Image with PowerShell – Easily Explained !

A lot of us, who administer Azure Virtual Desktop and engineer new images for our clients, have often the request to inject new Language Packs into the images, so that the new hosts can be setup in a different language.

While there is good article on the topic from Microsoft: Install language packs on Windows 10 VMs in Azure Virtual Desktop – Azure | Microsoft Learn

when I personally read it, it was confusing at first till I was able to implement it, so I decided to create this blog post and try to explain it in a simple way.

In the following article we’ll see how to create a PowerShell script, where we just prepare the sources one time and then we can run this script and reuse it in new environments

Step 1: Prepare the Files (1st time only)

From the Microsoft article, we need to download (Assuming that you’re using Windows 10 21H1 or later) the following 3 ISO’s:

The 3 files are pretty big, so make sure you have at least 20 GB on the drive:

Actually, the “Inbox Apps ISO” is the Windows Apps, which few people want to use in other languages, so in most cases, I only use the previous two ISO’s – Language and Features On Demand (FOD)

From the Language Pack ISO, copy the Language-Pack CAB file for the language you need:

Once you mount the ISO you’ll see the following folder:

For 64 bit OS, from the x64 Folder -> langpacks,

copy the Client Language Pack for the language you need

From the Local Experience Folder copy the following 2 Files from the subfolder with the language you need:

Now, let’s go to the other ISO. with the FOD:

Once you mount it, you’ll see a bunch of FOD language packs, for the different Windows Features, like Language Features Basic, Language Features Fonts and so on.

You can copy all you want, but make sure that you select the right language and system architecture, as some of them are for for other system architectures, like arm.

At the end you should be left with a folder, like this one (in the example for German)

Step 2: Upload the files to a cloud storage

Now, here you can use whatever cloud storage you want, but I used an Azure Storage Account, so my script will be optimized for that.

Zip your files and upload them one per language in the cloud storage

Then create a read-only SAS Link:

You should create one Zip file per Language Pack

Congratulatons – you’re done with the repository of files. This you need to do only once and make sure to prepare all language packs you need or may need

Step 3: Import the Language Packs in the AVD golden Image with PowerShell

Now for each new AVD Environment, you just need to run the script below, again, as explained in the Microsoft article: Install language packs on Windows 10 VMs in Azure Virtual Desktop – Azure | Microsoft Learn

Just make sure that you add a group, like the ones below for DE and NL, for each language you want to install in the image

########################################################
## Add Languages to running Windows Image for Capture ##
########################################################

[CmdletBinding()]
param(
    [Parameter(Mandatory = $false,
        HelpMessage = "specify which language you want to install. At the moment allowed values are DE, NL")]
        [ValidateSet("DE","NL")]
    [string]$LanguagePack="NL"
)

##Disable Language Pack Cleanup##
Disable-ScheduledTask -TaskPath "\Microsoft\Windows\AppxDeploymentClient\" -TaskName "Pre-staged app cleanup"

#connect to the storage account
Install-PackageProvider -Name "Nuget" -Force
install-module az.storage -Force

$context=New-AzStorageContext -ConnectionString "YOUR CONNECTION STRING HERE"

switch($LanguagePack)
{
"DE" {

    #Download the Language Packs"

    Get-AzStorageContainer -Context $context appsources | Get-AzStorageBlobContent -Blob "AVD Language Packs/DE/DE.zip" -Destination $env:TEMP

    Expand-Archive -Path "$env:temp\AVD Language Packs\DE\DE.zip" -DestinationPath "$env:TEMP\AVD Language Packs\Content" -Force

    $contentpath="$env:TEMP\AVD Language Packs\Content\DE"

    ##German##
    Add-AppProvisionedPackage -Online -PackagePath $contentpath\LanguageExperiencePack.de-de.Neutral.appx -LicensePath $contentpath\License.xml
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-Client-Language-Pack_x64_de-de.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-Basic-de-de-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-Handwriting-de-de-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-OCR-de-de-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-Speech-de-de-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-TextToSpeech-de-de-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-NetFx3-OnDemand-Package~31bf3856ad364e35~amd64~de-de~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-InternetExplorer-Optional-Package~31bf3856ad364e35~amd64~de-de~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-MSPaint-FoD-Package~31bf3856ad364e35~amd64~de-de~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-Notepad-FoD-Package~31bf3856ad364e35~amd64~de-de~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-PowerShell-ISE-FOD-Package~31bf3856ad364e35~amd64~de-de~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-Printing-WFS-FoD-Package~31bf3856ad364e35~amd64~de-de~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-StepsRecorder-Package~31bf3856ad364e35~amd64~de-de~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-WordPad-FoD-Package~31bf3856ad364e35~amd64~de-de~.cab
    $LanguageList = Get-WinUserLanguageList
    $LanguageList.Add("de-de")
    Set-WinUserLanguageList $LanguageList -force
}

"NL" {

    #Download the Language Packs"

    Get-AzStorageContainer -Context $context appsources | Get-AzStorageBlobContent -Blob "AVD Language Packs/NL/NL.zip" -Destination $env:TEMP

    Expand-Archive -Path "$env:temp\AVD Language Packs\NL\NL.zip" -DestinationPath "$env:TEMP\AVD Language Packs\Content" -Force

    $contentpath="$env:TEMP\AVD Language Packs\Content\NL"

    ##German##
    Add-AppProvisionedPackage -Online -PackagePath $contentpath\LanguageExperiencePack.nl-nl.Neutral.appx -LicensePath $contentpath\License.xml
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-Client-Language-Pack_x64_nl-nl.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-Basic-nl-nl-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-Handwriting-nl-nl-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-OCR-nl-nl-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-TextToSpeech-nl-be-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-LanguageFeatures-TextToSpeech-nl-nl-Package~31bf3856ad364e35~amd64~~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-NetFx3-OnDemand-Package~31bf3856ad364e35~amd64~nl-NL~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-InternetExplorer-Optional-Package~31bf3856ad364e35~amd64~nl-NL~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-MSPaint-FoD-Package~31bf3856ad364e35~amd64~nl-NL~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-Notepad-FoD-Package~31bf3856ad364e35~amd64~nl-NL~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-PowerShell-ISE-FOD-Package~31bf3856ad364e35~amd64~nl-NL~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-Printing-WFS-FoD-Package~31bf3856ad364e35~amd64~nl-NL~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-StepsRecorder-Package~31bf3856ad364e35~amd64~nl-NL~.cab
    Add-WindowsPackage -Online -PackagePath $contentpath\Microsoft-Windows-WordPad-FoD-Package~31bf3856ad364e35~amd64~nl-NL~.cab
    $LanguageList = Get-WinUserLanguageList
    $LanguageList.Add("nl-nl")
    $LanguageList.Add("nl-be")
    Set-WinUserLanguageList -LanguageList $LanguageList -force

}

}

remove-item -Path "$env:TEMP:\AVD Language Packs\" -Force -ErrorAction SilentlyContinue

After you’re done, make sure you run the following on the host pool:

Create a scheduled task to run with User Credentials on logon (again, you’d need to change it to the languages you need):

$LanguageList = Get-WinUserLanguageList
$LanguageList.Add("de-de")
$LanguageList.Add("nl-nl")
Set-WinUserLanguageList $LanguageList -force

That’s it, I do hope you’d be able to easily administer your AVD environments now 🙂

Related Posts

One thought on “Injecting new Language Packs into an Azure Virtual Desktop or Windows 365 Golden Image with PowerShell – Easily Explained !

  1. Copy files from FOD ISO in Powershell:

    # All de-DE packages
    # Target is Drive Z:\Langpack\

    copy *~de-DE~* Z:\Langpack\

Leave a Reply

Your email address will not be published. Required fields are marked *