Thursday, March 15, 2007

Powershell: Return value from remote registry key

One of the first things I wanted to do in Powershell was to get a value from a registry key on remote servers and show it to me in a list. For example, we use a backup program called Tivoli Storage Manager. If I want to see what version of the TSM client I have installed on all of my managed servers, here is a script that will do it.

A few things to note about this script. I'm using another function to get the list of servers that I want to check (see my previous blog post for more information). This script will actually ping each server before running get-wmiobject because get-wmiobject has a long timeout, and I dont want to have to wait 60 seconds for every server that may not be on the network.


$ErrorActionPreference = "silentlycontinue"
$starttime = get-date
$HKLM = 2147483650

$servers = get-servers

$auth = get-credential admin

# display the header info
write-host $('{0,-17}{1}' -f "ServerName","TSM Version")
write-host "--------------- -----------"

$count = 0

# sort the computer names and get various wmi info for that server
$servers.keys | sort | foreach `
{
$ping = get-wmiobject win32_pingstatus -filter "address='$_'"

if ($ping.statuscode -eq 0)
{
if ($servers.$_ -eq "ad" )
{
$reg = get-wmiobject -list -namespace root\default -computer $_ | where-object {$_.name -eq "StdRegProv" }

if ($? -eq $false)
{
write-host $_.tolower() "...wmi query failed." -foregroundcolor Red
}
else
{
$tsmver = $reg.getstringvalue($HKLM, "software\ibm\adsm\currentversion\backupclient","ptflevel")

write-host $('{0,-17}{1}' -f $_.tolower(),$tsmver.svalue)
}
}
else
{
$reg = get-wmiobject -list -namespace root\default -computer $_ -credential $auth

if ($? -eq $false)
{
write-host $_.tolower() "...wmi query failed." -foregroundcolor Red
}
else
{
$tsmver = $reg.getstringvalue($HKLM, "software\ibm\adsm\currentversion\backupclient","ptflevel")

write-host $('{0,-17}{1}' -f $_.tolower(),$tsmver.svalue)
}
}
}
else
{
write-host $_.tolower() "...ping failed." -foregroundcolor Red
}

$count += 1
}

write-host
write-host "Total servers: $count"
$endtime = (get-date).subtract($starttime)
write-host "Elapsed time:" $('{0:D2}:{1:D2}:{2:D2}' -f $endtime.hours,$endtime.minutes,$endtime.seconds)
write-host

Powershell: Find all managed servers

Where I work, about half of the servers I manage are active directory member servers, and the other half are standalone servers. Eventually they will all be in AD, but for now, thats just the way things are. Anyway, it has obviously been a systems management problem for running remote scripts. Now that Powershell is finally out, I decided to try and write some scripts to make my life easier. I decided to start blogging about some of what I’ve done because it is hard to find sample Powershell scripts, and the books that are currently out don’t quite go in depth enough.

So this brings me to my first Powershell function called get-servers.

get-servers will search an Active Directory OU (or more) for all computer objects, store it in a hashtable. In addition, it reads a text file (in the current directory) and adds those servers to the same hashtable. Server names are stored as keys, and “ad” or “standalone” is stored as the value. This way, I can use simple if/else statements to determine whether or not I need to pass along alternate credentials when using cmdlets such as get-wmiobject.


function get-servers()
{
$ErrorActionPreference = "silentlycontinue"
$objComputer = @()
$strCategory = "computer"
$servers = @{}

# bind to the directory - this also sets our starting point for the search
$objDomain = [ADSI]'LDAP://OU=myou,DC=mydomain,DC=com'
$objDomainDC = [ADSI]'LDAP://OU=Domain Controllers,DC=mydomain,DC=com'

$objSearcher = new-object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = ("(objectCategory=$strCategory)")
[void]$objSearcher.PropertiesToLoad.Add("name")

# run the search
$colResults = $objSearcher.FindAll()

$objSearcher.SearchRoot = $objDomainDC
$colResults += $objSearcher.FindAll()

$colResults | foreach-object { $servers.$($_.properties.name) = "ad" }

# add standalone servers
get-content servers.txt | foreach { $servers.$_ = "standalone" }

return $servers
}

Edit and save this to a file such as “get-servers-function.ps1″ and load it into your current environment by running:

 . ./get-servers-function.ps1

Then if you run get-servers then it should return your servers as designed. The advantage to having this in a function is so that you don’t have to duplicate the code in every script that you write. All you have to do when writing a new script is just add this line near the top:

 $servers = get-servers

Enjoy!