How to Create a Windows 10 Task Sequence to Install Language Packs Offline

I performed an interesting experiment in my lab today. I wanted to do a simple task sequence with a language pack. Now I have done this many time from work, but what I was doing differently today, was not using MDT — just installing the OS and language pack from native Config Manager task sequence. Guess what? It did not work as expected. I scratched my head. Thought about what was different, and then realized it was the “Gather” step. Normally, I create variables for the major language settings: System Locale, User Locale, UI Language, and Input Locale. These are not Config Mgr variables. During the “Apply Operating System” step the smsts.log files shows the following steps:

Loading existing answer file “c:\windows\panther\unattend\unattend.xml”
Getting namespace “Microsoft-Windows-Deployment” for architecture “amd64”
Getting namespace “Microsoft-Windows-Shell-Setup” for architecture “amd64”
Getting namespace “Microsoft-Windows-International-Core” for architecture “amd64”
Writing configuration information to c:\windows\panther\unattend\unattend.xml

Following Adam Gross’s mantra, I reviewed the running processes. The “osdapplyos.exe” was running. This function updated the unattend.xml file with the computer name, time zone, and domain join information. Since I could not find any pre-defined variables for language settings, I was left needed a method to change the variables in the unattend.xml file with the actual values. MDT has pre-defined variables for languages, and it updates the unattend.xml file. I decided I wanted to perform my build without using MDT component, even the lite version blogged about by Gary Blok and Gerry Hampson. The solution is a simple PowerShell script, inserted after the “Apply Operating System” step but before the “Apply Windows Settings” step.

With the locale and language information set in the unattend.xml, I just needed to apply the language pack and feature on-demand files while Windows was offline. For this, I created a package (see below) and inserted a “Run Command Line” step. The command line was “dism.exe /image:c:\ /add-package /packagePath:.\ /scratchdir:c:\windows\temp”. The detailed steps are listed below.

After the task sequence completed, I had a computer with Spanish and English installed. The default language and locale information are Spanish. The only “gotcha” with this method is when you go to upgrade the operating system to the next version. Since the system locale is Spanish, the upgrade needs the setup.exe and source files from a Spanish ISO. I have another post about my workaround for this. There is also a Microsoft blog about how to handle this situation.

How To Create a Language Pack Offline Install Task Sequence

You will need to complete the following tasks to build a computer with a language pack installed.

  • Download the Windows 10 language pack ISO from MVLS or MDSN. I usually also download the Feature on Demand disk 1 ISO so that I can include the optional features such as Speech, OCR, handwriting.
  • Create a package with the language pack Cab file and all the feature on-demand files you want to include. My package had the following files in it:
    • Microsoft-Windows-Client-Language-Pack_x64_es-es.cabMicrosoft-Windows-LanguageFeatures-Basic-es-es-Package~31bf3856ad364e35~amd64~~.cabMicrosoft-Windows-LanguageFeatures-Handwriting-es-es-Package~31bf3856ad364e35~amd64~
  • Use “Windows System Image Manager” to create an unattend.xml file for the build. In the “Specialized” and “oobeSystem” nodes, add the “wow64_Microsoft-Windows-International-Core__neutral_31bf3856ad364e35_nonSxS” component. For the InputLocale, SystemLocale, UILanguage, and UserLocale fields, use variables.
  • Create a package for the unattend.xml file.
  • Create a method to associate the language variables to the computer. I am adding the variables to the computer object that I imported.
$PCName = 'PC0001'
$CollectionName = "OSD - Windows 10"
$MACAddress = '00:15:5d:ee:ee:ee'

Import-CMComputerInformation -CollectionName $CollectionName -ComputerName $PCName -MacAddress $MacAddress -MergeIfExist

$PC = Get-CMResource -Fast -ResourceType System |Where {$ -eq $PCName}
Add-CMDeviceCollectionDirectMembershipRule -CollectionName $CollectionName -ResourceId $PC.resourceid -PassThru

New-CMDeviceVariable -DeviceName $PCName -VariableName UILanguage -IsMask $false -VariableValue 'es-ES'
New-CMDeviceVariable -DeviceName $PCName -VariableName SystemLocale -IsMask $false -VariableValue 'es-ES'
New-CMDeviceVariable -DeviceName $PCName -VariableName UserLocale -IsMask $false -VariableValue 'us-ES'
New-CMDeviceVariable -DeviceName $PCName -VariableName InputLocale -IsMask $false -VariableValue 'es-ES;en-US'

Invoke-CMCollectionUpdate -Name 'All Systems'
Invoke-CMCollectionUpdate -Name $CollectionName  

Create a Windows 10 Build Task Sequence with the following modifications:

  • In the “Apply Operating System” step, check the option “use an unattended or sysprep file for custom installation” and associate the package created above.
  • After the “Apply Operating System” step but before the “Apply Windows Settings” step, add a “Run PowerShell Script” step
    • Name: Update Unattend.xml
    • Enter a PowerShell Script
    • PowerShell Exectution Policy: Bypass
      (this was for my lab, so Bypass is acceptable. In production all scripts should be signed.)
#Update Variables in Unattend.xml file
$tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction SilentlyContinue

$UILanguage = $tsenv.value("UILanguage")
$SystemLocale = $tsenv.value("SystemLocale")
$UserLocale = $tsenv.value("UserLocale")
$InputLocale = $tsenv.value("InputLocale")

$File = "C:\windows\Panther\unattend\unattend.xml"

(Get-Content -Path $file)|
    ForEach-Object {$_ -Replace '%UILanguage%', $UILanguage} |
        Set-Content -Path $File

(Get-Content -Path $file)|
    ForEach-Object {$_ -Replace '%SystemLocale%', $SystemLocale} |
        Set-Content -Path $File

(Get-Content -Path $file)|
    ForEach-Object {$_ -Replace '%UserLocale%', $UserLocale} |
        Set-Content -Path $File

(Get-Content -Path $file)|
    ForEach-Object {$_ -Replace '%InputLocale%', $InputLocale} |
        Set-Content -Path $File
  • After the “Apply Operating System” step but before the “Setup Operating System” step, create a new “Run Command Line” step.
    • Name: Install LP Offline
    • Command:  dism.exe /image:c:\  /add-package /packagePath:.\ /scratchdir:c:\windows\temp
    • Package: the package created above.
    • Add a condition to the step:
      • Task Sequence Variable: UILanguage
      • Condition: Equal
      • Value: es-es

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s