Wednesday, 2 November 2011

Listing Lync Server Roles, Versions and Updates

As a Lync Server 2010 administrator one of the things I need to do is make sure that the infrastructure is running a consistent set of updates.

One way to do this is to run the Lync Server Cumulative Update installer on each server in the deployment, but that means connecting to each server and manually running the installer, and it wouldn't give me a nice list of versions.

Instead, I wrote this script to enumerate all the Lync servers in the topology, and get a list of all the roles on each server and their versions. The script uses the Lync database to enumerate all the servers in the deployment, then uses remote registry calls to get a list of all the installed roles, and the updates installed for those roles.


$Servers = Get-CsManagementStoreReplicationStatus | Where {$_.ProductVersion -ne ""} | Select-Object ReplicaFQDN

$Output = @()

ForEach ($Server in $Servers) {
 $ServerFQDN = $Server.ReplicaFQDN
 Write-Host $ServerFQDN
 
 $Reg = $Null
 $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $ServerFQDN)
 If ($Reg) {
  $RegKey = $Reg.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products')
  $ProductsKeys = $RegKey.GetSubKeyNames()
  ForEach ($ProductKey in $ProductsKeys) {
   $PropertiesKey = $Reg.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\" + $ProductKey + "\InstallProperties")
   If ($PropertiesKey.GetValue("DisplayName") -like "Microsoft Lync Server 2010,*") {
    $Component = "" | Select-Object ServerName,RoleName,RoleVersion,RolePatches
    $Component.ServerName = $ServerFQDN
    $Component.RoleName = $PropertiesKey.GetValue("DisplayName")
    $Component.RoleVersion = $PropertiesKey.GetValue("DisplayVersion")

    $PatchesKey = $Reg.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\" + $ProductKey + "\Patches")
    $Patches = $PatchesKey.GetSubKeyNames()
    
    $ComponentPatches = @()    

    ForEach ($Patch in $Patches) {
     $PatchKey = $Reg.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\" + $ProductKey + "\Patches\" + $Patch)
     $ComponentPatch = "" | Select-Object PatchDisplayName,DateInstalled
     $ComponentPatch.PatchDisplayName = $PatchKey.GetValue("DisplayName")
     $ComponentPatch.DateInstalled = (Get-Date -Year $PatchKey.GetValue("Installed").Substring(0,4) -Month $PatchKey.GetValue("Installed").Substring(4,2) -Day $PatchKey.GetValue("Installed").Substring(6,2) -Hour 00 -Minute 00 -Second 00)
     
     $ComponentPatches = $ComponentPatches + $ComponentPatch
    }
    $Component.RolePatches = $ComponentPatches
    $Output = $Output + $Component
   }
  }
 }
}

$PatchOutput = $Output | Select-Object ServerName, RoleName -ExpandProperty RolePatches 
$PatchOutput | Select-Object ServerName, RoleName, PatchDisplayName, DateInstalled | Export-CSV -Path ".\LyncPatches.csv" -NoTypeInformation

$Output | Select-Object ServerName, RoleName, RoleVersion | Export-CSV -Path ".\LyncVersions.csv" -NoTypeInformation
$Output | Select-Object ServerName, RoleName, RoleVersion | Format-Table -AutoSize

Download Script

The script needs to be run in the Lync Management Shell, and requires that the Remote Registry service is running (and accessible) on each server it checks. The script will produce output to the shell and two CSV files - one named LyncVersions.csv containing the current version of each role, and one name LyncUpdates.csv containing the updates installed for each role.

- Ben

4 comments:

  1. Really good script! However, it does not handle Edge servers correctly. Either the script should skip edge servers, or it should ask for credentials for each edge identity.

    ReplyDelete
  2. Thanks for the feedback, I'll try and find a way to better handle Edge servers and will update the script accordingly.

    - Ben

    ReplyDelete
  3. Great script Ben. One issue that I'm having with it though is that I can't get it to output the Server Name to the csv files.

    Running the first line on it's own shows that ReplicaFQDN is capturing the server name correctly, but for some reason it's not passing that on. I've tried experimenting with changing the $ServerFQDN variable but with no success so far.

    Any suggestions as to what may be going wrong?

    ReplyDelete
  4. Thanks for the feedback Pi. I'm not sure what could be happening when you run the script - I just ran it again in my own Lync environment and it seems to be working for me.

    What happens if you run the code by pasting all the script lines directly into a Lync Shell? Do you get any errors? Does $Output contain the FQDNs?

    - Ben

    ReplyDelete