Tag Archives: Function

Write Functions, Not Scripts – Part V

I like how my assumption that Part IV of Write Functions, Not Scripts wouldn’t be a worthy contender to the previous three parts (P1, P2, P3). Seriously, I didn’t think it compared, and if I’m not mistaken, it’s been the most popular post in this series thus far.

It’s a simple concept though: short, simple, single-purposed is sufficient for writing functions. Think small, easier to troubleshoot code. Say you have five functions that run in succession — one right after the other. The point at which things break will likely remove 4 of the 5 functions as areas you even need to review. That means you’re troubleshooting a fifth of the code the scriptwriter would have to review. Functions are a no-brainer. Well, they ought to be, anyway.

Time for some more thoughts on why functions over scripts. Hey, ever see one of these in a script?

$UserCount = 10
# When using in production, please comment out the above line.
# Then, uncomment the below line.
# $UserCount = 20

This is why we have a Read-Host. This, would be better.

$UserCount = Read-Host -Prompt 'Enter the preferred User Count'

Before blogging was my main gig, you might find me scouring various forums more often and helping others (and secretly learning more, too). A good number of people were getting their input via the Read-Host cmdlet. It’s nice and all, but by itself, there’s a bit of mystery as to what a user might enter. While I’m expecting 10, or 20, I could end up with all kinds of obnoxious values.

$UserCount = Read-Host -Prompt 'Enter the preferred User Count'
Enter the preferred User Count: DogCatMom5
# Huh!?

Inside a Do loop construct, we can better control the acceptable input.

Do {
    $UserCount = Read-Host -Prompt 'Enter the preferred User Count'
} Until ($UserCount -eq 10 -or $UserCount -eq 20)
Enter the preferred User Count: 1
Enter the preferred User Count: 2
Enter the preferred User Count: 3
Enter the preferred User Count: 4
Enter the preferred User Count: 5
Enter the preferred User Count: 6
Enter the preferred User Count: DogCatMom5
Enter the preferred User Count: 10

But there’s still a better way. Function’s allow for parameters and so, as absolutely as best as we can, we need to separate our lives — and our function writing — from Read-Host. If it’s ever in a function, and I’ll agree that it can happen, there ought to be really great reason as to why. We’ll close out today’s post with a simple function example that uses parameters. This is such a huge benefit.

Function Test-ParamsInAFunction {
    Param (
        $Number,
        $Letter
    )
 
    If ($Number -and $Letter) {
        Write-Output -InputObject "You have entered the number $Number, and the letter $Letter."
    } ElseIf ($Number) {
        Write-Output -InputObject "You have entered the number $Number."
    } ElseIf ($Letter) {
        Write-Output -InputObject "You have entered the letter $Letter."
    } Else {
        Write-Output -InputObject "You have not entered a number or letter."
    }
}
Test-ParamsInAFunction
Test-ParamsInAFunction -Number 5
Test-ParamsInAFunction -Letter X
Test-ParamsInAFunction -Number 10 -Letter D

You have not entered a number or letter.
You have entered the number 5.
You have entered the letter X.
You have entered the number 10, and the letter D.

Functions can handle run time delivered values. There’s no more opening up scripts and adding and removing static comments and variable assignments. There’s no using Read-Host inside our scripts and functions (unless, again, you’ve got a really great reason), even if, you’re using a language construct to protect the acceptable values. Before we full on wrap up, let me show you how to make a parameter only accept the 10 and 20 values.

Function Test-ParamsInAFunction {
    Param (
        [ValidateSet(10,20)]
        $Number
    )
    ...
}

See you again next time.

Write Functions, Not Scripts – Part IV

If you haven’t been with us in the previous three posts (this one, this one, and this one), we’ve been discussing why functions, over scripts. If you haven’t already, it’s time to change your mindset.

The idea is to write small, concise, single-purposed, chunks of code. We call these functions. What we don’t need… is long, overly complex, multi-purposed scripts. We’ve got to stop the scripting mentality. You know the one… you’re writing a script and your next thought is, now I need to do this…and then you write lines and lines beneath that which has already written. Now you have a multipurpose script and no clear end in sight.

It’s pretty simple: the longer the script, the more pieces to troubleshoot and when adding new features, the more potential areas to make errors. You’re creating more confusion. The whole process of script writing can easily and quickly get away from you. In one moment you add one solution, and then in another, you add more solutions to other problems you want to solve, and it’s all in a single file. Ugh.

That was it. Start writing in a single purposed methodology. Even if you’re not going not to write in functions beginning tomorrow morning, like you should, start scripting in single purposed scripts. If you need it, make that your first step. I’m not writing for my own benefit this evening; I’m writing for yours. You’re going to love being a function writer. You just need to make this the week you start, if you haven’t already. Here, I’ll get you started:

Function Get-ItDone {
    # Add your code here.
}
Get-ItDone

Alright, see you next time.

Write Functions, Not Scripts – Part III

First off, I have to say, the first two posts in this series have been very successful. Nearly 200 visitors on a Saturday and then a Sunday, too — that’s unheard of during the weekend. And to think, these two posts: Part I and Part II, have felt like some of the most distracted and unpredictable writing of mine to date, and I’ve been at this for almost four years straight, without a missed month.

Okay, to continue with a distraction, in Write Functions, Not Scripts — Part II, I showed a picture of the fog on my cul-de-sac in Tucson, AZ (the desert) last Friday. Here’s that picture now. That fog, is not typical for us. Maybe it happens once a year.

I mentioned that I’d include the typical view, and so here’s a picture from Saturday — just one day later. And that’s, what it normally looks like around here.

Moving along, as if there’s a PowerShell related topic at hand. So, here’s one reason for functions, over scripts: it’s variables. Let’s move out the fog, and explain.

When we’re developing scripts, we often create variables and assign them values at the top of our code. Normal stuff. The problem is that when we go to execute the script a second (or third, or fourth) time, the variables in our script have already been assigned a value, and that may make our results incorrect. This can lead to confusion and wasted time, incorrect results, and extra lines of unnecessary commands to initialize variables. Stop initializing, and then re-initializing, variables.

Let’s take a look at an example.

In each pass though the below ForEach-Object cmdlet, we set the $User variable to my name with the number of the current pass at the end. Then, we increase the $Count variable by 1. Take a look at the below results. Everything looks perfect. My name includes the current iteration through the loop, and the count matches, as it should.

1..5 | ForEach-Object {
    $User = "tommymaynard$_"
    $Count++

    "Count: $($Count)"
    "User: $User"
}

Count: 1
User: tommymaynard1
Count: 2
User: tommymaynard2
Count: 3
User: tommymaynard3
Count: 4
User: tommymaynard4
Count: 5
User: tommymaynard5

Now, let’s run it again.

1..5 | ForEach-Object {
    $User = "tommymaynard$_"
    $Count++

    "Count: $($Count)"
    "User: $User"
}

Count: 6
User: tommymaynard1
Count: 7
User: tommymaynard2
Count: 8
User: tommymaynard3
Count: 9
User: tommymaynard4
Count: 10
User: tommymaynard5

We didn’t want to continue to increase the $Count variable past five, but because we’re scripting, we did it anyway. Writing something like this would required a Remove-Variable, or Clear-Variable command, when the final loop was done. Annoying, and without it, when you’re scripting, you’re going to have inaccuracies.

With functions, we don’t ever need to reinitialize variables to their default values. And of course, we don’t need to remove them, or clear them, before our code is run a second, or third time. This isn’t to say you’ll never change the value of a variable inside a function, it’s to say that variables in a function don’t typically exist before, or after, a function is executed.

Function Show-CountAndUser {
    1..5 | ForEach-Object {
        $User = "tommymaynard$_"
        $Count++

        "Count: $($Count)"
        "User: $User"
    }
}

PS > Show-CountAndUser
Count: 1
User: tommymaynard1
Count: 2
User: tommymaynard2
Count: 3
User: tommymaynard3
Count: 4
User: tommymaynard4
Count: 5
User: tommymaynard5

Okay, that was the first execution. One through five: it looks good. And here’s, the second execution.

PS > Show-CountAndUser
Count: 1
User: tommymaynard1
Count: 2
User: tommymaynard2
Count: 3
User: tommymaynard3
Count: 4
User: tommymaynard4
Count: 5
User: tommymaynard5

Ah, perfect! Write functions, and stop worrying about having to set, and reset, variables. Use the variable scope provided by functions — it wants to help you. It’s one reason for functions over scripts. Back again later.

Part IV is now available.

Write Functions, Not Scripts – Part II

In Part I of this series, I was supposed to begin offering reasons as to why someone would want to stop writing scripts, and start writing functions. I got a bit distracted. Instead, we discussed making an easy, and mostly meaningless, function. The reasoning for this, was because I get this feeling that moving from a script writer to a function writer can be a unsettling endeavor for some. I liken it to bicycle training wheels. They’re so easy, and reliable. It’s just not until you take them off, and get your balance, that you suddenly realize all the things they actually kept your from doing. Learn to balance this bike, and then you can start jumping off your makeshift ramp in the middle of the cul-de-sac, instead of just riding around it.

Speaking of cul-de-sacs, here’s another distraction. It’s our view from Friday morning. That’s not what it usually looks like in Tucson, AZ. Fog is really, really rare. In one of these follow ups, I’ll be sure to include our typical desert view, uninterrupted by “winter” type weather events.

Functions can get advanced, sure, but once you know a few things about them, you’ll never look back. You’ll continue to learn more and more about them. There’s a whole new level of being proud of your work, as you transition to function writing. You’re going to sleep better at night knowing you’ve written five singled-purposed functions vs. that one long script you secretly hate to open and troubleshoot. Maybe, it’s not even a secret.

Look, at the end of the day, a function is nothing more than a scriptblock you can invoke by entering its name. You run commands all the time, maybe even a few, one right after the other. Now you can do the same thing, but by only typing a function’s name and pressing enter.

Let’s assume there’s was a point in time I often wanted to know the date, the system drive letter, the current PowerShell host major version, and return a random number between 1 and 20. If I grew tired of doing this manually, I could create a function to do this for me. It’s not likely this would ever be useful outside this post, but it certainly helps highlight what using a function does. It’s essentially four commands in one. Easy.

Function Start-RandomStuff {
    "Date : $(Get-Date)"
    "System Drive : $env:SystemDrive"
    "Host Major Version : $((Get-Host).Version.Major)"
    "Random Number (1-20) : $(Get-Random -Minimum 1 -Maximum 20)"
}
PS > Start-RandomStuff
Date : 02/13/2018 22:58:00
System Drive : C:
Host Major Version : 5
Random Number (1-20) : 3

Think of a function as a wrapper. You can wrap the execution of various (related) commands by running one command. This isn’t to say our example ran related commands so much; it really didn’t. The point is to keep in mind that functions should be single purposed. They’re tight, short, and to the point. If you start wondering if you’re adding too much procedural code to your function, you probably already have. If you keep adding to the function you’re currently writing, then you better be able to explain why.

I’ve done it again! I haven’t really hit those reasons as to why functions, over scripts. Or perhaps I have a little. I do have some specifics topics, and for the second time, let’s hope the next part of this series, gets serious. There really are a few specific things I want to share — reasons why functions are all you want to be writing.

Back soon. And hopefully, with a non winter picture of the cul-de-sac.

Part III is now available.

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)InPascalCase> {

}

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

[/plain]
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

[/plain]

Once you’re done replacing <Verb> with your approved verb, replace <SingularNoun(s)InPascalCase> with a singular noun, or set of singular nouns in pascal 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

[/powershell]
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.
[/powershell]

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!

ValidateSet Default Parameter Values

The example code I’m going to include below, I’ve used before. I really like it and so I’m going to give it place here on my website, in case it may ever be helpful for you, and maybe even me again.

The first time I used something like this code example was for a function that created random passwords. By default, that function’s CharacterType parameter would include the four values Lowercase, Number, Symbol, and Uppercase. By using the parameter, you can specify which of the values you actually use, if you didn’t want to use all four. By default, the parameter included them all.

We are defining an advanced function called Test-Function with a single parameter called Type. This parameter uses ValidateSet in order that it’ll only ever accept four different parameter values, for the Type parameter. Additionally, the Type parameter actually includes a default value that includes all four of the values: FullAccess, SendAs, SendOnBehalf, and Calendar. If you ever find yourself needing an All parameter value, just use this option instead; you don’t actually need an All parameter value, you just need to include all the possible values as the default.

After the parameter inclusion, the function begins with a Foreach language construct that will evaluate each Type that been included, whether it’s all four by default, all four because someone use the parameter and entered all four possibilities (not necessary, obviously), or something less than the four options.

Inside each iteration thought the Foreach there’s a Switch statement that will be evaluated. Based on the current type value, its value will be displayed in a string that includes a hard coded value to ensure it’s correct.

Function Test-Function {
    [CmdletBinding()]
    Param (
        [Parameter()]
        [ValidateSet('FullAccess','SendAs','SendOnBehalf','Calendar')]
        [string[]]$Type = ('FullAccess','SendAs','SendOnBehalf','Calendar')
    )

    Foreach ($T in $Type) {
        Switch ($T) {
            'FullAccess' {
                "Doing $T stuff (should match FullAccess)."
            }
            'SendAs' {
                "Doing $T stuff (should match SendAs)."
            }
            'SendOnBehalf' {
                "Doing $T stuff (should match SendOnBehalf)."
            }
            'Calendar' {
                "Doing $T stuff (should match Calendar)."
            }
        } # End Switch.
    } # End Foreach.
}

PS > Test-Function
Doing FullAccess stuff (should match FullAccess).
Doing SendAs stuff (should match SendAs).
Doing SendOnBehalf stuff (should match SendOnBehalf).
Doing Calendar stuff (should match Calendar).

PS > Test-Function -Type 'FullAccess','SendAs','SendOnBehalf','Calendar' # Same as above.
Doing FullAccess stuff (should match FullAccess).
Doing SendAs stuff (should match SendAs).
Doing SendOnBehalf stuff (should match SendOnBehalf).
Doing Calendar stuff (should match Calendar).

PS > Test-Function -Type 'FullAccess','SendAs','SendOnBehalf'
Doing FullAccess stuff (should match FullAccess).
Doing SendAs stuff (should match SendAs).
Doing SendOnBehalf stuff (should match SendOnBehalf).

PS > Test-Function -Type 'FullAccess','SendAs'
Doing FullAccess stuff (should match FullAccess).
Doing SendAs stuff (should match SendAs).

PS > Test-Function -Type 'Calendar','FullAccess','SendAs'
Doing Calendar stuff (should match Calendar).
Doing FullAccess stuff (should match FullAccess).
Doing SendAs stuff (should match SendAs).

PS > Test-Function -Type 'SendOnBehalf','FullAccess','Calendar'
Doing SendOnBehalf stuff (should match SendOnBehalf).
Doing FullAccess stuff (should match FullAccess).
Doing Calendar stuff (should match Calendar).

Nothing down here, but thanks for reading all the way! Actually, here’s a bonus if you didn’t already know it. Those hard coded statements inside the Switch statement, could’ve been written a little differently.

This:

...
            'FullAccess' {
                "Doing $T stuff (should match FullAccess)."
            }
            'SendAs' {
                "Doing $T stuff (should match SendAs)."
            }
            'SendOnBehalf' {
                "Doing $T stuff (should match SendOnBehalf)."
            }
            'Calendar' {
                "Doing $T stuff (should match Calendar)."
            }
...

could’ve actually been this:

...
            'FullAccess' {
                "Doing $T stuff (should match $_)."
            }
            'SendAs' {
                "Doing $T stuff (should match $_)."
            }
            'SendOnBehalf' {
                "Doing $T stuff (should match $_)."
            }
            'Calendar' {
                "Doing $T stuff (should match $_)."
            }
...

One Function for Them All

I’m in the middle of a project that requires me to read over existing PowerShell scripts and provide edits. This means I need to add error checking, remove unnecessary duplication, and break down lengthy scripts into tighter, singled purposed functions. After breaking down the first script into three functions and therefore concerned we’re going to have a lengthy list of independent functions to execute, I had an idea to make things easier. I’ve said it before: I’d rather troubleshoot five 20 line functions, than one, one hundred line script.

The below example creates five basic, basic functions. Each of these Show-*Number functions writes a single string to the host when invoked. The problem here, is that I’m required to remember the names of each of the functions and their order. This example uses fairly easy to remember names, and the order is pretty straightforward, too. Even so, in some situations they won’t always be so easy to recall, to include their order. I understand we have various tools for command discovery, but I want a simpler way.

Function Show-FirstNumber {
    'This is sequence 1.'
}

Function Show-SecondNumber {
    'This is sequence 2.'
}

Function Show-ThirdNumber {
    'This is sequence 3.'
}

Function Show-FourthNumber {
    'This is sequence 4.'
}

Function Show-FifthNumber {
    'This is sequence 5.'
}

PS > Show-FirstNumber
This is sequence 1.
PS > Show-SecondNumber
This is sequence 2.
PS > Show-ThirdNumber
This is sequence 3.
PS > Show-FourthNumber
This is sequence 4.
PS > Show-FifthNumber
This is sequence 5.

That simpler way, is to create a single function that has the job of invoking all of the other functions. I suspect you may have thought of this too, but if not, well then, there we go. Now, all my users need to know about is the Start-Sequence function defined below.

I’m out to “hide” those other functions. I want to say I took your three scripts, and now you can run them all with this single function. The user may not even know their single invocation just ran several separate functions, and that’s, fine with me.

Function Start-Sequence {
    'Starting the sequence.'
    Show-FirstNumber
    Show-SecondNumber
    Show-ThirdNumber
    Show-FourthNumber
    Show-FifthNumber
    'Ending the sequence.'
}

When the Start-Sequence function is invoked, it’ll remember the names of those other functions for us, and best of all, I won’t be required to the do the same. Here’s our results now. It’s one single command, running multiple functions, getting the entire job done, and making things much easier to troubleshoot later on.

PS > Start-Sequence
Starting the sequence.
This is sequence 1.
This is sequence 2.
This is sequence 3.
This is sequence 4.
This is sequence 5.
Ending the sequence.

Anyway, back to breaking apart some scripts.

An Alias and Function Change

At current, my site has 230 published posts. It’s 231 when this one goes live. Those are all written by me since mid 2014. I’m not sure of my per month average, but I can say for sure that I’ve never missed a month.

Outside of those 230 published posts, are 40 some drafts. Those are posts I started and never finished for one reason or another. Sometimes, I realized what I thought worked really didn’t, and therefore wasn’t able to complete the post. Well, in an effort to clean up the drafts, I’m publishing my first stupid draft turned post. Here’s goes… It’s learning from my failure — enjoy.

For just about as long as I can remember, I’ve always created an alias for my functions, just before the function is defined. This is of course when a function is defined outside of a module, such as when it’s defined in my $PROFILE script. In the below example, I do just that. After the alias is created, I define the function for which my alias can be used to invoke the function.

Set-Alias -Name saw -Value Show-AWord
Function Show-AWord {
    '!! A Word !!'
}

PS > saw
!! A Word !!

There’s another way to do this, as well. You can create the alias after the function’s been defined. You just swap the commands.

Function Show-EWord {
    '** E Word **'
}
Set-Alias -Name sew -Value Show-EWord

PS > sew
** E Word **

And here’s where the post went stupid.

I’ve always been mildly annoyed that I needed to have the code outside of the function, whether it’s before, or after, the function definition. I always wished there was a way to create aliases from inside the function.

Well there is, and there’s always been. I’ve just never given it much thought until about five minutes ago. This might be why I started this post; I didn’t think about it long enough. Here’s nearly the same function as above; however, now we’ll create the alias for the function, within the function. Because the Set-Alias cmdlet has a scope parameter, we can create a global alias from inside the function.

Function Show-GWord {
    Set-Alias -Name sgw -Value Show-GWord -Scope Global
    '$$ G Word $$'
}

PS > sgw
PS > # Nothing

Here’s about the time, I realized my problem. If you create an alias inside the function (using the Global scope [that’s how it “works”]), the alias is not going to exist until after the function has been invoked for the first time. Therefore, the function would have to be run like the below example. I pretty much removed a line outside the function, put it into the function, and then added another line outside the function. Ugh, not what I was after all.

PS > Show-GWord | Out-Null
PS > sgw
$$ G Word $$

So yeah, this post didn’t go as planned. No wonder it made its home in my drafts. It makes you wonder though. Why isn’t there a way to run some code inside a function when the function is being defined? Maybe because functions belong in modules and modules give you this ability when they’re imported, via their module manifest, and potentially, their ScriptsToProcess.

There you have it. A stupid draft I published.

A Function for my Functions

I’m in the process of working on a new advanced function template for work. It’ll be the 2.0 version of this: http://tommymaynard.com/function-logging-via-write-verbose-2016. The problem I have with the current version of the template is that I could never only log to a file, without writing to the host program, too. In the end, I want a way to do one of four different things when I run a function written using the template. They are (1) not log anything, (2) log to the screen only, (3) log to a file only (this is the addition I’m after), or (4) log to both the screen and a file simultaneously. This is nearing completion, but one of the downsides to a well thought out and robust advanced function template is that it’s getting, really long. I honestly believe I’m at over 100 lines now (the 1.x versions are in the 70ish line territory).

So, I’m sitting around and thinking of a way to disguise the template, or add my code to it when it’s done, and so I wrote the following pieces of PowerShell. No saying that I’ll ever use this, but it seemed worthy enough to share here. I’m out to automatically add my code to the template, so I don’t have to code around the template’s standard code that allows for the logging.

This first example begins the process of creating a new function named New-FunctionFromTemplate. It’s set to accept three parameters: Begin, Process, and End. For those writing advanced functions, this should be easily recognizable as to why. It’s going to stuff whatever values are supplied to these parameters into these respective sections in the function template, that it displays when it’s done executing. You write the code, and it’ll place that code into the function template, via the New-FunctionFromTemplate function.

Function New-FunctionFromTemplate {
    Param(
        [string]$Begin,
        [string]$Process,
        [string]$End
    )
...
}

Next, I’ve included the remainder of this function. It’s a here-string that includes the function template design and layout, with a place for each variable within it. These variables will be replaced with whatever we send into the New-FunctionFromTemplate function when it’s invoked.

Function New-FunctionFromTemplate {
    Param(
        [string]$Begin,
        [string]$Process,
        [string]$End
    )   

    @"
Function ___-________ {
    [CmdletBinding()]
    Param (
    )

    Begin {
        $Begin
    } # End Begin.

    Process {
        $Process
    } # End Process.

    End {
        $End
    } # End End.
} # End Function: ___-________.
"@
}

Now that we’ve defined our function, let’s use it. As a bit of a shortcut, and as a way to make things a bit more readable, we’ll create a parameter hash table and splat it to the New-FunctionFromTemplate function. The below example could’ve been written as New-FunctionFromTemplate -Begin ‘This is in the Begin block.’ -Process ‘This is in the Process block.’ … etc., but I’m opted to not to that, to make things a bit easier to read and comprehend.

$Params1 = @{
    Begin = 'This is in the Begin block.'
    Process = 'This is in the Process block.'
    End = 'This is in the End block.'
}
New-FunctionFromTemplate @Params1

Below is the output the above commands create.

Function ___-________ {
    [CmdletBinding()]
    Param (
    )

    Begin {
        This is in the Begin block.
    } # End Begin.

    Process {
        This is in the Process block.
    } # End Process.

    End {
        This is in the End block.
    } # End End.
} # End Function ___-________.

This thing is, someone’s not typically only going to add a single string — a single sentence, if you will — inside a Begin, Process, or End block. They’re much more likely to add various language constructs and logic, and comments. Here’s a second example to help show how we would go about adding more than just a single string, using a here-string for two of the three blocks.

$Params2 = @{
    Begin = @'
If ($true) {
            'It is true.'
        } Else {
            'It is false.'
        }
'@
    Process = @'
'This is in the Process block.'
'@
    End = @'
If ($false) {
            'It is true.'
        } Else {
            'It is false.'
        }
'@
}
New-FunctionFromTemplate @Params2

When the above code is run, it produces the below output. It includes both our template structure, as well as, the code we want inside each of the blocks. In case you didn’t catch it right away, there’s a bit of a caveat. The first line is right up against the left margin. Even though, it’ll drop everything into it’s proper place — a single tab to the right of the beginning of each block. After that first line, you have to be the one to monitor your code placement, so that when it’s combined with the template, all the indentations line as expected.

Function ___-________ {
    [CmdletBinding()]
    Param (
    )

    Begin {
        If ($true) {
            'It is true.'
        } Else {
            'It is false.'
        }
    } # End Begin.

    Process {
        'This is in the Process block.'
    } # End Process.

    End {
        If ($false) {
            'It is true.'
        } Else {
            'It is false.'
        }
    } # End End.
} # End Function: ___-________.

And, that’s it. While I put this together, I’ve yet to implement it and truly code separately from my current template. We’ll see what the future holds for me, but at least I know I have this option, if I decide it’s really time to use it. Enjoy your Thursday!

Years Too Late: My First ISE Snippet

Every time I start to write a new PowerShell function, I manually write the same block of text. No idea how many times I’ve done it, but I’ve finally decided to stop. Today, I wrote it for the last time.

$Text = @'
Function ___-_________ {
    [CmdletBinding()]
    Param (
    )

    Begin {
    } # End Begin.

    Process {
    } # End Process.

    End {
    } # End End.
} # End Function: ___-_________.
'@

I’ve known about ISE snippets for some time, but haven’t taken a minute to get my advanced function included. Well, that finally ended today. With the $Text variable assigned above, I ran the following command.

New-IseSnippet -Title BasicFunction -Description 'Basic Advanced Function.' -Text $Text -Author 'Tommy Maynard'

Well, what did this just do? In the most basic reply to that question, it added a new snippet — a reusable chunk of text — I can add to the ISE anytime I want. All I have to do is press Ctrl + J and select BasicFunction from the available options.

That may be all you need, but I was curious what this really did. To find out, I ran Get-IseSnippet and it returned a path — helpful.

Get-IseSnippet

    Directory: C:\Users\tommymaynard\Documents\WindowsPowerShell\Snippets

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        9/27/2016   0:32 PM            867 BasicFunction.snippets.ps1xml

Then I ran Get-Content on the file to see what it was storing.

Get-Content -Path (Get-IseSnippet).FullName

<?xml version='1.0' encoding='utf-8' ?>
    <Snippets xmlns='http://schemas.microsoft.com/PowerShell/Snippets'>
        <Snippet Version='1.0.0'





<Header>
                <Title>BasicFunction</Title>
                <Description>Basic Advanced Function.</Description>
                <Author>Tommy Maynard</Author>
                <SnippetTypes>
                    <SnippetType>Expansion</SnippetType>
                </SnippetTypes>
            </Header>






            <Code>
                <Script Language='PowerShell' CaretOffset='0'>
                    <![CDATA[Function ___-_________ { [CmdletBinding()] Param ( ) Begin { } # End Begin. Process { } # End Process. End { } # End End. } # End Function: ___-_________.]]>
                </Script>
            </Code>

    </Snippet>
</Snippets>

So, there it is. Not only do I have my basic function snippet the next time I need it, I know that New-IseSnippet is writing an XML (.ps1xml) file to my Snippets directory in my Documents/WindowsPowerShell directory in my local profile. The date on my snippet and the directory indicate they were both created when I ran this command. I told you I hadn’t used snippets before.

Me being me, I ran Get-Command against New-IseSnippet and guess what? It’s a function; it’s not compiled code. Let’s take a look at it; let’s find where it decides whether to create the directory, or not.

Get-Command -Name New-IseSnippet

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        New-IseSnippet                                     1.0.0.0    ISE

(Get-Command -Name New-IseSnippet).ScriptBlock

While I didn’t include the entire results of the last command, I’ll include what’s important for how it determined whether or not to create the Snippets directory. In this first part, the function creates a $snippetPath variable. In it, it stores the current user’s WindowsPowerShell directory path. Before writing that to the variable, Join-Path appends “Snippets” — the child path — to the end. That means that in the end, the $snippetPath variable contains C:\Users\tommymaynard\Documents\WindowsPowerShell\Snippets.

$snippetPath = Join-Path (Split-Path $profile.CurrentUserCurrentHost) "Snippets"

In this section of the function, it runs Test-Path against $snippetPath, to determine if the path exists, or not. This cmdlet returns $true or $false depending on whether the path exists.

if (-not (Test-Path $snippetPath))
{
    $null = mkdir $snippetPath
}

If the path doesn’t exist, thanks to the -not, it executes the mkdir function against the path, and the directory is created. The next time New-IseSnippet function is run, the directory will already exists and this part of the function won’t be run.

Well, that’s it. I’m already looking forward to pressing Ctrl + J the next time I need to start a new advanced function.