Automating the Import of Office 2013 Language Packs into MDT with PowerShell

I recently needed to add Office Language Pack support into an MDT deployment and found this to be a looooong time consuming task…especially if you need to support a number of languages. So I ended up writing a PowerShell script to automate the steps.

Some background first. The following blog by Johan Arwidmark and Patrick Zovistoski is a great post and should be used in conjunction with my script: – Installing Office 2013 Language Packs in MDT

The high level steps to Install Office Language Packs with MDT are as follows:

  1. Obtain the Office 2013 Language Pack DVD media from the Microsoft VL website.
  2. Create the correct Folder structure for each language you need to import (containing all component language folders, setup files etc…)
  3. Update the config.XML for each language to run silent and suppress reboot.
  4. Import each language into MDT as an application.
  5. Add the Applications into the MDT Task Sequence.

My script automates the first 4 steps…so all you need to do manually is add the languages to the Task Sequence 🙂

So onto the script.  You need to do the following:

  1. Mount the two Office 2013 Language Pack ISO files on your MDT server.
  2. Create a staging folder on your server.  This location will be used to create the source folders for the Language packs.
  3. Copy the script and CSV file (Download from here) into the staging folder on your MDT Server.
  4. Update the varibles at the top of the script with details of your deployment share, path of the Language Pack DVD’s etc…SetVars
  5. Update the CSV file to reflect the languages you want to import.csv
  6. Run the script.

The script will create the source folders for each language you specify and update the config XML’s for each language:Staging afterFinally the script will import each language pack into your MDT deployment share:DSAfter

And that’s it… A good hour of work down to about 5 mins.

Download the script from here

Enjoy 🙂


Quickly Applying MBAM Policy

Implementing Microsoft Bitlocker Administration and Monitoring (MBAM) is a great way to manage Bitlocker on your devices and can be quickly included in the deployment task sequence so that devices are encrypted as part of the task sequence and policy is enforced right from the start… almost.

My preferred way to implement MBAM during an OS deployment is to configure Bitlocker to use the protector “TPM Only” and encrypt the disk as part of the task sequence.  Once the deployment is complete the computer reboots and Group Policy is then applied.  In this Group Policy are the MBAM policy settings that dictate that devices should be protected by both the “TPM Only” and “Startup PIN” protectors.  Therefore, when the MBAM agent refreshes policy, it realises the discrepancy between how Bitlocker is currently configured (TPM Only) and what Group Policy is stipulating and then goes ahead and prompts the user to configure a PIN via the MBAM GUI.

Doing it this way works great except for one simple fact: you need to wait for the MBAM agent policy refresh cycle to run and display the GUI to the user to resolve the configuration discrepancy.  This can take up to 90 minutes which means that during this time window a device is not configured with the Startup PIN as required.

If you don’t want to wait for this policy refresh you can shortcut it by using the below trick.


The MBAM agent actually detects straight away that the configuration set on the device does not match Group Policy and logs an error 2 event in the MBAM event log, but it doesn’t display the MBAM GUI immediately that obliges the user to add the PIN.  You can use the Windows Task Scheduler to attach a task to this event so that, when it is logged, the GUI will be loaded immediately.

The below screenshots show the configuration I use in the scheduled task.

I publish the scheduled task via a Group Policy Preference which means that it is automatically deployed to all in-scope computers as soon as they are built.  Doing it this way means it can be centrally managed and updated easily.

Updating your NO-IP account with PowerShell

This is a little off topic from our usual stuff, but I could find no PowerShell examples anywhere on the web for this so thought it could be useful to someone else!

I run a server at home and have been using No-IP services for free DNS for years.  I had my Internet router configured to update NO-IP directly when my external IP address changed.  Recently though I changed the router for a new one, only to discover that it only supports updates to the DynDNS service, which is no longer free.

Rather than installing the No-IP agent onto my server I wondered if it would be possible to script it in PowerShell and run it as a scheduled task.  There are plenty of Linux and Python scripts on the web that do this, but none I could find in PowerShell; so I wrote my own 🙂

It’s simple to use, just follow the below steps:

  1. Create new EventLog source by running the following command from an elevated PowerShell window: New-EventLog -LogName “Application” -Source “NO-IP Updater”  –  all events logged by the script will appear in this log
  2. Add the below code to a PowerShell script file
  3. Modify the values at the top of the script with your own values
  4. Set it to run as a scheduled task

The script currently pushes an update to the No-IP servers on a schedule, even if your IP has not changed.  Also, it only logs in the Application EventLog the return code from No-IP and does not take any actions based on the value returned.  I’ll fix both of this points at some point in the future if they prove to be in demand.

Finally, it uses an external site in order to discover your external IP address.  This request can sometimes fail for no reason.  When this happens there is a routine in the script to try again from a different source.


# Set static content
$myUser = “MyUser”
$myPass = “MyPassword”
$myHost = “”

Write-Eventlog  -Logname ‘Application’ -Source ‘No-IP Updater’ -EventID 666 -EntryType Information -Message “Starting…”

# Fetch external IP
Write-Host “Fetching external IP…”
$myIP = (Invoke-WebRequest
$myIP = $myIP.Trim()

Write-Host “Value $myIP found, validating…”

# Validating IP
$IPCheck = [bool]($myIP -as [ipaddress])
Write-Host “Validation result: $IPCheck $myIP”

If ($IPCheck -eq $false)
    Write-Host “Failed to get external IP.  Trying with different host”
    $myIP = (Invoke-WebRequest
    Write-Host “External IP: $myIP”


Write-Host “External IP: $myIP”

# Build URL for update
$URL = “$myUser&password=$myPass&hostname=$myHost&ip=$myIP”

# Print output
Write-Host “Updating host $myHost with IP $myIP”

# Updated
$update = Invoke-WebRequest $URL

# Write to EventLog
$strToLog = “Error returned: $update`r`nFull HTTPS string used: $URL”
Write-Host “Writing to log: $strToLog”
Write-Eventlog  -Logname ‘Application’ -Source ‘No-IP Updater’ -EventID 666 -EntryType Information -Message $strToLog

Fixing Office 2013 1920 Error during OSD on Windows 10 Technical Preview

I have been playing with the Windows 10 Technical Preview this week and I ran into an obscure error when trying to install Office 2013 as part of my Deployment Task Sequence in SCCM. The Office 2013 setup.exe returned a 1920 error when deploying the Task Sequence to Virtual Machines (Physical were fine). The Office Setup log reports the following error:

Error 1920. Service ‘Windows Font Cache Service’ (FontCache) failed to start. Verify that you have sufficient privileges to start system services.

Taking a closer look I could see the service was in a stopped state and I could not start it manually…that was until I restarted 🙂  Even with Office being the first Application installation in the Task Sequence it would fail. So if you are installing Office 2013 on the Win 10 TP you need to make sure you add an additional restart immediately before the Office installation step.


Scripting the Creation of Windows 7 Libraries

Firstly: happy new year!  Secondly: yes I know this is about Windows 7 but there are still plenty of Windows 7 desktops out there!  I haven’t tested this on Windows 8.x yet, so if you do please let me know how it goes 🙂

Ever since Windows 7 was first released in Beta, one of the common gripes amongst the techies doing deployment projects was how to automate through scripting, and therefore MDT, the creation/modification of the Windows 7 Libraries.  I’ve seen a few different solutions but they were all rather ugly as they involved hacking the registry using a process discovered (no idea by who) through trial-and-error. I never liked these hacks so would always steer customers well away from them (as well as towing the Microsoft line of “this is an unsupported method” etc.).

Well, fret no more, because it seems that Microsoft finally got round to creating such a tool – and even published the source code for it!  You can find the MSDN documentation for SHLIB.exe here:

Here are the usage instructions for it: shlib.exe SUBCOMMAND

Supported commands:

create   –   Creates a library at the specified path.
info   –   Prints info about the given library.
enum   –   Enumerates the folders in the library.
setattrib   –   Modifies the attributes of the library.
add   –   Adds the specified folder to the specified library.
remove   –   Removes the specified folder from the library.
setsaveloc   –   Sets the default save location of the library.
resolve   –   Resolves the specified folder in the library.
resolveall   –   Resolves all locations in the library in bulk.
manage   –   Displays the Manage Library Dialog for the library.

By the way, Raymond Chen also blogged about it here:

Snippet #2 – Checking for free disk space with PowerShell

Inspired by Dan’s last post, I thought I would share some more PowerShell goodness. A very quick one to start with: How to check for free disk space with PowerShell.

As you would expect with PowerShell its amazingly simple. Here is the function:

Function GetFreeDiskSpace([string]$DriveLetter, [string]$Measurement)


$Drives=Get-WmiObject -Class Win32_LogicalDisk

foreach($Drive in $Drives)


If ($Drive.Name -eq $DriveLetter)






The function simply queries the WMI LogicalDisk class and then converts the fresspace to GB, MB or KB. For example, if you want to find out how much free disk space is available for the c:\ in Gigabytes, you would call the function as follows:

GetFreeDiskSpace “C:” “GB”

write-host “Freespace = $Freespace

That’s all for now 🙂

Snippet #1 – Checking for Admin Rights with PowerShell

I know we’ve all been rather slack with the blog lately, this post is a more concerted effort by me to resolve this!

As a die-hard VBScript-er I never really saw the need for me to learn Microsoft PowerShell… until I was required to use it to conform to the coding standards on a project I was working on. Well I get it now, it works and is immensely powerful.  Also, why write 20 lines of VBS when you can achieve the same with a single line of PowerShell? It works a treat, although I still drop back to VBS from time to time 🙂

Over time I’ll share code snippets that I use frequently that could prove useful to the masses, even if it is just to show the simplicity of something in PowerShell. The first snippet I’m sharing is how to check if your script is running with admin rights or not.

Add it as a function to your script, it will then return a Boolean value back to your caller.

$boolAdminRights = funcCheckAdminRights
Write-Host $boolAdminRights

Function funcCheckAdminRights
$windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object ‘System.Security.Principal.WindowsPrincipal’ $windowsIdentity
$AdminRights = $windowsPrincipal.IsInRole(“Administrators”)

If ($AdminRights)
# Admin rights OK
Return $True
# No admin rights
Return $False

Pretty simple!