My take on the Verified Effective self-assessment.

In the latest PowerShell.org TechLetter, Don released a slimmed-down version of a retired scenario for the Verified Effective exam, which is offered at the PowerShell Summits. I decided to type up a solution for it and share my thought process. If you haven’t worked through it yourself yet, I’d recommend doing that first.

The exam takes the form of a PowerShell transcript, which shows you the commands that someone entered at the console, and the output from those commands. Your job is to write a PowerShell function which will produce exactly the same output, if those same commands are run (with some minor exceptions, such as your computer’s name or serial number / etc.) Here’s the transcript for the sample self-assessment.

<#
**********************
Windows PowerShell transcript start
Start time: 20150602172842
Username  : COMPANY\Administrator 
Machine	  : WIN81 (Microsoft Windows NT 6.3.9600.0) 
**********************
Transcript started, output file is C:\example1.txt
PS C:\> help Get-CorpSysInfo
PS C:\> Get-CorpSysInfo -ComputerName win81

BIOSSerial                    ComputerName                                      SPVersion OSVersion                    
----------                    ------------                                      --------- ---------                    
VMware-56 4d 09 95 89 20 e... win81                                                     0 6.3.9600                     


PS C:\> Get-CorpSysInfo -ComputerName win81 -Protocol dcom

BIOSSerial                    ComputerName                                      SPVersion OSVersion                    
----------                    ------------                                      --------- ---------                    
VMware-56 4d 09 95 89 20 e... win81                                                     0 6.3.9600                     


PS C:\> Get-CorpSysInfo -ComputerName win81 -Protocol cim
Get-CorpSysInfo : Cannot validate argument on parameter 'Protocol'. The argument "cim" does not belong to the set 
"Dcom,Wsman" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command 
again.
At line:1 char:47
+ Get-CorpSysInfo -ComputerName win81 -Protocol cim
+                                               ~~~
    + CategoryInfo          : InvalidData: (:) [Get-CorpSysInfo], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Get-CorpSysInfo
 
PS C:\> Get-CorpSysInfo -ComputerName win81 -Verbose
VERBOSE: Attempting connection to win81 over Wsman
VERBOSE: Operation '' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_OperatingSystem'.
VERBOSE: Operation 'Enumerate CimInstances' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_BIOS'.
VERBOSE: Operation 'Enumerate CimInstances' complete.

BIOSSerial                    ComputerName                                      SPVersion OSVersion                    
----------                    ------------                                      --------- ---------                    
VMware-56 4d 09 95 89 20 e... win81                                                     0 6.3.9600                     


PS C:\> Get-CorpSysInfo -ComputerName win81,NOTONLINE -Verbose
VERBOSE: Attempting connection to win81 over Wsman
VERBOSE: Operation '' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_OperatingSystem'.
VERBOSE: Operation 'Enumerate CimInstances' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_BIOS'.
VERBOSE: Operation 'Enumerate CimInstances' complete.

BIOSSerial                    ComputerName                                      SPVersion OSVersion                    
----------                    ------------                                      --------- ---------                    
VMware-56 4d 09 95 89 20 e... win81                                                     0 6.3.9600                     
VERBOSE: Attempting connection to NOTONLINE over Wsman
New-CimSession : WinRM cannot process the request. The following error with errorcode 0x80090311 occurred while using 
Kerberos authentication: There are currently no logon servers available to service the logon request.  
 Possible causes are:
  -The user name or password specified are invalid.
  -Kerberos is used when no authentication method and no user name are specified.
  -Kerberos accepts domain user names, but not local user names.
  -The Service Principal Name (SPN) for the remote computer name and port does not exist.
  -The client and remote computers are in different domains and there is no trust between the two domains.
 After checking for the above issues, try the following:
  -Check the Event Viewer for events related to authentication.
  -Change the authentication method; add the destination computer to the WinRM TrustedHosts configuration setting or 
use HTTPS transport.
 Note that computers in the TrustedHosts list might not be authenticated.
   -For more information about WinRM configuration, run the following command: winrm help config.
At C:\Program Files\WindowsPowerShell\Modules\CorpTools\CorpTools.psm1:25 char:28
+                 $session = New-CimSession -ComputerName $computer -SessionOption ...
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [New-CimSession], CimException
    + FullyQualifiedErrorId : HRESULT 0x8007051f,Microsoft.Management.Infrastructure.CimCmdlets.NewCimSessionCommand
    + PSComputerName        : NOTONLINE
 
VERBOSE: Operation '' complete.
WARNING: Failed establishing Wsman session to NOTONLINE


PS C:\> 'win81','localhost' | Get-CorpSysInfo -Verbose
VERBOSE: Attempting connection to win81 over Wsman
VERBOSE: Operation '' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_OperatingSystem'.
VERBOSE: Operation 'Enumerate CimInstances' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_BIOS'.
VERBOSE: Operation 'Enumerate CimInstances' complete.

BIOSSerial                    ComputerName                                      SPVersion OSVersion                    
----------                    ------------                                      --------- ---------                    
VMware-56 4d 09 95 89 20 e... win81                                                     0 6.3.9600                     
VERBOSE: Attempting connection to localhost over Wsman
VERBOSE: Operation '' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_OperatingSystem'.
VERBOSE: Operation 'Enumerate CimInstances' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_BIOS'.
VERBOSE: Operation 'Enumerate CimInstances' complete.
VMware-56 4d 09 95 89 20 e... localhost                                                 0 6.3.9600                     


PS C:\> Get-CorpSysInfo -ComputerName win81,NOTONLINE -Verbose -Protocol dcom
VERBOSE: Attempting connection to win81 over dcom
VERBOSE: Operation '' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_OperatingSystem'.
VERBOSE: Operation 'Enumerate CimInstances' complete.
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, 
''namespaceName' = root\cimv2,'className' = Win32_BIOS'.
VERBOSE: Operation 'Enumerate CimInstances' complete.

BIOSSerial                    ComputerName                                      SPVersion OSVersion                    
----------                    ------------                                      --------- ---------                    
VMware-56 4d 09 95 89 20 e... win81                                                     0 6.3.9600                     
VERBOSE: Attempting connection to NOTONLINE over dcom
New-CimSession : The RPC server is unavailable. 
At C:\Program Files\WindowsPowerShell\Modules\CorpTools\CorpTools.psm1:25 char:28
+                 $session = New-CimSession -ComputerName $computer -SessionOption ...
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [New-CimSession], CimException
    + FullyQualifiedErrorId : HRESULT 0x800706ba,Microsoft.Management.Infrastructure.CimCmdlets.NewCimSessionCommand
    + PSComputerName        : NOTONLINE
 
VERBOSE: Operation '' complete.
WARNING: Failed establishing dcom session to NOTONLINE


PS C:\> Stop-Transcript
**********************
Windows PowerShell transcript end
End time: 20150602173025
**********************
#>

Now, some of that output is pretty detailed. One of the errors even has a partial line of code that you can copy and paste, showing that there are $session and $computer variables in play, and the New-CimSession command is being used. You can see that the information is coming from the Win32_OperatingSystem and Win32_BIOS classes. There’s a fair bit of verbose / warning output to duplicate (though as you’ll see, quite a bit of it is automatic.) We can see exactly what the ValidateSet options on the Protocol parameter should be, and that it should default to Wsman. As long as you’re familiar with how these features work already (which is basically what the exam is testing), that information should jump right out of the transcript.

I can tell you that if you’re running the latest versions of the PowerShell v5 preview, some of the output is going to look slightly different. In the current v5 version, PowerShell tries to auto-size the table output by briefly delaying the first output from displaying (for like a quarter of a second or something.) This means that where you see output interspersed with Verbose lines in the transcript, you might see a bunch of Verbose followed by a bunch of Output instead. That’s fine, don’t worry about it. (In the real exam, I believe they tell you exactly what version of PowerShell to be running, just in case. If you need to, create a VM without all your more recent goodies, in preparation for the test.)

So, far starters, here’s a gist link with how I would write this function if I didn’t have to try to exactly match this transcript: https://gist.github.com/dlwyatt/d4f7504c21afdd341473#file-attempt1-ps1

It’s pretty straightforward code for a function that supports multiple values to a parameter either on the command line or via the pipeline. You declare the parameter as an array (in this case, of strings), and then have a foreach loop in the function’s Process block. I put the call to New-CimSessionOption in the Begin block, since it only needs to happen once anyway. Inside the Process block’s foreach loop, there’s a Try block that contains the steps required to collect the data from a remote computer, and output a PowerShell custom object with the properties shown in the transcript. Any errors are output as non-terminating errors in the Catch block, and the session is cleaned up in the Finally block, if it was successfully opened. (Note that $session is set to $null just before the Try block; this is a good habit to have, when using that sort of cleanup code in a Finally block.)

In the real world, I’d be perfectly happy with that code. I’d just need to add a comment-based help block, and I’d be done. It already has a pretty decent amount of Verbose output from the cmdlets themselves.

However, in this case, the job is to match the transcript exactly, and after running those commands, I can see that it misses the mark in a few ways. This is where the attention to detail (and knowledge of some of PowerShell’s behavior, such as with error handling) kicks in, and I suspect that quite a few people might wind up failing here. Here’s what I noticed when running those commands with my original code:

  • There’s no Verbose output showing “Attempting connection to win81 over Wsman”, but the rest of the automatic Verbose output looks pretty good.
  • There’s no Warning output on failed connections.
  • The error output looks quite a bit different. Instead of showing that the error came from New-CimSession, it shows Get-CorpSysInfo instead. This is due to how I did my error handling, with the try/catch and Write-Error construct.
  • There’s a sneaky call to “help Get-CorpSysInfo” at the beginning of the transcript, with completely blank output. I have a suspicion that the original version of this exam (which I have not seen) actually included help output, and the test-taker would have to write a comment-based help block. However, in the interest of reproducing the sample transcript, I’ll try to find a way to make the blank output happen.

Adding calls to Write-Verbose and Write-Warning is easy enough. I don’t like having to change my error-handling habits to match a transcript, but oh well, that’s the task at hand. Here’s attempt #2: https://gist.github.com/dlwyatt/d4f7504c21afdd341473#file-attempt2-ps1

Now it’s just about done. By allowing the errors from New-CimSession to be displayed normally (rather than using -ErrorAction Stop, and calling Write-Error in the Catch block), the error output matches the transcript. The new calls to Write-Verbose and Write-Warning also match. The only thing left, really, is that blank “help” output.

I tried to get clever on that. I thought, maybe if I put this into a script module, and in the script module, created an en-US\Get-CorpSysInfo.help.txt file that was blank, maybe that would fool the help system. No such luck; it still just displays the default help output. I couldn’t think of a decent way of doing this… so I resorted to cheese. Wait for it:

function help { }

Yeah, that’s dumb. But it worked!

If I were taking the actual test, at this point, I’d put my code into C:\Program Files\WindowsPowerShell\Modules\CorpTools\CorpTools.psm1 (a path which you can see in the various bits of Error output).

Advertisements

About Dave Wyatt

Microsoft MVP (PowerShell), IT professional.
This entry was posted in PowerShell, Professional. Bookmark the permalink.

2 Responses to My take on the Verified Effective self-assessment.

  1. McAndersDK says:

    Just a note, the transcript dictate that the module need to be CorpTools and is located here: C:\Program Files\WindowsPowerShell\Modules\CorpTools\CorpTools.psm1

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s