Removing Speech files from Lync Server 2010 by using Windows PowerShell


This article discusses a Windows PowerShell script that queries the server, and collects the names and uninstall information for all files installed, but not removed, by the Lync Server Deployment Wizard. Exceptions are those common files that are typically reused on another server installation, such as the SQL-related files and Microsoft Silverlight browser plug-in. When uninstalling a Lync server, the Deployment Wizard removes files from servers that no longer appear in the topology document created by Topology Builder. However, there are several files that are not removed. Specifically, the Speech Server platform files and all the language variations are not removed. The script is new, but is inspired by an initial script written by Scott Stubberfield and Jens Trier Rasmussen. (Kudos for the idea to refine it and the permission to do so.)

Author: Rick Kingslan

Publication date: July 2011

Modification date: August 2011

Product version: Microsoft Lync Server 2010, Planning Tool

Introduction

When you install Microsoft Lync Server 2010 Standard Edition Front End servers and Enterprise Edition pool members, the Lync Server Deployment Wizard installs prerequisite and required files for the Front End servers to function as they were designed. Part of what is installed, in addition to several other files, is the following items:

  • Microsoft Server Speech Platform Runtime
  • Microsoft Server Speech Recognition Language
  • Microsoft Server Text to Speech Voice components

You might be thinking, what’s the importance of these three files? And, why would you write an article about removing them? You’d be correct that there would not be an article on a more automated way to remove three files. But, there are a total of (as of the writing of this article, more could be added as demand dictates) one for Microsoft Server Speech Platform Runtime, twenty-six for Microsoft Server Speech Recognition Language, and twenty-six for Microsoft Server Text to Speech Voice components. During the writing of the Uninstalling Lync Server 2010 and Removing Server Roles topics, it was necessary to frequently uninstall and then reinstall the servers. Deployment Wizard does not remove the Speech files when you remove the server role from a Topology Builder design. It became somewhat tedious to manually remove these files. The outcome of this particular pain point is the Windows PowerShell script that is presented in this article. The script is a way to do this task more automatically.

It’s not that using Add/Remove Programs in Windows Server is difficult – in fact, it’s quite easy. But, no one wants to have to remove fifty-three files, one at a time, where each requires a minimum of three mouse clicks. You would think that there is a better way to handle this – and there is. PowerShell can automate the process and remove all the Speech Server-related files for you by running one script. That’s the focus of this article – the PowerShell script and how it works.

Keep in mind that the intention of this article is not to educate you about PowerShell. There are many books, websites, and other articles that do a better job than this article can. This article focuses on how the script does what it does to remove the Speech Server files, and then on how to extend the script to remove more, if that’s something you need to do. So, let’s move on to the script itself.

The Windows PowerShell Script

Code Listing 1 is the script that does the work that has been discussed, complete with comments that describe what each section of the script actually does. The script can be used as is, or you can modify it to better suit your needs.

Code Listing 1. PowerShell code listing for removing Speech files from a Lync server

<#

.AUTHOR

Rick Kingslan

(Inspired by a script by Scott Stubberfield and Jens Trier Rasmussen)

.VERSION 1.1

.SYNOPSIS

Solves the problem where a lot of software files are left installed, specifically, Speech files.

.DESCRIPTION

The script accesses Uninstall keys in registry to retrieve the uninstall strings for installed software. Specific installed software is searched for and the uninstall string and package name written to an array. The array is parsed for name and uninstall string. The uninstall string is called to uninstall the software. Note that the PowerShell ability to access the registry directly through the PSDrive HKLM:. Also note that the uninstall nodes under \Software\Wow6432Node\Microsoft and \Software\Microsoft are both searched to ensure that all packages are found.

.EXAMPLE

There are no parameters for this script. You run the script from the Windows PowerShell console or Lync Server 2010 Management Shell on the server where the software packages are installed. for example:

PS > ./RemoveLyncServerFiles.ps1

#>

# Create the variables and arrays

$apps = @()

$appsid = @()

$Confirm = "@"

$i = "0"

# Read the registry Uninstall keys for all

# software installed. Get Display Name and Uninstall

# string for only those that match the criteria

Get-ChildItem -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall | Where-Object -FilterScript { $_.GetValue("DisplayName") -like "*Microsoft Server Speech Recognition Language*"} | ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString") }

Get-ChildItem -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall | Where-Object -FilterScript { $_.GetValue("DisplayName") -like "*Microsoft Server Speech Platform Runtime*"} | ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString") }

Get-ChildItem -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall | Where-Object -FilterScript { $_.GetValue("DisplayName") -like "*Microsoft Server Speech Text to Speech*"} | ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString") }

Get-ChildItem -Path HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall | Where-Object -FilterScript { $_.GetValue("DisplayName") -like "*Microsoft Server Speech Recognition Language*"} | ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString") }

Get-ChildItem -Path HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall | Where-Object -FilterScript { $_.GetValue("DisplayName") -like "*Microsoft Server Speech Platform Runtime*"} | ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString") }

Get-ChildItem -Path HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall | Where-Object -FilterScript { $_.GetValue("DisplayName") -like "*Microsoft Server Speech Text to Speech*"} | ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString") }

# Write out what we intend to remove for the

# user so that he/she can make a decision. If

# no target apps are installed, exit right here.

if ($appsid.Length -eq 0)

{

[Console]::ForegroundColor = "red"

Write-Host "No qualifying packages found - Exiting....."

$apps = ""

$appsid = ""

$Confirm = ""

$i = ""

[Console]::ResetColor()

Exit

}

Write-Host "Applications listed in Control Panel's Applications and Programs that met the criteria:`n`n"

$apps | Format-Table -AutoSize DisplayName

# Change the color of the Host Console output to

# call attention to it

[Console]::ForegroundColor = "yellow"

# Get the input value - is @, should be y,n,yes,no

# otherwise default 'Try again...'

while ($Confirm -eq "@")

{

switch (Read-Host "`n`n`n`aResponding YES or Y will affect Lync Server functionality.`n The packages listed will be removed.'n Are you sure you wish to continue? [Y] Yes [N] No")

{

"yes" {$Confirm = "yes"}

"no" {$Confirm = "No"}

"y" {$Confirm = "yes"}

"n" {$Confirm = "No"}

default {Write-Host "Invalid selection, please try again"}

}

}

# Got a valid answer - reset console to default

[Console]::ResetColor()

if ($Confirm -eq "yes")

{

# Loop though the array - write out the package name,

# pass uninstall string to host via ‘CMD /c’ for action

for ($i = 0; $i -lt $appsid.Count; $i++)

{

# Reformat the Uninstall string

# As written, MsiExec /I won't work

$uninstallstring = $appsid[$i].ToUpper()

if ($uninstallstring -match "MSIEXEC.EXE /I")

{

$appsid[$i] = $uninstallstring -replace "MSIEXEC.EXE /I", "MsiExec.exe /Q /X"

}

elseif ($uninstallstring -match "MSIEXEC.EXE /X")

{

$appsid[$i] = $uninstallstring -replace "MSIEXEC.EXE /X", "MsiExec.exe /Q /X"

}

Write-Host "Uninstalling" $apps[$i]"..."

#This is here for debug support

#Write-Host $appsid[$i]

#.Execute the MsiExec command natively

cmd /c $appsid[$i]

}

}

# Clean up behind ourselves

$apps = ""

$appsid = ""

$Confirm = ""

$i = ""

How the script works

Remember that this article isn’t an in-depth discussion of PowerShell. We’ll look at specific sections of the script so that it’s easier to understand what is happening. This will let you refine and extend the script for your needs. Also, we’ll provide a quick ‘send-off’ by showing you how to quickly get the execution of the script up and running.

Getting information about what to remove

The script uses direct references to the HKEY Local Machine registry path by way of default mappings of a PSDrive in PowerShell. A powerful feature of PowerShell, this allows this script to directly read and write to registry, as if the registry was a file system. In the case of this script, we use this feature to search the registry for the entries we need to remove and read the UninstallString registry entry.

Get-ChildItem -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall | Where-Object -FilterScript { $_.GetValue("DisplayName") -like "*Microsoft Server Speech Recognition Language*"} | ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString") }

The workhorse portion of the script is using a filter to get a match on the phrase "Microsoft Server Speech Text to Speech Voice," which matches a portion of the displayed string in Add/Remove Programs in Control Panel ({ $_.GetValue("DisplayName") -like "*Microsoft Server Speech Recognition Language*"}). An example subset of text strings that match the criteria is shown in Figure 1.

Figure 1. Snippet of Add/Remove Programs showing the Speech Text to Speech Voice packages

 

Be aware that we are not exactly matching the name. If we did, we’d have twenty-six entries, which would defeat the purpose of automation. We can match enough of the string to ensure that it’s unique and that we match all possible instances.

Every match in registry for the phrase is then stored in an array, $apps, and the uninstall string associated with the application is stored in an array $appsid (ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString") })

Be aware that that this is generic PowerShell and it does not leverage any items specific to Office Communications Server or Lync Server 2010.

Displaying Results and Acting on Them

As soon as the script has parsed the registry and all matches have been made and assigned to the arrays, it’s time to do something with them.

The collected information is displayed:

Write-Host "Applications listed in Control Panel's Applications and Programs that met the criteria:`n`n"

$apps | Format-Table –AutoSize

The script then asks for confirmation, based on what’s displayed:

while ($Confirm -eq "@")

{

switch (Read-Host "`n`n`n`aResponding YES or Y will affect Lync Server functionality.`n The packages listed will be removed.'n Are you sure you wish to continue? [Y] Yes [N] No")

If the user enters ‘y’, ‘Y’, Yes or yes, the script then executes the uninstall strings stored in the $appsid array using a simple Write-Host to send the uninstall string to the operating system for processing:

for ($i = 0; $i -lt $appsid.Count; $i++)

{

# Reformat the Uninstall string

# As written, MsiExec /I won't work

$uninstallstring = $appsid[$i].ToUpper()

if ($uninstallstring -match "MSIEXEC.EXE /I")

{

$appsid[$i] = $uninstallstring -replace "MSIEXEC.EXE /I", "MsiExec.exe /Q /X"

}

elseif ($uninstallstring -match "MSIEXEC.EXE /X")

{

$appsid[$i] = $uninstallstring -replace "MSIEXEC.EXE /X", "MsiExec.exe /Q /X"

}

Write-Host "Uninstalling" $apps[$i]"..."

Script Execution

1. Copy the entire script code, that is everything in the box labeled Code Listing 1 in the section “Windows PowerShell Script,” and then paste it into any text editor tool. (Notepad works fine for this.)

2. Save the script as SpeechPackRemoval.PS1 (save it as any file name you want as long as it has the .ps1 file extension).

To run the script, right-click the file, and then select Run with PowerShell as shown in Figure 2.

Figure 2. Right-click and select Run with PowerShell

As soon as the script has started, enter Y to uninstall all the Speech Packs.

Summary

Although effective and tested, the script isn’t perfect. There are ways to improve it (error handling, better use of memory, more concise calling, and, potentially not using the registry at all). But, the intention of this script is to remove several application files from Add/Remove Programs on a server that you are likely decommissioning anyway. Can you extend this to do more? Of course! Remember that we are doing a filter search on strings in registry that correspond to the display string of an application (or a part of an application string). To extend the functionality of the script, just add your criteria:

Get-ChildItem -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall| Where-Object -FilterScript { $_.GetValue("DisplayName") -like <Add your Own File or Files to Remove Here} | ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString") }

For example, adding the following to the script directly before or after the first Get-ChildItem script block, as follows, would result in removing the IIS URL Rewrite Module 2. Simple, right? Easy to extend, easy to use. And, you don’t have to lose feeling in your fingers as you mouse around fifty-three times and over 150 mouse clicks to remove the speech files.

Get-ChildItem -Path Get-ChildItem -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall | Where-Object -FilterScript { $_.GetValue("DisplayName") -like "IIS URL Rewrite Module 2"} | ForEach-Object -Process { $apps += $_.GetValue("DisplayName"); $appsid += $_.GetValue("UninstallString"); $uninstallstring += $apps + ',' + $appsid + "`n`r";; $displayname += $apps + "`n`r"; $displayid += $appsid }

Additional Resources

To learn more check out the following articles:

Lync Server Resources

We Want to Hear from You

Keywords: speech files, PowerShell, Silverlight, deployment, Topology Builder, Speech Server

Comments (5)
  1. SMS Bradley says:

    Rick, nice extension of the original script by Scott Stubberfield and Jens Trier Rasmussen. Powershell rocks.

  2. soder says:

    Just an FYI: lync FE / standard edition can successfully be installed, if all the language packs are missing in the installation folder (except the en-US of course). However, the edge server install wizard alwayss stops if any of these language packs are missing. Quite strange, isnt it?

  3. nick says:

    nice post.

  4. @soder - I'm not completely sure that I understand. The Speech files are only installed where voice and voice to text requirements would place them - such as the Front End(FE) servers or the Standard Edition (SE) server. Typically, the speech files aren't on the Edge. So, I'm quite interested in the issue that you are seeing where the Edge cannot be installed if language packs EXCEPT for en-US are missing.

  5. soder says:

    Rick:

    sorry, I was not monitoring all of my open threads worldwide, but I am here again 🙂

    Test is very simple: copy all the content of the Lync DVD media to the local hard disk, but ignore all the speech runtime files from the DVD (to be honest I am not even sure if you need even the en-US speech pack or not, but better to feel safe than sorry lets be generous you can copy that single file).

    Start the deployment wizard, create the local copy of the CMS, and in the 2nd step try to add/remove lync roles. It will fail @ the file precaching phase, as it will unable to locate the speech runtime files.

    Bug or a feature? 🙂

Comments are closed.

Skip to main content