The Surly Admin

Father, husband, IT Pro, cancer survivor

Powershell Best Practices – Surly Style

I have to be honest, I’m a bit of a perfectionist.  I like things neat and tidy, and I have to be honest, I see a lot of Powershell code being posted on the Intrawebs that drives me crazy.  I’ve kept my peace, my mouth has been shut and I let things slide. I have.  I haven’t said, wtf are you thinking?  How the hell is anyone supposed to know what this is doing?  I sure as hell ain’t gonna run it to see what happens!  But no more.  Time to put down here a couple of things I do in Powershell to try to make my code a little easier to access for the average admin.  Clearly you should be doing it too.  Surly Style.

Comment-Based Help

You know, I can live with almost everything else, but I really believe comment-based help should be on every script that you put out in the wild.  It’s not unusual for my comment-based help section to actually be larger than the actual script.  This is a good thing.  It’s not hard to document your script using this system, and because it follows a very familiar pattern you’ll soon get the hang of it and be cranking out good documentation with every script you do.  Read more about comment-based help here.  I’ve seen scripts come out at Poshcode.org that I literally had no idea what they did and there was no comments and the code is complex enough that I just don’t feel like trying to break it down and figure out what it’s doing.  So I skip it.  It might actually be something I would really find useful, but who has the time to reverse engineer these scripts?

Should I use Alias’?

It depends.  It comes down to the concept of scripting versus coding.  Scripting is when you’re just writing something out in the Shell trying to accomplish a goal.  Get some quick information on a user, maybe find out what groups they belong to and you don’t feel like firing up Active Directory Users and Computers.  When scripting I use alias’ extensively because I don’t want to type things out.  ? for Where, % for Foreach, etc.  The following line of code is a good example:

get-adgroup mygroup | get-adgroupmember | sort name | % {$_.name}

Quick and easy and I find out all the group members in MyGroup.  In fact, I used to use an 85 line vbScript to find out group memberships (it produced a text report and emailed it) and was able to replace that with the line above.  Modify that line, and add one line for the email and I could replicate the functionality completely (even improve on it a little).

But if I’m coding, that is I’m writing a script that’s performing a function I do repetitively, I never use alias’.  Why not?  The idea is that I’m always trying to write code with the next guy in mind.  If I publish the code publicly I have to assume that someone running the code may not know Powershell as well as I do and the extensive use of alias’ can make it pretty confusing.  But even if you don’t plan on publishing it out in the world, you have to keep your successor in mind.  Odds are you won’t be in your current job forever, or certainly past the life expectancy of Powershell, so write it with the guy who’s coming after you in mind.  He needs to be able to read the code and understand what it is you’re trying to do.

Indent Please

If you’ve been coding for a while, you already know to do this but I see a lot of Powershell that’s pretty arbitrary in their use of indenting.  This makes it just as difficult to read as not indenting at all.  Here’s an example of stuff I’ve seen:

If ($myvariable -eq $True) {
   Write-Host "It's true, really true!"
      } else {
         Write-Host "Yeah, not so true after all" }

Maybe it’s just me, but I find the above difficult to follow.  I know the way of writing the else clause above is kind of a Powershell standard out there but I really don’t like it.  If you have a scriptblock with a lot of lines of code in it, finding that ending brace can be difficult.  Here’s how I like to write it:

If ($myvariable -eq $True)
{   Write-Host "It's true, really true!"
}
Else
{   Write-Host "Yeah, not so true after all"
}

Notice how the If, Else, opening brace and closing brace are all on the same level.  This makes finding what’s in the true scriptblock and what’s in the else scriptblock very easy to spot.  You could go a step further and put the opening brace of the true scriptblock and the else scriptblock on a line all by itself, but I find this format to work for me.  Let’s go a step further and see what it looks like:

If ($myvariable -eq $true)
{   If ($myothervariable -eq $false)
    {   Write-Host "my true, other false"
    }
    Else
    {   Write-Host "my true, other true"
    }
}
Else
{   Write-Host "My true"
}

We have a nested If statement, with an Else clause.  With this style of indenting everything is on the same level so you can easily see which scriptblock belongs to what If statement.  I find this much easier to work with.  Of course, if you’ve followed my blog for a while, you may have noticed that the code I post here is rarely indented?  Unfortunately that’s an issue with WordPress, and I’m too lazy to go through and put the indents in after I copy and paste them in.  In fact, when using PowerGUI it copies all kinds of HTML code so I have to paste into Notepad first, then copy and paste again into WordPress.

Just as a tangent, you can always simply testing if something is true by using this notation:

If ($myvariable)
{   ...do something here...
}

This works for any boolean variable to test if it’s true.  But also works for any other variable to test if it has something, you won’t specifically know what is in it, but you know something’s there, and there are quite a few instances where that’s good enough.

Comments in Code

I talked about this in my Comments post a couple of weeks ago, but wanted to touch on it again here.  Should you comment in code?  Absolutely.  But don’t go too crazy either.  If you are writing a line of code, then a comment, then a line of code, then a comment etc you’re probably commenting too much.  What happens is the code actually disappears a bit in all of the comments.  Make a comment about what you’re doing at the top of a section then go to it.

Spell it out

As with most scripting languages, and Powershell is no different, there are usually many, many ways of doing things.  But when given the choice of using a fancy coding technique, or using a couple of extra lines to write things out pick the later.  Why?  It just makes the code easier to read.  In almost all cases any Powershell script you’re writing does not need to be written to squeeze every ounce of performance out of it that you can, and if that’s the case for your script go ahead and slow it down a few thousand milliseconds by writing something out.  This is going to be true with the pipeline.  Now, the pipeline is hugely important in Powershell and you can’t–and shouldn’t–avoid it but consider if it’s really necessary all the time.  In fact, sometimes avoiding it can actually improve the performance of your script quite a bit, read here.

Also, try to keep it in the family.  Yes, Powershell is fully .Net capable, and can use the type accelerators for just about anything.  Here’s an example:

[datetime]::Now.AddMinutes(-10)

Versus

(Get-Date).AddMinutes(-10)

Which should you use?  The [datetime] type accelerator is actually a .Net object and is very fast.  If you use your Measure-Command cmdlet you’ll find the [datetime] accelerator is much faster than Get-Time (I believe this is because Get-Time is getting and calculating all kinds of information).  In fact, the [datetime] accelerator made the calculation in about a quarter of a millisecond.  Get-Time took a whopping (so need a sarcasm font!) 1.5 milliseconds.  So should you avoid Get-Time?  I don’t think so, in fact using Get-Time is preferrable to [datetime].  Why?  Well, it’s a more Powershelly way of doing things for one, but try this command and let me know how it worked:

Get-Help [datetime] -Full

Yeah, I didn’t get anything either.  But run the same command on Get-Help and you get quite a bit, huh?  Then try “Get-Help | gm” and you get even more (to be fair “[datetime]::now | gm” yields quite a bit too).  But that’s what I mean by spelling it out.  Who’s your audience, and are they going to know how to work the [datetime] type accelerator?  How easily can they find the help for it?  But if you sacrifice 1.25 milliseconds–and it takes about 250 milliseconds for you to blink–you can use a command that a Powershell newbie can easily check the built-in help files and know just what you’re doing.

Best Practices

So, be a hero, use the Powershell Best Practices, Surly Style!

  1. Comment-Based Help
  2. Avoid Alias’ in code
  3. Indent, and keep it consistent.
  4. Comment in code, but subscribe to KISS
  5. Spell it out

What are some of the best practices you use?  Let me know in comments!

Advertisement

December 31, 2012 - Posted by | Powershell - Best Practices, Powershell - Getting Started | , , ,

2 Comments »

  1. […] also uses some scriptblock spacing that I hate (read here for what I mean), but I cleaned that up for my script.  What this section of code is doing is […]

    Pingback by Functions – When and how? « The Surly Admin | January 3, 2013 | Reply

  2. […] How do you start using Powershell? Powershell Discovery: Variables Get Started with Powershell: Loops and branches Powershell and the Pipeline Powershell Best Practices – Surly Style […]

    Pingback by Powershell – Steep Learning Curve? « The Surly Admin | March 28, 2013 | Reply


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 )

Connecting to %s

%d bloggers like this: