Friday, February 11, 2011

Audit Tivoli Storage Manager Backup Client Schedules on Windows Servers

As any System Administrator knows you need to have a good backup. IBM Tivoli Storage Manager is a common platform used in large environments. It's a client-server platform with an installed client that once registered with the TSM server, backups the local server (client) to the destination backup storage. Keeping track of client versions, client nodes and log files can be burdensome if you have multiple TSM servers in different data centers. To keep abreast of this information, auditing is key. Knowing how the configuration of Tivoli Storage Manager Backup Client is stored for schedules gives us the ability to audit them. Most of the information resides in the registry under "HKEY_LOCAL_MACHINE\SOFTWARE\IBM". In the code example below, a list of server fully qualified domain names are stored in a text file and read into the script. To obtain this information using PowerShell, I am using the Microsoft.Win32.RegistryKey class to navigate the branches of the registry containing the configuration values of interest to me. One of those values, the option file path, allows me to obtain even more information about the backup -- the TSM server and port used for client-server communication. I take the local path of the option file and translate it to a UNC Path that I can remotely access, load the file into a foreach loop, attempt to located those two values and store them into strings. Just in case this process fails, I pre-populate the two strings with "FAILED". There are a few reasons that this could fail, the security context that is trying to access the option file doesn't have rights, the file doesn't exist (an uninstall failed to clear the registry) or the variables in the opt file we are searching for are not present.

This script does not report the servers lacking a client schedule. It is really important to know this information. Without a backup, you do not have recovery! I have another method that ensures that all servers that require backup are monitored for that process and if it was successful. If you do not, I would take some time to alter the script record the servers without schedules.

If you are running this script in an environment with both 32 bit and 64 bit Windows Server versions, you need to execute it from a 64 bit version of Windows Server. If you run it from a 32 bit and query the registry of a 64 bit server, the returned registry will be the 32 bit compatibility hive. TSM Backup Client has a native 64 bit version.

Function Get-TSMInfo($server, $tsmInfo) {
 $key = "SOFTWARE"
 $hKey = [Microsoft.Win32.RegistryHive]::LocalMachine
 $baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hKey, $server)
 foreach($rootKeyValue in ($baseKey.OpenSubKey($key)).GetSubKeyNames()) {
  if($rootKeyValue -eq "IBM" -and ($baseKey.OpenSubKey("$key\IBM\ADSM\CurrentVersion")).SubKeyCount -gt 2) {
   $tsmVersion = ($baseKey.OpenSubKey("$key\IBM\ADSM\CurrentVersion\BackupClient")).GetValue("PtfLevel")
   $tsmPath = ($baseKey.OpenSubKey("$key\IBM\ADSM\CurrentVersion\BackupClient")).GetValue("Path")
   $key = "SYSTEM\CurrentControlSet\Services"
   if($tsmVersion -ne "" -and $tsmPath -ne "") {
    foreach($keyValue in ($baseKey.OpenSubKey($key)).GetSubKeyNames()) {
     foreach($subKeyValue in ($baseKey.OpenSubKey("$key\$keyValue")).GetSubKeyNames()) {
      $clientNodeName = ""
      $errorLog = ""
      $optionsFile = ""
      $scheduleLog = ""
      if(($baseKey.OpenSubKey("$key\$keyValue").GetValue("Start")) -eq "2") {
       if($subKeyValue -eq "Parameters") {
        foreach($value in ($baseKey.OpenSubKey("$key\$keyValue\Parameters")).GetValueNames()) {
         if($value -eq "clientNodeName") {
          $clientNodeName = ($baseKey.OpenSubKey("$key\$keyValue\Parameters")).GetValue($value)
         } elseif($value -eq "errorLog") {
          $errorLog = ($baseKey.OpenSubKey("$key\$keyValue\Parameters")).GetValue($value)
         } elseif($value -eq "optionsFile") {
          $optionsFile = ($baseKey.OpenSubKey("$key\$keyValue\Parameters")).GetValue($value)
         } elseif($value -eq "scheduleLog") {
          $scheduleLog = ($baseKey.OpenSubKey("$key\$keyValue\Parameters")).GetValue($value)
      if($clientNodeName -ne "" -and $errorLog -ne "" -and $optionsFile -ne "" -and $scheduleLog -ne "") {
       $optionsFileUncPath = ("\\$server\" + ($optionsFile.SubString(0,1) + "$" + $optionsFile.SubString(2)))
       $tsmServer = "FAILED"
       $tsmClientPort = "FAILED"
       if(Test-Path -path $optionsFileUncPath) {
        foreach($line in (Get-Content -path $optionsFileUncPath)){
         if($line -match "TCPSERVERADDRESS") {
          $tsmServer = ($line -replace "TCPSERVERADDRESS","").Trim()
         if($line -match "TCPCLIENTPORT") {
          $tsmClientPort = ($line -replace "TCPCLIENTPORT","").Trim()
       $clientNodeInformation = New-Object -typeName PSObject
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "server" -value $server
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "tsmVersion" -value $tsmVersion
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "installPath" -value $tsmPath
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "tsmServer" -value $tsmServer
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "tsmClientPort" -value $tsmClientPort
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "scheduleName" -value $keyValue
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "clientNodeName" -value $clientNodeName
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "optionsFile" -value $optionsFile
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "scheduleLog" -value $scheduleLog
       Add-Member -inputObject $clientNodeInformation -type NoteProperty -name "errorLog" -value $errorLog
       $tsmInfo += $clientNodeInformation
 return $tsmInfo


if(![bool]$file) {
 Write-Host "You are missing the `"-file`" command line argument" -foregroundColor Red
 Write-Host ""
 Write-Host "This is the file that contains the list of server fully qualified"
 Write-Host "domain names that you want to audit their TSM Clients"
 Write-Host ""
 while((![bool]$file)) {
  $file = Read-Host -prompt "`tFile"
 Write-Host ""
if(!(Test-Path -path $file)) {
 Write-Host "Unable to locate: $file" -foregroundColor Red
 Write-Host ""
 Write-Host "Please review the correct path"
 Write-Host ""
 while(!(Test-Path -path $file)) {
  $file = Read-Host -prompt "`tFile"
 Write-Host ""
$answer = $null
while($answer -ne "Y" -and $answer -ne "N") {
 $answer = Read-Host -prompt "Are you sure you want to use $file (Y/N)?"
if($answer -eq "N") {
 Write-Host ""
 Write-Host "Quiting..." -foregroundColor Yellow

$tsmInfo = @()

foreach($server in (Get-Content -path $file)) {
 Write-Host "Auditing $server"
 $tsmInfo = @(Get-TSMInfo $server $tsmInfo)
 Write-Host ("Schedules found: " + $tsmInfo.count)

$tsmInfo | Export-Csv -path "TSM Client Audit.csv" -noTypeInformation

No comments:

Post a Comment