Expert Solution for 2011 Scripting Games Beginner Event 5: Use PowerShell Objects to Gather Basic Computer Information

Expert Solution for 2011 Scripting Games Beginner Event 5: Use PowerShell Objects to Gather Basic Computer Information

  • Comments 9
  • Likes

Summary: Microsoft PowerShell MVP Jeffery Hicks solves 2011 Scripting Games Beginner Event 5, and returns objects with desktop configuration information.

Microsoft Scripting Guy, Ed Wilson, here. Today we have Jeffery Hicks as our expert commentator for Beginner Event 5.

Beg5-1

Jeffery Hicks is a Microsoft MVP in Windows PowerShell and an IT veteran with almost 20 years of experience. Much of his time has been spent as an IT consultant specializing in Microsoft server technologies. He works today as an independent author, trainer, and consultant. Jeff also writes the popular Prof. PowerShell column for MPCMag.com. His latest book is Managing Active Directory with Windows PowerShell 2.0: TFM 2nd Ed. (SAPIEN Press 2011).
Jeff’s contact information:
Blog: The Lonely Administrator
Twitter: twitter.com/jeffhicks

Worked solution

In this event, you were asked to find computer and operating system information and save it to a text file. An advanced solution would most likely center on a reusable advanced function. But for this exercise, I’ll assume that you want more of a workflow approach. The script is a complete set of commands that you might run on a monthly basis. My solution should cover all of the essential elements, and it throws in a few extra goodies that I feel even as a beginner you should be striving to include.

Right off the bat, I hope you notice all the internal comments and use of spacing and indenting. This makes your script much easier to read and troubleshoot. You’ll also notice that I am not using any aliases and I am using full parameter names. It may take a bit longer to initially type, but there’s nothing cryptic to decipher.

When you start reading the script, you’ll notice that it accepts parameters.

Param (

 [string]$File="computers.txt",

 [string]$LogFile="ComputerReport.txt",

 [string]$ErrorLog="failed.txt",

 [Switch]$Append

 )

This allows maximum flexibility, although I’ve hard coded in some default values. My solution will go through all the computers in the comptuers.txt file and save the results to ComputerReport.txt. Any computers that can’t be contacted will end up in the Failed.txt log file. The Append parameter is a switch. If you use the parameter, it has a value. It might look like this:

PS C:\> MyScript –file computers.txt –switch

I’m using the parameter to indicate if the results should be appended the log file.

My version of the script gets all computer names from the text file and saves them to a variable.

#retrieve computer names filtering out any blank lines

    $Computers=Get-Content -Path $file | Where {$_}

I pipe the file contents to Where-Object so that only lines that exist will be saved. This is a nifty trick for filtering out blank lines. The next special feature I’ve added is to ping the ComputerName first by using Test-Connection.

if (Test-Connection -ComputerName $Computer -Count 2 -Quiet)

Test-Connection has a Quiet parameter that suppresses the ping information and simply returns True or False. Any failures will result in writing the computer name to the error log.

else

         {

            #computer is not pingable

            $Computer.ToUpper() | Add-Content -Path $ErrorLog

         }

Otherwise, the script uses WMI to get the necessary information.

Try

{   

   #attempt to retrieve WMI information and if there is an error catch it

   $OperatingSystem=Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer -ErrorAction Stop

   $ComputerSystem=Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer -ErrorAction Stop

In my version I’m taking advantage of Try/Catch to handle any errors. For example, even though I might be able to ping the computer, I might not have the right permissions (my version doesn’t support alternate credentials), or there might be a WMI problem. If there is an exception, the code in the Catch script block is executed.

Catch

            {

                Write-Warning "There was a problem retrieving information from $($Computer.ToUpper())"

                #write the exception message as a warning

                Write-Warning $_.Exception.Message

                #write the failed computername to a text file

                $computer.ToUpper() | Add-Content -Path $ErrorLog

            } #close Catch scriptblock

By the way, I hope you notice my use of Write-Host and Write-Warning. I use the former to display status messages and the latter to display more serious messages without having to throw a complete exception.

Assuming that I was able to get the WMI information, I use New-Object to create a custom object. The data came from two different WMI classes, so this is an easy way to synthesize a custom object. Using the Property parameter and a hash table makes this a pretty easy process and one that I use very frequently in my scripting.

if ($OperatingSystem -AND $ComputerSystem)

{

    #create a custom object using a property hash table

    #I'm including ServicePack information since that might come in handy

    $data+=New-Object -TypeName PSObject -Property @{

       User=$ComputerSystem.UserName

       Commputer=$OperatingSystem.CSName

       Domain=$ComputerSystem.Domain

       OS=$OperatingSystem.Caption

       ServicePack=$OperatingSystem.ServicePackMajorVersion

       Model=$ComputerSystem.Model

       Reported=(Get-Date)

      }

The Reported property will contain the value of the Get-Date expression. In my version, I add the custom object for each computer to a previously initialized array.

When all the computers have been processed, all that is left is to create the text file. But it only makes sense if information was returned.

#measure how many computers were queried and save the count property to a variable

  $count=($data | measure-object).Count

  Write-Host "Successfully queried $Count computers." -ForegroundColor Cyan

  #write the WMI information to a log file if $data has anything in it

  if ($count -gt 0)

   {

If there is data, I check to see if the Append parameter was used. If so, the data is piped to Out-File by using its Append parameter. Otherwise a new file is created. In both instances, I’m specifying the encoding to be ASCII.

#if -Append then add the data to the file

if ($Append)

{

   $data | Out-File -FilePath $LogFile -Encoding ASCII -Append

}

else

{

   #write the data to a new file

   $data | Out-File -FilePath $LogFile -Encoding ASCII

}

 

   Write-Host "See $LogFile for details and $ErrorLog for failed computers." -ForegroundColor Cyan

Here’s an example of what you might see when you run my script.

Image of script output

The log file will have an entry like this for every computer:

Domain      : JDHIT

Commputer   : SERENITY

Reported    : 3/22/2011 3:03:22 PM

ServicePack : 0

User        : JDHIT\Jeff

Model       : Qosmio X505

OS          : Microsoft Windows 7 Ultimate

The error log file will be a list of computer names that I could troubleshoot, and when it is ready, I could use this file as my new source file.

Even though this is a beginner event, there are still a number of scripting best practices you should attempt to incorporate that I hope I’ve demonstrated in this solution.

Thank you, Jeffrey, for providing this solution to Beginner Event 5.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Error handling; I know someone who will be happy.  I was going to ask about how to use the -property which is what I got dinged on with my script but you show it nicely.  I'll definitely use that from now on; but still using notepad.

  • Serenity?  Is Mr. Hicks by any chance a Browncoat?

  • Wonderful script! Great, especially including error handling and writing to an error log is a good thing!

    The outline is quite easy to read and has a simple but efficient structure.

    Nothing to add to it! Except: It would be nice, if it had been presented in one piece at the end ... but: no problem with that!

    @cseiter: yepp! I'm happy :-)))

    @Tony: I had to google "Serenity" and "Browncoat" and still I don't know how "fireflies" are related to the script?!  

    kind regards, Klaus

  • Serenity is the name of the Firefly class cargo ship on the TV show Firefly.  Two of the main characters fought in a war together and were often referred to as 'Browncoats', because their uniforms had a browncoat.  The show has something of a cult following and diehard fans will sometimes refer to themselves as 'Browncoats'.  Because Mr. Hicks named his computer Serenity I wondered if he was a fan.

    There is also a movie called Serenity, which is based on the TV show.

    It has absolutely NOTHING to do with scripting. But, is has everything to do with the 'verse, the black and breaking atmo' ;)

  • @Tony: Thanks! I searched this page for "serenity" but didn't find it!

    It's part of the creenshot I realized now!

    So might be that you are right ....

    good night,

    Klaus

  • I am in fact a latecomer fan to the Firefly series. The Serenity name is actually in reference to the series as well as it's definition. In the past I've used a computername of CHAOS but thought I needed to relax a bit more.  But I'm really glad you found my commentary helpful.

  • Serious question time:

    Did you leave out the "foreach ($computer in $computers) {}" statement?  

    If I'm wrong and you don't need a foreach in your script could you elaborate?

  • I don't see where you can view the full solution. I'll post something on my blog.

  • I've posted my code for Beginner Event 5 plus an advanced variation http://bit.ly/e8JEEJ