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}},            
 @{l='IP-Address';e={$_.IPAddress}}            
)            
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 = "127.0.0.1","::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 powershell.org forums…and of course pratice writing scripts. The official Scripting Games will start in April but you can still subscribe to the powershell.org newsletter and participate in the Winter Scripting Games.

Advertisements

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