How to improve your Powershell skills

Today, I’ve seen a link on twitter to a link on the technet gallery Get AD-Computer network settings

I’ve quickly said that and @mattifestation replied:

Matt is probably right but I want to take the opportunity to help Andrea and other powershell beginners to show how to improve your skills.

First, Powershell it’s much more than a Unix shell where you cut & replace strings (grep/awk/sed/…), it’s actually full object oriented.
To demonstrate this, let’s take the following subset of the code:

$NICs = Get-WmiObject -Namespace 'root\cimv2' -Class 'Win32_NetworkAdapterConfiguration' -Filter {IPEnabled='True'}            
$NICs | ForEach-Object {             
    $NIC_Entry = New-Object system.object            
    $NIC_Entry | Add-Member -Type NoteProperty -Name 'Interface Index' -Value ($_.Index) -Force             
    $NIC_Entry | Add-Member -Type NoteProperty -Name 'IP-Address' -Value $_.IPAddress -Force             
    $NIC_Entry | Add-Member -Type NoteProperty -Name 'Subnetmask' -Value $_.IPSubnet -Force             
    $NIC_Entry | Add-Member -Type NoteProperty -Name 'MAC-Address' -Value $_.MACAddress -Force             
    $NIC_Entry | Add-Member -Type NoteProperty -Name 'IP Default Gateway' -Value $_.DefaultIPGateway -Force             
return $NIC_Entry

First let’s achieve the same thing by typing less. Instead of using Add-Member, let’s use the properties argument of New-Object:

# Instead of Add-Member            
Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration' -Filter {IPEnabled='True'} | ForEach-Object -Process {            
    New-Object -TypeName PSObject -Property @{            
        'Interface Index' = $_.Index;            
        'IP-Address' = $_.IPAddress;            
        'Subnetmask' = $_.IPSubnet;            
        'IP Default Gateway' = $_.DefaultIPGateway            

Let’s do even less and still rename all properties:

# Using select and changing properties name            
$arofproperties = @(            
 @{label='Interface Index';expression={$_.Index}},            
Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration' -Filter {IPEnabled='True'} | Select -Property $arofproperties            

If we don’t mind about using default properties names, we can simply use the full power of the pipeline by selecting a subset of properties like this:

Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration' -Filter {IPEnabled='True'} | Select -Property Index,IPAddress,IPSubnet,DefaultIPGateway

Now, that we achieved the same thing, let’s plug some nice error handling on top of the code.
If you do a WMI query against remote computers, you absolutely need to do the following:

  1. Make sure to add a
    -ErrorAction Stop

    at the end of the Get-WMIobject cmdlet

  2. Put the whole statement in try/catch block

Hands on! Let’s say I’ve an array of computers.

$Computers = "","::1","localhost"

We can do:

$Computers | ForEach-Object -Process {            
    $Computer = $_            
    $allWMIproperties = $null            
    try {            
        $allWMIproperties = Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration' -Filter {IPEnabled='True'} -ErrorAction Stop  -ComputerName $Computer            
    } catch {            
        Write-Warning -Message "WMI query again computer $Computer failed because $($_.Execption.Message)"            
    if ($allWMIproperties) {            
        # It worked, so I can continue            

Last thing. Instead of talking about splatting which another topic, I’d like to mention that the Active Directory module is a feature that first needs to be added to a workstation or a server like this:

Add-WindowsFeature -Name RSAT-AD-PowerShell -Restart:$false -Verbose

Even loading a module can go wrong. You can even disable the automatically mounted AD: drive and on Windows 2012, you’ve now the module preloading feature…
While not being perfect especially if you disabled the automatically mounted AD: drive, I’m usually using the following code:

# First we load the Active Directory module if required            
if ((Get-Module -Name ActiveDirectory).Name -ne "ActiveDirectory") {            
    Write-Verbose -Message "Attempting to load Active Directory module for Powershell"            
    Import-Module -Name ActiveDirectory -ErrorAction SilentlyContinue            
    if ( (Get-PSDrive -PSProvider ActiveDirectory -ErrorAction SilentlyContinue).Name -ne "AD") {            
        Write-Warning -Message "Failed to load the ActiveDirectory Module"                    

If, you’re still reading at this point, you’ll probably think that the above tips aren’t sufficient. You’re right, it’s not enough. You should enroll in the powershell community, read blogs, books, watch videos, ask questions on forums…and of course pratice writing scripts. The official Scripting Games will start in April but you can still subscribe to the newsletter and participate in the Winter Scripting Games.


Leave a Reply

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

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

Google+ photo

You are commenting using your Google+ 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 )


Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.