Write Functions, Not Scripts – Part I

Seriously. If you’re not writing PowerShell functions yet, it’s. time. to. begin. It’s been time.

I’ve decided I should begin to compile some potentially influencing reasons why you’d want make this move. Additionally, I also thought that maybe we need to first, give those out there, that haven’t been doing it, a little push by example. Before we get to some reasons why functions over scripts, do this. Next time you start to write a PowerShell solution, just type what’s been written in the below example.

Function <Verb>-<SingularNoun(s)InCamelCase> {

}

Now, in place of <Verb>, enter an approved verb from the results of the Get-Verb cmdlet. As of today, there’s 98 approved verbs in Windows PowerShell 5.1, and an even 100 in PowerShell 6.0.*

PS > Get-Verb
Verb        Group
----        -----
Add         Common
Clear       Common
Close       Common
Copy        Common
Enter       Common
Exit        Common
Find        Common
Format      Common
Get         Common
Hide        Common
Join        Common
Lock        Common
...

Once you’re done replacing <Verb> with your approved verb, replace <SingularNoun(s)InCamelCase> with a singular noun, or set of singular nouns in camel case. Here’s some examples: Set-ADDisabledUser, Test-KMSServer, Get-TimeZone, Read-Log, etc. We don’t need plural nouns even if your function is going to run against multiple users, servers, timezones, or logs.

Let’s say you chose Get-TimeZone. Before we actually begin to write timezone related code, let’s make sure our function actually works. Copy and paste the below function to the ISE, or Visual Studio Code, or even the ConsoleHost, and press F8 or Enter (depending on which host you chose). What happens?

Function Get-TimeZone {
    '---->Testing our Get-TimeZone function.<----'
}

Nothing, right? It didn’t even work!

That’s not exactly true. Something did happen. Running the code that makes up a function, doesn’t invoke, or execute, the function. It adds it to memory, or, it adds it to the current PowerShell session. It gets it ready to be run, but how do we know? Let’s check our Function PSDrive. In this example, we’ll see the full process of the function not yet existing, it being created, and then it being available to use.

Here, we’ll verify the function doesn’t yet exist.

Get-Item -Path Function:\Get-TimeZone
Get-Item : Cannot find path 'Function:\Get-TimeZone' because it ...

Here, we’ll add the function to our session/memory.

Function Get-TimeZone {
    '---->Testing our Get-TimeZone function.<----'
}

And here, we’ll verify that it does, now exist.

Get-Item -Path Function:\Get-TimeZone

CommandType     Name                     Version    Source
-----------     ----                     -------    ------
Function        Get-TimeZone

Since it’s now available, let’s try it out.

PS > Get-TimeZone
---->Testing our Get-TimeZone function.<----

A function has a name that we can use to run its included code. The thing is, the code (our function) has to have been defined, or created, before we try and use it. Simple. So, before a function can be used… it must be loaded, or added to memory. Keep all of this in mind.

I want to show one other little secret that’s going to be helpful as you transition to using and writing functions. I know, I know, we were supposed to discuss why you want to use functions, not how to use them. I think it’s important to know a couple things before we really get started, and thus the minor derailment on today’s post. When we get back next time, we’ll immediately jump into the reasons to use functions.

Let’s say we have a file on our Desktop called MyFunctions.ps1. And, let’s say it contains three functions, like this:

Function Test-One {
    'This is the Test One function.'
}
Function Test-Two {
    'This is the Test Two function.'
}
Function Test-Three{
    'This is the Test Three function.'
}

How do we get these functions into our session!? They’re in a file… In a PowerShell host program, perhaps the ConsoleHost, dot source the file as in the below example. You can use either the full path, or navigate to the Desktop first, and just dot source the file by itself (no path). But what’s dot source, right? It’s a dot. Seriously.

PS > Get-Item -Path Function:\Test-One
Get-Item : Cannot find path 'Function:\Test-One' because it ...
PS > . C:\Users\tommymaynard\Desktop\MyFunctions.ps1
PS > Get-Item -Path Function:\Test-One

CommandType     Name                     Version    Source
-----------     ----                     -------    ------
Function        Test-One

PS > Test-One
This is the Test One function.
PS > Test-Two
This is the Test Two function.
PS > Test-Three
This is the Test Three function.

Had we not used the full path, we would have had to navigate to the file, and then as mentioned, dot source the file. This below example would also add the three functions to our current PowerShell session.

PS > Set-Location -Path C:\Users\tommymaynard\Desktop
PS > . .\MyFunctions.ps1

Okay, we’re done for today, and again, I’m promise we’ll go into why you want to use functions over scripts… now that you know functions aren’t even scary.

* If you’re curious which two additional approved verbs PowerShell 6.0 has over PowerShell 5.1, then take a look below to find out.

PS > ($PSVersionTable).PSVersion.ToString()
6.0.0
PS > (Get-Verb).Verb | Out-File .\Desktop\6.0Verbs.txt
PS > ($PSVersionTable).PSVersion.ToString()
5.1.14409.1012
PS > (Get-Verb).Verb | Out-File .\Desktop\5.1Verbs.txt
PS > 
PS > $Ref = Get-Content -Path .\Desktop\6.0Verbs.txt
PS > $Dif = Get-Content -Path .\Desktop\5.1Verbs.txt
PS > Compare-Object -ReferenceObject $Ref -DifferenceObject $Dif

InputObject SideIndicator
----------- -------------
Build       <=
Deploy      <=

Part II is now available!

Leave a Reply

Your email address will not be published.