Expert Solution for 2011 Scripting Games Beginner Event 3: Use PowerShell to Get Information about Windows Event Logs

Expert Solution for 2011 Scripting Games Beginner Event 3: Use PowerShell to Get Information about Windows Event Logs

  • Comments 5
  • Likes

Summary: Windows PowerShell MVP, Kirk Munro, solves the 2011 Scripting Games Beginner Event 3 by getting information about event logs.

Microsoft Scripting Guy, Ed Wilson, here. Today we have Kirk Munro as our expert commentator for Beginner Event 3.

Photo of Kirk Munro

Kirk Munro is a four-time recipient of the Microsoft Most Valuable Professional (MVP) award for his deep involvement with the Windows PowerShell community. He has worked in the IT industry for more than 14 years, with the last four years focused 100% on Windows PowerShell and software solutions that use Windows PowerShell. He currently works at Quest Software as a product manager for their award winning PowerGUI product line.

When he's not working on PowerGUI, Kirk spends his time working with Windows PowerShell in the community. He is the president of the PowerShellCommunity.org site, a central hub for all things PowerShell. He is also a frequent blogger, and he presents at conferences, launch events, and user group events whenever he gets the opportunity.

Here is Kirk’s contact information: 
Blog: Poshoholic: Totally addicted to PowerShell
Twitter: http://twitter.com/Poshoholic
Linked-in profile: http://www.linkedin.com/in/kirkmunro
Personal profile: http://poshoholic.com/about

Worked solution

One of my favorite things about Windows PowerShell is that the scripting language itself allows you to ask questions about PowerShell, and once you learn a few basic tricks, you can get answers to your most common questions quickly and easily. This event is a prime example, and you’ll see how you can use this feature of Windows PowerShell to complete the event.

Let us start by breaking the event down into a series of steps so that we can start looking for the answers:

1. Get a list of all event logs (classic and event tracing logs) on the local system.

2. Identify properties on those event logs that allow you to determine the event log’s name, its size, whether it is a classic event log, whether the event log is enabled, and the last modification date of the event log.

3. Filter the list of event logs to only show those that are enabled.

4. Sort the results by the last modification date, in descending order.

5. Include only the event log name, the size of the event log, whether it is a classic event log, and the last modification date.

6. Separate the classic event logs from the event tracing logs.

7. Re-sort the results by log size first (descending), and then by last modification date (also descending).

8. Reconfigure the script to gather this data from 50 servers in your organization, and keep the different server data separate.

Steps 1 through 5 are the core tasks for this event that must be completed, and steps 6 through 8 are tasks that should help us get full bonus points. With the event broken down into a series of steps, we are ready to begin, so let us fire up our favorite editor and get started. I am going to use the Script Editor that comes with PowerGUI Pro.

Step 1

The first step requires us to know what command should be used to retrieving classic and event tracing logs by using Windows PowerShell. To find such a command, you should use the Get-Command cmdlet. Get-Command allows you to discover other commands on the system. Because it has to do with event logs, you should run a command like this:

Get-Command -Name Get-*Event*

This command will give you a list of all “Get” commands (those that are used to retrieve data from Windows PowerShell) that also have Event somewhere in their noun. Looking through the list of results, the most obvious candidate is Get-EventLog. If you use the Get-Help cmdlet to read documentation about Get-EventLog, you will see that Get-EventLog lists event logs, but not event tracing logs. Get-WinEvent is the actual cmdlet needed for this event, and you can verify that by running this command:

Get-Help Get-WinEvent –Full

The help information for Get-WinEvent describes a parameter that can be used to list the event logs and event tracing logs. By passing an asterisk to the ListLog parameter, it will list all logs. This is what we need for the first step, so we can now complete that step with this command:

Get-WinEvent -ListLog *

Step 2

Now that we have the event logs showing up in the console, we need to do a bunch of things with them. Everything returned from Get-WinEvent (or any other Windows PowerShell command you call) are objects with many properties that define them. In the case of Get-WinEvent (and many other PowerShell cmdlets), you are shown results with a default set of properties, but there are others that are available as well. To find out what properties are available on an object, you need to pass the results to the Get-Member cmdlet. Windows PowerShell uses pipelines to pass the results of one command into another. Simply pass the results of your Get-WinEvent call to Get-Member like this, and you will see a description of the properties (and methods) that are available on those objects:

Get-WinEvent -ListLog * | Get-Member

In the output of that command, you can see that there is a LogName property that identifies the name of the log, a FileSize property for the size, an IsClassicLog property to distinguish the type of log that is returned, a LastWriteTime property that indicates the last time the log was written to, and an IsEnabled property indicating if the log is enabled or not. These five properties will be used in steps three through eight to get the results needed to complete this event.

Step 3

Filtering results in Windows PowerShell is easy. When a cmdlet does not have an appropriate parameter to allow you to get a filtered result directly (for example, Get-WinEvent does not have an Enabled parameter to return only enabled event logs), you pass the results of your command to the Where-Object cmdlet to do the filtering. This is done again by using the pipeline. We can remove the call to Get-Member that we just used because that was only gathering information we needed, and it is not part of the report we want to generate here. We are going to replace that with a call to the Where-Object cmdlet.

The Where-Object syntax can be a little tricky the first time that you look at it. You pass it a script block, and inside the script block you define conditions that must pass for the objects to be returned. A special variable ($_) is used to represent the current object (one of the event logs returned by Get-WinEvent) that you are working with, and we need to check to see if its IsEnabled property is set. Here is the command you would use to perform this test:

Get-WinEvent -ListLog * | Where-Object {$_.IsEnabled}

The IsEnabled property is a Boolean value, which is what we need when checking a condition, so simply getting that Boolean value allows us to verify that the event log is enabled before outputting it in our report. This gives us a list of event logs and event tracing logs that are enabled, so we are finished with this step and well on our way to solving this problem.

Step 4

Now that we have our enabled event logs and event tracing logs, we want to sort the results to show those that use the last modified date with the most recent dates on top. This requires using the LastWriteTime property that we discovered earlier. We are going to sort by using that property, but in descending order to show the most recent dates first.

Sort-Object is a cmdlet that can be used to sort anything you get from Windows PowerShell. You simply pass the results you want to sort into Sort-Object by using the pipeline. If you read the documentation by using Get-Help, much like you did for Get-WinEvent in Step 1, you will see that Sort-Object can sort on multiple property values (good to know for the bonus points in this event), and by default it sorts in ascending order, but there is a Descending parameter to allow you to sort in descending order.

We identified earlier that LastWriteTime is the property that contains the last time the event log file was written to, so our command to sort our results using LastWriteTime in descending order looks like this:

Get-WinEvent -ListLog * | Where-Object {$_.IsEnabled} | Sort-Object -Property LastWriteTime -Descending

This gives us the sorted results we need, so we’re almost finished the core requirements for this event. The last thing we need to do is make sure we have the right properties in our report.

Step 5

To create a report with specific properties, Windows PowerShell has a set of Format cmdlets available. Format-List is used to show the results in list format, and Format-Table is used to show the results in tabular format. We want a table for our output, so we simply need to pipe our sorted results to Format-Table and specify the properties we want to see in the order that we want to see them. For this event, we want to see LogName first, then FileSize, then IsClassicLog, and last LastWriteTime. Here is our command, modified to create a formatted table with the results we need:

Get-WinEvent -ListLog * | Where-Object {$_.IsEnabled} | Sort-Object -Property LastWriteTime -Descending | Format-Table -Property LogName, FileSize, IsClassicLog, LastWriteTime

With this command, we have completed the core requirements for the event and we can stop here. There are a number of bonus design points to be had though, and they are not difficult to get, so let us see how we can modify this command to get all bonus points that are available.

The first bonus design point mentions running this command against a remote machine. As a rule, I always work out a command completely on a local machine first before I run it remotely, so we will tackle the remote machine execution in step 8. The next bonus design points talk about doing more sorting in the report. This is useful, but before we do that, we need to know what we want our final report to look like (the last bonus design points), so I am going to tackle the bonus design points in reverse order.

Step 6

One of the bonus points available is to improve the presentation of the output of the report. If you sort your data correctly, you can use Format-Table to create a grouped tabular output that can make it easier to consume information. For example, I think it is useful to see the results for classic event logs and event tracing logs separately. This can be achieved by first sorting using the IsClassicLog property, and then updating Format-Table so that it groups by the IsClassicLog property instead of outputting it in the table itself.

Sorting on additional properties is straightforward. You simply use multiple properties in the Property parameter of Sort-Object. Here is how you can sort your results first by IsClassicLog, then by LastWriteTime:

Get-WinEvent -ListLog * | Where-Object {$_.IsEnabled} | Sort-Object -Property IsClassicLog, LastWriteTime -Descending | Format-Table -Property LogName, FileSize, IsClassicLog, LastWriteTime

When you have the classic event logs separated from the tracing event logs, you can add grouping to your table to group classic logs together, and remove the IsClassicLog property from the table itself. Format-Table has a GroupBy parameter to allow you to group results in your tabular output by a specific property. Here is our command, modified this time to group the results by the IsClassicLog property:

Get-WinEvent -ListLog * | Where-Object {$_.IsEnabled} | Sort-Object -Property IsClassicLog, LastWriteTime -Descending | Format-Table -GroupBy IsClassicLog -Property LogName, FileSize, LastWriteTime

With this output we’re starting to see how easy it is to get nicely formatted results by using simple modifications to Windows PowerShell commands. Now we can move on to the other bonus design points.

Step 7

We are already sorting using two properties, but one of the bonus design points suggests that we should also sort by FileSize. Personally, when looking at event log data like this, I prefer to see the event log size in descending order, and then for multiple event logs of the same size, I would like to look at the last modification date in descending order. Then if I’m concerned with log files being too big, I can look at when they were written to in order to determine what to do about their size. To accomplish this I want to add FileSize to the list of properties to sort on, just before LastWriteTime. Here’s what the modified command looks like:

Get-WinEvent -ListLog * | Where-Object {$_.IsEnabled} | Sort-Object -Property IsClassicLog, FileSize, LastWriteTime -Descending | Format-Table -GroupBy IsClassicLog -Property LogName, FileSize, LastWriteTime

Now that we are at this point, we have done everything we need to do locally, so the last step is to scale this out to run on our 50 servers.

Step 8

Remember earlier when I mentioned that I’m using the Script Editor in PowerGUI Pro for this task? There is a reason for that. PowerGUI Pro’s Script Editor comes with a very powerful feature called Easy Remote Script Execution. This feature allows me to take any script and run it remotely on as many systems as I like. It also allows me to specify what proxy credentials to use if they are necessary, which ports to use, whether or not I should use SSL, what credentials I want to use, whether I want to run the remote task as a background job, and other more advanced security and configuration settings. It requires that Windows PowerShell 2.0 remoting is enabled on the systems where you want to run commands remotely. For this solution, I’ll assume that you have already added the Windows PowerShell feature to the Windows Server 2008 systems, and that you have called Enable-PSRemoting on all of the Windows Server 2008 and Windows Server 2008 R2 systems that you’re working with.

Right now we have a one-liner (a single line of script composed of multiple commands that are called in sequence in a pipeline) that does all of the necessary work, and we want to run that one-liner remotely on our servers. With PowerGUI Pro, you accomplish this by doing the following:

1. If you do not have the one-liner in a script file already, create a new unsaved script file, and copy your one-liner from step 7 into this file.

2. Select Debug | Run on Remote System. This will open the Run on Remote System dialog box. Note that there are some predefined configurations, but none for all Windows Server 2008 servers in your environment, so we’ll create one. Here is what the Run on Remote System dialog box looks like with the default remoting configurations:

Image of dialog box

3. Click the New… button to create a new remoting configuration. This displays the remoting configuration dialog box. Fill in the name, description, and script that will be used to retrieve the servers in your environment directly from Active Directory (AD). I use the Quest AD cmdlets for this because they have been around for a while, and in my opinion, they provide a better scripting experience than the Microsoft AD cmdlets. Here’s what the populated dialog box looks like:

Image of dialog box

The script that is used to retrieve the computer names dynamically loads the Quest AD cmdlets if they are not already loaded, and then it calls Get-QADComputer to retrieve any computer with an OSName that matches the ‘Windows*Server*’ wildcard pattern. It passes the computers that are returned to Select-Object, which extracts only the server name (remoting requires server names, not server objects).

For this solution, we’ll assume that you’re running as a user who has administrative permissions for the servers in your environment and that we only want to use the default configuration settings. You could modify this configuration to use alternate credentials though, or SSL, or different ports, certificates, proxy servers, etc., depending on the way you have configured remoting in your environment.

When you have finished entering this information, click OK to save the remoting configuration. This configuration will now be available to you for any remoting tasks you want to perform on your servers.

4. Now that you have the remoting configuration to create your server names, select the new remoting configuration, and then click the View Remoting Script button to generate the script that will get your event log and event tracing log data for all of the servers in your environment. Here is the script that is generated when you do this:

$scriptBlock = {

      Get-WinEvent -ListLog * | Where-Object {$_.IsEnabled} | Sort-Object -Property IsClassicLog, FileSize, LastWriteTime -Descending | Format-Table -GroupBy IsClassicLog -Property LogName, FileSize, LastWriteTime

}

 

[String[]]$computerNames = @()

$computerNameLookupScript = {

      Add-PSSnapin Quest.ActiveRoles.ADManagement `

      -ErrorAction SilentlyContinue

      Get-QADComputer -OSName 'Windows*Server*' |

      Select-Object -ExpandProperty Name

}

foreach ($item in & $computerNameLookupScript){

      if (Test-Connection -ComputerName $item -Count 1 -Quiet) {

            $computerNames += $item

      }

}

 

if (-not $computerNames) {

      throw 'No computer names found. You must have at least one computer for your remote command to run against.'

}

 

$connectionUri = @()

foreach ($computerName in $computerNames) {

      $connectionUri += "http://${computerName}:5985/WSMAN"

}

 

Invoke-Command -ScriptBlock $scriptBlock -ConnectionURI $connectionUri -ThrottleLimit 15

 

This script is quite long, and with good reason. Running commands on remote systems can be complicated, especially if you want to make sure that you only run against systems that respond to ping, or if you want to use advanced security or configuration settings. The Easy Remote Script Execution feature handles all of this for you, allowing you to create configurations with the appropriate settings and then either generate scripts (which you could then schedule as nightly tasks) or invoke commands immediately by using those scripts.

5. If you run the script that was generated, you will get all enabled event log and event tracing log data for each of the servers, but you will not have anything indicating which of those results are associated with the specific computers. You can accomplish this, but it requires additional work. To accomplish this, we need to pull our sorting (the Sort-Object call) and formatting work (the Format-Table call) outside of the script that we are invoking remotely so that all sorting and formatting work is done on the client computer. Then we can add one additional property to our sorting and grouping commands to ensure that we can easily identify the computer name that the results are associated with. First, we move the Sort-Object and Format-Table portion of the pipeline to after our call to Invoke-Command. This is a simple cut and paste operation, and the end result looks like this:

 

$scriptBlock = {

      Get-WinEvent -ListLog * | Where-Object {$_.IsEnabled}

}

 

[String[]]$computerNames = @()

$computerNameLookupScript = {

      Add-PSSnapin Quest.ActiveRoles.ADManagement `

      -ErrorAction SilentlyContinue

      Get-QADComputer -OSName 'Windows*Server*' |

      Select-Object -ExpandProperty Name

}

foreach ($item in & $computerNameLookupScript){

      if (Test-Connection -ComputerName $item -Count 1 -Quiet) {

            $computerNames += $item

      }

}

 

if (-not $computerNames) {

      throw 'No computer names found. You must have at least onecomputer for your remote command to run against.'

}

 

$connectionUri = @()

foreach ($computerName in $computerNames) {

      $connectionUri += "http://${computerName}:5985/WSMAN"

}

 

Invoke-Command -ScriptBlock $scriptBlock -ConnectionURI $connectionUri -ThrottleLimit 15 | Sort-Object -Property IsClassicLog, FileSize, LastWriteTime -Descending | Format-Table -GroupBy IsClassicLog -Property LogName, FileSize, LastWriteTime

 

With this step completed, we need to add an additional property to the Sort and Format grouping. Whenever you make a remoting call to Invoke-Command, a PSComputerName property is added to the results to allow you to determine which computer the objects came from. This property can be added to our Sort-Object property set and it can also be used in the GroupBy parameter in Format-Table to create a custom grouping. This gives us a list of all enabled event logs, sorted first by PSComputerName, then by the other properties, and with results that are grouped by PSComputerName and IsClassicLog. The tricks here are:

a) We want computer names sorted in ascending order, and we’re sorting all other fields in descending order, so we need to use a hashtable for that property to handle that exception to our default sort order.

b) We want to group by multiple values, which requires a custom grouping definition.

c) We want to customize the format output so that we can see as much data as possible.

When you get into customizing formatting, there are a ton of things you can do with the format cmdlets to create nice looking reports, including using custom column names, custom group names, custom alignments, and widths. You can customize the data values as well. In my final script, I did a lot of this to show you some of the capabilities, and the kind of results that you can achieve. Describing how these work is beyond the scope of this very long solution, but I wanted to show you the example as food for thought as you continue working with Windows PowerShell. Here’s the script with the additional changes I added to it:

 

$scriptBlock = {

      Get-WinEvent -ListLog * | Where-Object {$_.IsEnabled}

}

 

[String[]]$computerNames = @()

$computerNameLookupScript = {

      Add-PSSnapin Quest.ActiveRoles.ADManagement `

      -ErrorAction SilentlyContinue

      Get-QADComputer -OSName 'Windows*Server*' |

      Select-Object -ExpandProperty Name

}

foreach ($item in & $computerNameLookupScript){

      if (Test-Connection -ComputerName $item -Count 1 -Quiet) {

            $computerNames += $item

      }

}

 

if (-not $computerNames) {

      throw 'No computer names found. You must have at least onecomputer for your remote command to run against.'

}

 

$connectionUri = @()

foreach ($computerName in $computerNames) {

      $connectionUri += "http://${computerName}:5985/WSMAN"

}

 

$sortPropertyDefinitions = @(

      @{

            Expression={$_.PSComputerName}

            Ascending=$true

      },

      'IsClassicLog',

      'FileSize',

      'LastWriteTime',

      @{

            Expression={$_.LogName}

            Ascending=$true

      }

)

$tableGroupingDefinition = @{

      Name='Group'

      Expression={'{0} logs on ''{1}''' -f $(if ($_.IsClassicLog) {'Classic event'} else {'Event tracing'}),$_.PSComputerName}

}

$tablePropertyDefinitions = @(

      @{

            Name='Name'

            Expression={$_.LogName}

            Width=85

      },

      @{

            Name='Size'

            Expression={'{0:0.##} KB' -f ($_.FileSize / 1KB)}

            Alignment='Right'

            Width=10

      },

      @{

            Name='LastModificationDate'

            Expression={$_.LastWriteTime}

            Width=24

      }

)

Invoke-Command -ScriptBlock $scriptBlock -ConnectionURI $connectionUri -ThrottleLimit 15 | Sort-Object -Property $sortPropertyDefinitions -Descending | Format-Table -GroupBy $tableGroupingDefinition -Property $tablePropertyDefinitions

 

If you run this script, you’ll get back results that look something like this:

 

  Group: Classic event logs on 'Exchange01'

 

Name                      Size LastModificationDate

----                      ---- --------------------

Security                  20484 KB 10/03/2011 9:11:32 AM

System                    6212 KB 23/03/2011 7:15:48 AM

Application                 5188 KB 15/03/2011 4:12:04 PM

Windows PowerShell              4164 KB 28/03/2011 2:12:37 PM

OAlerts                   1028 KB 10/03/2011 9:53:08 AM

HardwareEvents                 68 KB 29/01/2011 12:02:28 PM

Internet Explorer               68 KB 29/01/2011 12:02:28 PM

Key Management Service             68 KB 29/01/2011 12:02:28 PM

Media Center                  68 KB 29/01/2011 12:02:28 PM

 

 

  Group: Event tracing logs on ' Exchange01'

 

Name                      Size LastModificationDate

----                      ---- --------------------

Microsoft-Windows-GroupPolicy/Operational  4100 KB 22/03/2011 6:57:42 AM

Microsoft-Windows-PowerShell/Operational   2116 KB 31/03/2011 11:49:07 AM

Microsoft-Windows-ParentalControls/Opera... 1092 KB 04/02/2011 9:13:20 PM

Microsoft-Windows-Application-Experience... 1028 KB 30/03/2011 9:12:26 PM

Microsoft-Windows-Diagnosis-PCW/Operatio... 1028 KB 29/03/2011 1:32:11 PM

Microsoft-Windows-WLAN-AutoConfig/Operat... 1028 KB 29/03/2011 1:22:44 PM

Microsoft-Windows-NlaSvc/Operational     1028 KB 11/03/2011 11:22:30 AM

Microsoft-Windows-HomeGroup Provider Ser... 1028 KB 11/03/2011 8:59:08 AM

Microsoft-Windows-Diagnostics-Performanc... 1028 KB 10/03/2011 9:32:39 PM

Microsoft-Windows-Dhcpv6-Client/Admin    1028 KB 10/03/2011 9:32:23 PM

Microsoft-Windows-Application-Experience... 1028 KB 10/03/2011 12:38:33 PM

Microsoft-Windows-WindowsUpdateClient/Op... 1028 KB 10/03/2011 9:19:13 AM

Microsoft-Windows-Known Folders API Serv... 1028 KB 10/03/2011 9:15:58 AM

Microsoft-Windows-Bits-Client/Operational  1028 KB 10/03/2011 9:13:13 AM

Setup                    1028 KB 10/03/2011 9:12:19 AM

Microsoft-Windows-Windows Firewall With ... 1028 KB 10/03/2011 9:12:15 AM

Microsoft-Windows-Diagnosis-DPS/Operatio... 1028 KB 10/03/2011 9:12:15 AM

Microsoft-Windows-NetworkProfile/Operati... 1028 KB 10/03/2011 9:12:15 AM

 

Well there you have it, a script to remotely retrieve all event log data for classic and event tracing logs from all the servers running Windows that are online in your environment. Building the basic script like we did in steps 1 through 5 is really easy. Adding remoting support for a dynamic list of computers when you may need to consider how remoting security is configured in your environment is of moderate difficulty, and a great program like PowerGUI Pro with its Easy Remote Script Execution feature comes in really handy in these cases so that you don’t have to create all of the necessary script from scratch. Customizing sorting and formatting in Windows PowerShell can be more complicated, but the built in Sort-Object and Format-Table cmdlets are quite flexible, allowing you to get results that are easy to read, save to a file, send via email, etc.

This script could absolutely have been accomplished much more simply if you were working with a hardcoded list of computer names; if you didn’t care about errors from computers that were offline; if you were running in an account that has the proper permissions or in an environment where you don’t need to consider security configuration, SSL, ports, certificates, etc.—essentially, in the most simple use case where you don’t need to think about any of that. In production environments, scripts are actually important and security is not just something that others think about. But that is often not the case, so I wanted to show you how you would build a script if you had to deal with all those things.

If you made it this far, congratulations! This is a long solution to a beginner event. Do not be worried if you did not understand all of the concepts described here. Simply coming across the more complex things that are done in this script once will make it easier next time. Something I think is key, though, is making sure that you are using the right tools to make it easier for you.

Thanks for reading! Kirk out.  

We want to thank Kirk for his solution to Beginner Event 3.

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
  • <p>Excellent!</p> <p>Very good explanantions and I learned that Format-Table can do more than I thought up to now!</p> <p>Thoughtful introduction into the solution ... great!</p> <p>But still ... error handling is not provided ... my all time favorite complain :-)))</p> <p>Best solution so far!</p> <p>kind regards, Klaus (Schulte)</p>

  • <p>It&#39;s a very good script, my only thought would be that it seems to rely on the quest snap-in, and your add-pssnapin fails silently if it&#39;s not there, so running the script fails on a system where those don&#39;t exist. Seems like you should catch that error and potentially let the user know where they can go to get them.</p> <p>I also seem to recall the scripting guy mentioning that as one of the things you should do in writing your own scripts that require additional components.</p>

  • <p>Klaus: Thanks for the positive feedback. You can do a ton with Format-Table, it&#39;s quite versatile! I&#39;m curious where you feel that error handling is lacking. If you&#39;re referring to the script block that is used to look up the computer names, you&#39;re right, that could be done better. It uses SilentlyContinue for the ErrorAction parameter so that it succeeds if the snapin is already loaded. I chose that route because if it is not loaded or installed, you&#39;ll get an error about the missing command (Get-QADComputer). It would be better if it were designed to return an error if the snapin was not installed though, telling users where they can go to get it (which is <a rel="nofollow" target="_new" href="http://quest.com/PowerShell">http://quest.com/PowerShell</a> for anyone who was curious about that). Other than that I felt the script was written sufficiently well to handle unexpected situations appropriately, using Test-Connection to verify which computers were online, and throwing an exception if no online computers could be found.</p> <p>Jeff: I agree, it would be better if it were to tell users where to get the snapin (see previous comments). Since the Quest AD cmdlets are pretty well known, I was taking it a little for granted. I&#39;ll do this differently next time.</p>

  • <p>Dear Kirk,</p> <p>one last word on error handling! </p> <p>If I issue Invoke-Command with -ComputerName or as you do using a -ConnectionUri to a remote machine, I nearly always see errors popping up!</p> <p>You know that I&#39;m no admin and Invoke-Command on my neighbourÄ&#39;s computer might fail miserably!</p> <p>That&#39;s my point ... do you aggree?</p> <p>Nice to see your long comment ... you do really take care of us! Seriously ... I like that!</p> <p>Thanks, Klaus</p>

  • <p>I agree with you Klaus, if the last call to Invoke-Command fails, the error you receive will often not be very helpful. That&#39;s a general problem that PowerShell remoting has. I could probably determine the most common errors that would show up in this case and provide useful tips on what to do, but that seems a little beyond the scope of this article to me. Personally I&#39;m hoping that the PowerShell team gives better error output from Invoke-Command when it leverages PowerShell remoting to let you know what failed and to give you some prescriptive guidance for some of the things that you should check to ensure that remoting is configured correctly. Either that or maybe a Test-RemoteCommand cmdlet to perform such a test explicitly and give you detailed output describing the tests being performed and the results of those tests along with possible actions you can take to resolve any issues. I&#39;ll think about this some more since I know I&#39;ll be using Invoke-Command more and more frequently going forward, and this is definitely something that I need to be concerned with for PowerGUI PowerPacks that require remoting to work correctly. Thanks for getting me to start paying more attention to this! :)</p>