Advanced Windows PowerShell Scripting Video Training

Advanced Windows PowerShell Scripting Video Training
Advanced Windows PowerShell Scripting Video Training

Monday, October 24, 2016

How to See a Cmdlet’s Code

This last week I had the privilege of speaking at the PowerShell Summit Asia in Singapore.  After an 11 hour day of speaking, you better believe that I had a lot of attendees asking me questions over the following 2 days.  A really good one that stuck in my head involves how to see the code that was used to write a cmdlet.  Hey, I teach PowerShell so this is a good one.

PowerShell cmdlets are actually either a “cmdlet” or a “function”.

PS C:\> Get-Command -Name Get-Process, Get-SMBShare

CommandType     Name            Version    Source                                                                               
-----------     ----            -------    ------                                                                               
Cmdlet          Get-Process    Microsoft.PowerShell.Management                                                      
Function        Get-SmbShare    smbshare                                                                             

Cmdlets are actually created from C# while functions are created with PowerShell.  Since C# is compiled we cannot crack them open to see what makes them tick without some extra software.  However, you can very easily see the code from a function

PS C:\> Get-Command -Name Get-SMBShare | Select-Object -ExpandProperty ScriptBlock | Out-File -FilePath ##Someplace to put it "

Set the –FilePath to a txt or PS1 file so you can look at the code.

It is no secret that I advocate to practice of “never reinvent the wheel”.  In other words if you need to know how a function did something, just look at the code.  

Friday, October 14, 2016

A Good Reason to Take an Instructor Led Class

I am doing my final checkout of my code for my evening session with Jaap Brasser for next week’s PowerShell Conference Asia.  Microsoft released Windows Server 2016 last week and all of us are rebuilding our test environments and validating our code with the final product.  While doing my thing, I received an email notification from the  IDERA community from someone new to PowerShell.  They were having some difficulty getting the distinguished names of their clients from Active Directory.  First off, for someone who is new to PowerShell, the code they posted is intense:

Function Get-ComputerFromAD
  ForEach ($Domain in $Global:ADDomains)
   $LdapPath = "LDAP://OU=Computers,OU=GPWS,DC=$Domain,DC=xxx,DC=com"
   [System.DirectoryServices.DirectorySearcher]$Searcher = $LdapPath
   $Searcher.Filter = "(&(objectCategory=computer)(cn=$ComputerName))"
   $Searcher.SearchRoot = $LdapPath
   $Searcher.SearchScope = "Subtree"
   $Searcher.PageSize = 1000
   $PropertiesToLoad = "objectClass","cn","description","distinguishedName","operatingSystem","operatingSystemVersion","operatingSystemServicePack","dnsHostName","lastLogonTimeStamp","whenCreated"
   foreach ($property in $PropertiesToLoad) { $Searcher.PropertiesToLoad.Add($property) | Out-Null }
   $ADResult = $Searcher.FindOne()
   if ($ADResult -ne $null)
    return $ADResult
  return $null

That is really awesome for someone who is new.  The problem is that this wheel has already been invented.  To get the distinguished name of a client in Active Directory, you simply need to use the Active Directory Module for Windows PowerShell.  Here is the command:

 Get-ADComputer -filter * | Select-Object -ExpandProperty DistinguishedName

After 5 days of my PowerShell classes, I routinely get comments like:
  • ·         I would have never figured that out on my own.
  • ·         I read the entire book and could not understand it until I listened to you.
  • ·         I’ve tried to understand this for a month, you taught it to me in an hour.

The internet is great, but you just cannot beat an interactive class with an instructor who knows there stuff.  Give your local Microsoft Training Partner a call and ask them to place you in a  PowerShell class in your area. If you want me to be your instructor, just ask them to contact me.  Let me help you invest your time wisely and get you well on your way to being a PowerShell rock star.  

That command took me 10 seconds.  Image the time that I can save you.

Thursday, October 13, 2016

Avoid Displaying Information

When teaching PowerShell, I use Write-Host often just to display what is going on.  It may be to show the result of an IF or SWITCH construct.  Maybe to let us know that a value incremented.  In practice, I avoid placing text on the screen unless the user specifically asks for it. Write-Host places information directly onto the screen.  Write-Verbose and Write-Information places information in a data stream that eventually ends up on your screen.  This is bad for a few reasons.

First of all, the extra screen candy can be frustrating to watch.  Second, it takes time to write anything to the screen.  This is never good.  You should only display information if necessary and give the user a chance to suppress that information.  I decided to do a little test between Write-Host, Write-Verbose, Write-Information, and displaying nothing.  The code below loops 100 times for each command and again where nothing is performed in the loop.

Function Test-DisplayHost
    For($X = 0 ; $X -lt 100 ;$X++)
        Write-Host "Write-Host $X" -ForegroundColor green

Function Test-DisplayVerbose
    For($X = 0 ; $X -lt 100 ;$X++)
        Write-Verbose "Write-Verbose $X" -Verbose

Function Test-DisplayInformation
    For($X = 0 ; $X -lt 100 ;$X++)
        Write-Information "Write-Information $X" -InformationAction Continue

Function Test-NoDisplay
    For($X = 0 ; $X -lt 100 ;$X++)

Write-Host "Testing Write-Host" -ForegroundColor Cyan
$WriteHost = Measure-Command -Expression {


Write-Host "Testing Write-Information" -ForegroundColor Cyan
$WriteVerbose = Measure-Command -Expression {


Write-Host "Testing Write-Information" -ForegroundColor Cyan
$WriteInformation = Measure-Command -Expression {


Write-Host "Testing Nothing" -ForegroundColor Cyan
$WriteNoting = Measure-Command -Expression {


Write-Host "Write-Host        | $($WriteHost.Ticks)"
Write-Host "Write-Verbose     | $($WriteVerbose.Ticks)"
Write-Host "Write-Information | $($WriteInformation.Ticks)"
Write-Host "Write nothing     | $($WriteNoting.Ticks)"

Here is the final output:

Write-Host        | 1167467
Write-Verbose     | 400858
Write-Information | 599326
Write nothing     | 6445

Two things that I took note of when running this code multiple times. The first is Write-Host always took longer.  The second, not displaying anything is always faster. 

The lessons learned here is to use Write-Verbose and Write-Information.  This allows the user to call the extra screen candy if they want, and suppress it when they do not.  Remember that Write-Information is a PowerShell 5 cmdlet.

Wednesday, October 12, 2016

Using Start-Job with Azure

Today I am like a kid playing with Legos. I’m prepped and ready for the PowerShell Conference in Asia so I’m working on Plan B.  Wait, what?  I just said that I was prepped and ready.  I am, but that does not mean that I am going to rest.  I am delivering a workshop on learning PowerShell, but we are using the attendee’s local computer.  My Plan B is to provide everybody with a virtual environment to play in for the day using Azure. 

My goal is to take everyone’s email address and create a VM in Azure for them.  I want to provision them live.  The problem with this is that if I do sequentially, it will take more than 4 hours to complete.  So, it is time to use background jobs.  When done sequentially, the task completes without any problems.  When performed using Start-Job, my Azure credentials are not passed into the ScriptBlock parameter of Start-Job.  

Azure team member Mark Cowlishaw published a fix for this issue on GitHub.  Here is his solution:

$path = ".\profile.json"
$deploy = {
    Select-AzureRmProfile -Path $path

Save-AzureRmProfile -Path $path

$job = Start-Job -ScriptBlock $deploy
Wait-Job $job
Receive-Job $job

To show you how to use it in practice, here is my test code to remove all of the Azure resources that each test run creates.

$path = "$env:USERPROFILE\documents\WindowsPowerShell\profile.json"
$Number = 18

For ($X = 0 ; $X -lt $Number + 1; $X++)
    $name = "NC"+$X
    Start-Job -ArgumentList $Name, $Path `
              -Name $Name `
              -ScriptBlock {
    Param ($Name, $Path)
    Select-AzureRmProfile -Path $path
    Remove-AzureRmResourceGroup -Name $Name -Force -Verbose

A slightly different implementation than what Mark proposed, but only because some of my code is very large.  In short, it works.  I am about to take a sequential 4 hour + build of the Virtual Machines and reduce this task to about 16 minutes with background jobs.

I may just have an awesome opening demonstration of automation at the beginning of my presentation.  See you all next week from Singapore!

Tuesday, October 11, 2016

A New Trick with the Backtick

I know, I’m opening this can of worms again.  You either love or hate the backtick in PowerShell.  The little thing is so awesome, but so hard to see.  It is no secret that I use it for line continuation.  I have a set of rules that I follow to allow me to use it without issues.  Here they are. 

At the end of the line:
o   Press SPACE
o   Press the BACKTICK
o   Press ENTER
o   Indent the first continued line
o   Do not use it on the last line of the command

Here is a command without line continuation:

New-ADFineGrainedPasswordPolicy -Name "Group1PSO" -Precedence 10 -ComplexityEnabled $True -Description "PSO for Group 1" -DisplayName"Group1PSO" -LockoutDuration "0.12:00:00" -LockoutObservationWindow "0.00:15:00" -LockoutThreshold 3 -MaxPasswordAge "10.00:00:00" -MinPasswordAge "1.00:00:00" -MinPasswordLength 8 -PasswordHistoryCount 10 -ReversibleEncryptionEnabled $False

Here is the same command following my rules.

New-ADFineGrainedPasswordPolicy -Name "Group1PSO" `
 -Precedence 10 `
 -ComplexityEnabled $True `
 -Description "PSO for Group 1" `
 -DisplayName "Group1PSO" `
 -LockoutDuration "0.12:00:00" `
 -LockoutObservationWindow "0.00:15:00" `
 -LockoutThreshold 3 `
 -MaxPasswordAge "10.00:00:00" `
 -MinPasswordAge "1.00:00:00" `
 -MinPasswordLength 8 `
 -PasswordHistoryCount 10 `
 -ReversibleEncryptionEnabled $False

I need to add another one.  Based on teaching PowerShell so many times, I need to make a change to the colors that I use in the PowerShell ISE and SAPIEN PowerShell studio.  I need to change the color of parameters so they are not so close to the color of a cmdlet.

Take a look at both of these. 
Changed parameter color
New-ADFineGrainedPasswordPolicy `
 -Name "Group1PSO" `
 -Precedence 10
 -ComplexityEnabled $True `
 -Description "PSO for Group 1"

New-ADFineGrainedPasswordPolicy `
 -Name "Group1PSO" `
 -Precedence 10
 -ComplexityEnabled $True `
 -Description "PSO for Group 1"

On the third line, I forgot to use a backtick.  On the example on the left, it is very hard to see the parameter on line 4 is not the correct color.  In the example on the right, it is. This makes it more obvious if you broke on of my rules and makes debugging easier.  These are the color settings that I am now using in my PowerShell classes.

PowerShell ISE:

In SAPIEN PowerShell Studio.
Click the Home tab and the Options on the far right.

I hope the PowerShell teams adds some type of splatting that allows for TAB completion.  That would get me to move away from the backtick.  But until then, I will continue to use it with my rules in place.  To learn more about line continuation in PowerShell, check out my blog article: Line Continuation in PowerShell – The Big Debate

Monday, October 10, 2016

Recording a Data in a Log

I’m wrapping up another PowerShell class right now in Fort Wayne, IN.  At the end of my classes, I like to do a “Project Day”.  I invite my class to do a project for themselves in our test environment so I can coach them.  In between coaching, I’m working on prepping my content for the PowerShell Conference in Asia next week.  When I work on my projects, I keep the content on the big screen to get my class to ask questions.

While working on the project in SAPIEN PowerShell Studio, I needed a way to format the date-time stamps on the internal log that I like to use when I’m coding a large project.  I noticed that what I was using was not sorting the way that I wanted it to based on the date-time information.  To fix this, I changed formats to YYYY-MM-DD HH:MM:SS.

 I took a look at the GetDateTimeFormats method of the System.DateTime object to see if I could generate this format easily.

PS C:\> (Get-Date).GetDateTimeFormats()

This produced a huge list of possibilities.  In order to use one, you need to specify the index number.

PS C:\> (Get-Date).GetDateTimeFormats()[10]
7 October, 2016

Well, there are 132 possible formats and I did not want to count so here is a simple function to help you decide which one you want to use.

Function Get-DateTimeFormat
    $Formats = $Date.GetDateTimeFormats()
    $Index = 0
    ForEach ($F in $Formats)
        $Obj = New-Object -TypeName PSObject -Property @{
            Index = $Index
            Format = $Date.GetDateTimeFormats()[$Index]
        Write-Output $Obj | Select-Object -Property Index, Format


Index Format                            
----- ------                            
    0 10/7/2016                         
    1 10/7/16                           
    2 10/07/16                          
    3 10/07/2016                         
    4 16/10/07                          
    5 2016-10-07                        
    6 07-Oct-16                         
    7 Friday, October 7, 2016           
    8 October 7, 2016                   
    9 Friday, 7 October, 2016           
   10 7 October, 2016           

Simple, but effective. Mine ended up being number 93. Here is a screen shot of where I used it.