About a hundred years ago when I stopped riding dinosaurs to work and started learning PowerShell, I struggled to know what to learn first and where to even begin. That blue box with white writing was intimidating to say the least. I finally worked up the courage to type ipconfig in it just to say I used PowerShell. And it’s been a lot of trial and error/love and hate ever since. As much as I love PS, there are some things that would have made my life easier if I had known them when I was a baby. I’ve spent countless hours hammering on my keyboard (sometimes with my fingers… sometimes with my forehead) wondering why the Shell of Power is such a tyrannical bully overlord dictator, only to figure out that I was the idiot. I hope this post saves you from some of the frustration I’ve faced. This is in no way an exhaustive list, but here are 6 things I wish I had known when I started learning PowerShell.

\\\1) Variables Vary

Naming your variables doesn’t seem like it should be that difficult or require a lot of thought, but there’s nothing worse than going back to your script 6 months after you wrote it and wondering how the commies hacked your computer because this couldn’t have been you. What the heck does $TzNS mean? When naming your variables, you should use plain English (or Klingon or whatever your native tongue is) and give it enough of a description that you’ll know what it means even after you view it post alien abduction. Well, that last part might be a challenge, but hopefully you’re picking up what I’m putting down. Not too little and not too much. Somewhere in between $ThatOnePersonWithFrecklesAndGlassesAndIsLeftHanded and $TOPWFAGAILH.

\\\1.1) BONUS – Destroyed

Man, I’m already breaking my numbering system.

But when using variables, I would often write my code destructively and overwrite my objects I would actually need later. 2 examples of this:

 

Example 1:
$AllExchangeServers = (Get-ExchangeServer).count
This is great if I only want the count and I never want to use any of the actual data from the Exchange Servers. But what if I wanted to list the name of the servers later? I would have to get them all over again. Depending on what the command is, it could take several seconds or maybe minutes compared to the near instantaneous count method. It would be better to do $AllExchangeServers = Get-ExchangeServer and then count when I needed it ($AllExchangeServers.count) and list the name of the servers when I needed it ($AllExchangeServers.Name).
Example 2:
$ExchangeServer = Get-ExchangeServer -Name Server1
$ExchangeServer = $ExchangeServer.Info
$ExchangeServer = 'Shiver Me Timbers'

This is a real world example…just kidding, this example is absolutely horrendous, but hopefully it gets the point across. Each time I write $ExchangeServer =, I’m giving it a new value and destroying whatever it was previously. If I need to go back and get something from the first time it was defined, I’m out of luck. It would be better to write it sequentially and store the various values in different variables if I need to go back and reference them. Of course, if you don’t need any of the previous data for validation or anything else, destroy away! Swing that hammer like Jim Adler!

\\\2) Counting Sheep

I used to have issues counting lists that may not return anything. I would expect to get 1, but I would often get nothing. If you wrap your variable with only 1 object in it in an array, you can count it properly:

@($Var).count
This can seem minor, but if you write in logical operators like if($X -gt 1)…this can be a problem. If you make it into an array and there’s only 1 object, it will count it accordingly. Life saver.

\\\3) Get-Command

You can easily use Get-Command to pull back the curtain and see the guts of a function. This is an easy way to see what module the command is a part of, but it’s also super useful for seeing the code itself. Once you start writing your own modules it can be troublesome to look through all of the functions you’ve written to find what’s actually in the code. You can pipe Get-Command NameOfYourFunction | fl (alias for Format-List) to see all of the code quickly.

function Hi($Hello){
    $New = $Hello + "Hi"
    $New
}
Get-Command Hi | fl

\\\4) [PSCustomObject]

I feel like I found this one way late in my coding life, but this is an easy way to build your own table. I originally went down a deep dark path to bring my own table to life, but this way is much more palatable and environmentally friendly. Sea turtles rejoice!

$New = [PSCustomObject]@{
    Name = 'Steve'
    Title = 'El Jefe'
    Location = 'Mars'
}
This is especially amazing when combining multiple sets of data into one object in a ForEach loop. Say you’re combining AD Group info with Exchange Distro info. You can create a blank array ($CombinedInfo) outside of your ForEach loop and then add your newly created PSCustomObject to it. An added bonus is naming the column headers whatever your heart desires without having to whack out a couple million lines of code with the Select-Object cmdlet. A quick and dirty example of what this looks like:
$CombinedInfo = @()
$Groups = Get-ADGroup -Filter "GroupCategory -eq 'Distribution'"
foreach($Grp in $Groups){
    $DL = Get-DistributionGroup
    $CombinedInfo += [PSCustomObject]@{
        ADSamAccountName = $Grp.SamAccountName 
        ExMaxSend = $DL.MaxSend
        ExMaxReceive = $DL.MaxReceive
    }
}

\\\5) Continue, Return, Break, Exit

I initially thought the instructions for these were “just season to taste,” but boy was I wrong. These can make or break (pun ALWAYS intended) your script if you don’t fully understand when and how to use them. I’m like Uncle Ben here telling you that with great power comes great responsibility.

To demonstrate how these work, let’s look at the same chunk of code and how each keyword behaves differently. Here is the block of code:

$test = 1..5
foreach($t in $test){
    if($t -eq 3){
        continue
    }
    $t
}
"Hi"
Continue

When we use continue, it will output each integer but skip 3 and keep going and finish the ForEach loop. And after the loop completes it will write “Hi”. This is useful when we want to skip something in a loop and keep processing the rest.

PowerShell Continue Keyword
Break

Now note the difference when we use break instead of continue. This time it exits the loop at 3 and executes “Hi” outside of the loop. This is handy when we want to stop processing the loop when something specific happens but keep going with the code we have after the loop.

PowerShell Break Keyword
Return

Slightly different than continue and break, using return in the same example will stop everything. If return was used in a function however, it would terminate the function and then continue to the next line of code.

PowerShell Return Keyword
Exit

Doom and gloom. This is comparable to those people spamming Alt+F4 in chats since the first troll crawled out from under a rock. It’s a fun way to destroy everything you love and care for.

\\\6) Keyboard Shortcuts

Although there are a ton, here are the main ones that have changed my life…other than Ctrl+C (Copy) and Ctrl+V (paste) for all of you who were born after 2010:

  • Ctrl+Z for undo
  • Ctrl+Y for redo
  • Home to go to the beginning of a line
  • End to go to the end of a line
  • Hold Shift + left or right to highlight a section
  • Hold Shift + Ctrl and tap left or right to jump a word at a time
  • In PowerShell ISE/VS Code:
    • Click the number of the line and it will highlight the entire line
    • Click one of these and then hold Shift and click another to highlight everything in between

\\\7) More MAGIC

Man, these numbers are all just made up, aren’t they? Kind of like all of the taxes deducted from my check, but I digress…

Honorable mentions that I wish I had known when I started learning PowerShell are:

  • Get-Member (useful for getting hidden properties)
  • Get-Type (easy way to check what the data type is)
  • The various pipelines (important when you start writing your own debugging/logging)
  • Adding a value to an array instead of replacing it (Like the proxyAddresses attribute in AD)
  • Renaming a Property/Column with Select-Object
  • Pretty much all of the string manipulation I cover in two posts on String Theory I and II.

For those new to PowerShell, what has been the hardest thing for you to wrap your head around? And for those salty seasoned vets, what’s something you wish you had known before starting your PowerShell journey?

OTHER POSTS YOU WANT TO READ

Nested AD Managers (PowerShell)

So I had a somewhat odd request (isn't that how all truly great stories start) a long time ago in an IT galaxy far, far away. And it went something like this: we want to email an employee and CC their manager, but if their manager is inactive (took a one-way trip to...

Index Scripts for Windows Search

So you just finished writing some code and you go to save your file. You summarize all of the important aspects this section of code contains into a nice, easy-to-read file name that your future self will immediately recognize. Fast forward to the future where you...

Array vs ArrayList (PowerShell)

For some tasks in life, being precise is a necessity. But most of us get away with rounding, paraphrasing, and hitting in the general vicinity most of the time. Depending on your personality, you may be one who strives for perfection and strains on every miniscule...

Spice Up HTML Emails with PowerShell – Part III

So far in this series we've fumbled our way around the kitchen and tried to wing it when sending HTML emails with PowerShell. It was clunky to say the least. We then went through our spice rack and built an HTML template, highlighting the nuances of each spicy element...

Spice up HTML Emails with PowerShell – Part II

In Part I of our scrumptious concoction we put our script into the oven to let it bake. But we forgot to add our secret sauce that's sure to leave our recipients drooling, which is clearly our goal. In this post we'll continue to spice up HTML emails with PowerShell...