Hello tech world this is TechThoughts
and welcome to this special tech thoughts video series focused on
learning PowerShell. This is an operationally designed series the aims
to get you ramped up and using PowerShell quickly. This is episode 11 in
the series PowerShell functions. As always if you prefer written
documentation the corresponding article for this topic can be found on the techthoughts.info blog which I've linked in the description below. Let's go ahead and
get started. So I've got VSCode open right here, and in a
new tab that is running as you can see down here in the bottom right hand
corner PowerShell and we're coding against PowerShell seven and let's dive
into a brief overview of what makes up a PowerShell function. There are several
components. You have function help, your function name, a CmdletBinding, some
parameters, of course your function logic what your function is actually
going to do and it might return something. So that may seem like a lot to
take in all at once but remember we have the ability to leverage the power of the
VSCode editor and if we simply start typing function because we have that
power shell extension installed we're going to get some intellisense and
auto-completion here so we can choose to do a function which notice automatically
populates the proper syntax for how to declare a PowerShell function and we
also have a function advanced. And we'll be diving into the differences between
these two. So let's go ahead and remove the standard function for just a second
and notice that most of the components that make up a PowerShell function are now included here. We have the function name which is right here after the function
declaration. We have the CmdletBinding, the parameters section, we have
our begin, process, and end which is optional to contain our logic and we
could return something at the end of this function if we needed to. The
thing that's missing based on our anatomy of a PowerShell function up here
is help. And again you don't have to memorize anything regarding that either
at the top of your function you can simply start typing help and there is a
comment - help that you can simply tab into which goes ahead and scaffolds the
properly formatted help at the top of your function So you'll be able to
declare a synopsis of your function, a description, some examples of how to run
it so that people running your function know what it's for and how to use it.
So by simply typing in the word function and using auto completion and typing in
the word help and using auto completion we have a scaffold function here that we
can now start to use that aligns with the anatomy of what makes up a
PowerShell function. We're going to be covering all of these different
components in depth and I think the best way to do that is to not look at this
kind of theory example. Let's dive right into an actual real life function and
explore its use. Okay so I've pasted it in a function here and notice again we
do have our function declaration that's always going to be the same, the word
function. And it's followed by our approved verb structure and this is
going to be Get-PublicIP. So we can start inferring that this is going to
get our public IP based on the approved verbs in this function name. We do of
course have help at the top of our function which arms us with information
about what this particular function does. So we can see that as the synopsis is
it's going to return our public IP address. The description says it's going
to query the ipify public IP API and return our public IP. And in order to
run this function we're simply going to run Get-PublicIP and it will return
the public IP address. So help is optional but it's always a really good
idea to include it at the top of your function so that people know what your
function does and how to use it properly. So we have our function help which is
part of our PowerShell function anatomy, we have our function name. Notice that we
do not have CmdletBinding in this particular example. We'll cover that more
in just a moment and this function is going to run just fine without it.
Notice this function also doesn't have any parameters because that section is
also optional. We of course do have to have some function logic but notice that
in this function that we do not have the begin, process, and end, that component also being optional. There's lots of options that you have when it comes to functions.
And we are going to return something, the public IP in this particular function so
we do have that return statement returning our public IP value. So what I
want to point out in this function right here is that if
we were to take away the function declaration and this closing bracket
right here what we really have at this point is a PowerShell script. And I could
save this and notice that this is saved as the demo.ps1 so it is a
PowerShell script. So that brings up a really interesting question of when
should you write a function versus when should you just leave it as a script?
Because the logic in here is pretty straightforward.
We're hitting an API URL, we're gonna use Invoke-RestMethod to hit that URI, and
we're going to return the value to the user. So pretty straightforward logic. And
at the end of the day a partial script is a set of code that performs a task.
And guess what? A PowerShell function does the exact same thing. There's not an
easy answer to this question and there's no firm line. For a quick task that
you'll be running on your workstation one time, a script might make more sense.
But if you're writing something that you expect other people to use, or if you
expect to use the logic more than once, a function is likely going to serve you
better. Remember that functions are single purposed they're gonna do
something very narrow and they're gonna do that things very well. So I'm gonna
push ctrl Z on my keyboard and add that function back in and this function Get-PublicIP is function scoped to do one thing very well, get the public IP
address and return that information. So it's single purposed. We do have help at
the top of this function and I'll show you in a moment that we're gonna be able
to use Get-Help on this function to determine what it does, which is really
empowering for other people to engage with your function. And of course as you
start to get more advanced in your PowerShell career you can start writing
tests against this function which can really help elevate your code. So again
there's not a perfect answer to when should it be a function versus a
script but hopefully that gives you some context about why you might want to
choose to write something inside of a function versus a script. Okay speaking
of that let's go ahead and run this and kind of see the difference. So I'm gonna
push ctrl tilde on my keyboard so I'm going to go ahead and dot source the
demo.ps1 script, and that's right it is still a script but this script contains
a function, which is Get-PublicIP. So notice that when I run this script
nothing occurs. And that's because the code was loaded in but in
order to invoke this particular function to actually do something it has to be
called in the actual terminal. So before we go ahead and kick off a function I
want to draw your attention to the fact that we can run Get-Help against Get-PublicIP. And because we put help at the top of the function we can see our
synopsis, it returns the public IP address and we can see things like
examples of with - examples and we can see we simply need to run Get-PublicIP. So again it's really important to add some help at the top of your function to
let people know what's going on. So we'll go ahead and run Get-PublicIP like the
example shows and we get a return of a string value because our output in the
help does indicate we'll be getting a string back and it contains our public
IP. Okay so there is a very real world practical function that you can use in
PowerShell. Remember that all of this code is available on the Learn PowerShell GitHub and that link is in the description below, so feel free to
reach out and grab this code and use this function in your own code if you
ever need to get a public IP address. OK so now you've seen how to declare a
function, how to add some logic to it, how to return some information, and add help
to your function. OK let's explore CmdletBinding now.
This is a very confusing topic for a lot of people new to PowerShell functions
and really by adding this what you're essentially doing is turning your basic
function into an advanced function. So when we ran Get-PublicIP a moment ago
it kind of looked and acted like a cmdlet. Like we had the cmdlet
Get-PublicIP. But remember cmdlets are written and compiled in c-sharp, so
when you run things like Get-Service or Get-Process those are cmdlets
written by the PowerShell team and compiled in c-sharp. but Get-PublicIP,
while it looks and acts like a cmdlet is actually written in PowerShell. So
it's not actually a true PowerShell cmdlet.
It is a PowerShell function. CmdletBinding
adds some additional advanced features that make this function act more like a
c-sharp compiled cmdlet. So you get some advanced features, I'll demonstrate
those now. So notice that if we Get-PublicIP and add the verbose switch
we don't get the verbose switch down here as an option for tab completion. So
we can't use things like Write-Verbose in this particular function because
right now it's loaded in without the CmdletBinding. So what I can do here is
I can come down to the first part after I declare my function. okay I can start
typing in param block and that will add in my CmdletBinding and my
parameter declaration. Now I don't need any parameters to come into this
function so I'm just gonna delete it and leave it empty. And now after URI I'll
add in a Write-Verbose and say the message I want to say is pulling public
IP from this URI. And so I'll go down here and dot source the demo.ps1
script and notice now that if I do Get-PublicIP then I do get a verbose switch
and if we run that you'll see in verbose pulling public IP from api.ipify.org
And we get some other verbose information output from Invoke-RestMethod. So just by adding the CmdletBinding and param at the top of this
function we turned it into advanced function giving us some additional
capabilities to our PowerShell function. CmdletBinding adds some additional
advanced features like should process. If your PowerShell function does something
dangerous you can give the user the option to confirm whether they want to
go ahead with that action prior to PowerShell doing it. It also supports
things like what if, and positional binding. And you can look up some of
these additional advanced features on the PowerShell docs. As a general
recommendation it doesn't hurt anything to add the
CmdletBinding and param stack at the top of your function. It gives you
some nice advanced features. It's my recommendation is to get in the habit of
throwing it on all of your PowerShell functions.
Okay let's dive into the wide world of PowerShell parameters now
for your functions. And I'll do that by adding in another function. This one will
be a little bit longer and will require some various PowerShell inputs. I can't
cover every single possible PowerShell parameter that there is for a function,
as they're quite lengthy and there are a lot of different various combinations.
What I want to drive home here is that parameters are the way for you to take
dynamic input into your function. So let's explore this particular one and
see how the various parameter settings here are helping us out in performing
various actions inside of this PowerShell function. So looking at our
approved verb and noun structure notice that we're going to be getting the
weather. So already we're inferring what this is going to do but we're gonna
check the help to make sure that it does what we think. So we have good help at
the top of this function. The synopsis is it's going to return weather report
information. The description is console oriented weather forecast that returns
weather information for the specified parameters. So this is great we're gonna
be able to check the weather using PowerShell and our console. How do we do
that? Well the example says just run Get-Weather and it returns full weather information based on the location of
your IP. Well what if I want to do more than just where I'm located by my IP?
Well it looks like there's some other examples using parameters and if we come
down here you'll notice that there are some parameter examples here . parameters city, parameter units, and parameter language. And so if we declare a city we
can do something like San Antonio and declare what type of units we want our
weather to be in metric or US and whether we want to be short return or
long return. So we have a couple of different parameters that are going to
change the way that this function behaves. And so let's take a look at that
now. So again in this episode we're not diving super deep into the logic flow of
what this function is actually doing, but at a high level is just going to go hit
an API and pull some weather information down. But you can dive more into the
details of how to write this kind of thing inside of the previous episodes of
Learn PowerShell. What we're gonna focus here is the structure of the function,
specifically the parameters for this example. So we have a couple of
parameters and one of them is City. So the correct way to declare a parameter
inside of a function is in the param block. The syntax here can be a little challenging
to learn and to be honest even after years of writing PowerShell I still
personally struggle with memorizing the exact syntax of the proper way to
declare parameters and their various ways of validating the correct input. So don't
feel bad about having to reach out and check the parameter documentation. I've
linked that in the description below and if you have to consult it on an ongoing
basis, so do I! So inside of our parameter block we can declare various types of
parameters and you could keep this very simple you could keep it something like
this, $AParameter and that is a legitimate parameter. It's a variable
that would take in input. But notice that we don't have what type of variable it
is, where it is in the position, whether it's mandatory or false.
So there's no control around this particular very simple parameter that
we've declared here. Because you're likely going to be expecting very
specific stuff from your user inside of your parameter block it's typically a
good idea to strongly type your parameter input to make sure that you're
getting the expected values from your user. So here we have the variable city
declared, it's always at the bottom. And you'll see that this is type string. I
personally find it easier to read these kind of things going backwards, and so we
have city, it's of type string, and then we have the parameter section here. And
this is giving us some validation and telling us a little bit about the
parameter. So we see this is going to be in the first position which starts at
zero, and it is not required. So the user has the option to either declare a city
or not. Our second parameter at the bottom is units. Now this is interesting
because it is equal to USCS, and what that means is that if the user does not
specify what the units is that means that is going to be by
default equal to USCS. The user of course can override this with a
different value but if they don't provide a value it's going to default to
this one. It's also going to be of type string, and notice that we have a
validate set. Again this is very specific syntax and something that you are
encouraged to look up if you ever have any questions about how to validate
input. Again leverage your VSCode editor. If you simply start typing in a
bracket and start typing in validate you'll notice that there are a wide
variety of different validation types that you can put inside of a parameter
to make sure you're getting the exact type of syntax, the exact pattern, or the
exact length that you are expecting your user to provide. So this validate set is
saying that only two values are allowed for this parameter. The user can specify
metric or they could specify us, for their weather information. And going back
to the first part this is going to be in position 1. We should be getting the hang
of reading these now and our third variable in our parameter section is
language. It is also going to default to English so if the user doesn't specify
one they'll get English back. And there are a couple different validation sets
for the various different language codes available in the world. And this is going
to be in parameter position two. And then last we have the short variable. This is
going to be slightly different, this is a switch variable and so it doesn't
contain a value. It will either be - short or - short will not be provided. So
this is a switch type variable. And this is in position 3. So again your parameter
section can contain as many variables as you like. They can be very simple, they
can be very complex, they can have relationships between each other. The
syntax in here can be a little bit challenging to memorize but as you write
more PowerShell you'll become more comfortable and again even I have to
look up the syntax of how to properly declare parameters from time to time. OK
let's go ahead and load all this into memory I'm gonna highlight the entire
function including the help. And I'm going to
click f8 on my keyboard which is going to load that function in its entirety
into memory. Now because this had help I'll be able to do Get-Help on Get-Weather. And one thing I can do if you're running on Windows, sorry Linux users I
don't think this will work for you. You can type in show window and that will
pop up an entirely separate window which will contain all of the help information
in a nice easy to read format. So here is our show window.
It contains the synopsis of what we're going to be doing as well as some
examples of how to run this. Okay so we'll pull that over to the right and
we'll try running this down in the console with Get-Weather. So if I do -
one of the parameters unsurprisingly is going to be city because that's our
first parameter that was declared in position 0. And we can do something like
San Antonio because that does take in a string and our units,
I'm not going to declare that I'll just give it leave it as US.
I'll leave our languages us as well because that's not a required parameter.
And I'll go ahead and click enter. And so notice that, I'm gonna click the arrow
here and so we can see this nice and big it's giving us a nice console based
weather report for San Antonio. If I clear the screen with cls I can try
again. Get-Weather, this time we'll do London
and this time we'll provide the short switch variable. And notice that that
gives us a shorter version of the output in the return. Again the logic that is
inside of the function is exactly like a script, or writing a PowerShell script.
And so there are different switches and if statements inside of this logic flow
that make certain adjustments based on the parameters that are provided. So for
instance you can see here that if the short variable is provided that the URI
string is gonna have a slightly different format
giving us a different output from the API. I'll go ahead and change that to
metric again giving short and now we see that in Celsius instead of Fahrenheit.
And we get a nice metric weather report for London.
So parameters are your way of taking dynamic input into your function and
doing things a little bit differently depending on what your user is after.
This is a very powerful way to really elevate your PowerShell code and empower
yourself and your users to do a wide variety of different things depending on
your needs. OK I'm going to click ctrl in on my vs code and that's going to open
up a new window and I'm going to paste in a function that uses the begin,
process, and end inside of the function logic. And we'll talk about what this
actually entails. So the primary reason that you would use begin process and end
inside of your PowerShell function is that you expect your function to support
pipeline input. Where your function is going to be doing something for every
single thing that comes inside of it. And so what this allows you to do inside of
your function is the begin section will execute and run only one time. The
process section will run each time it gets input inside of the pipeline, and
then end will do any kind of cleanup. This in theory can be a little bit hard
to grasp but I think once we demo it you'll start to see what this is
actually capable of doing. So again I will load this in the function entirely
in using f8 into memory inside the console below. And notice here that I
have a line which takes the numbers 1, 2, & 3 and pipes those into this function
name. Now our function does support a parameter block and again as I mentioned
before you can make your parameters very complex with lots of validation, or your
parameters can be very simple and just say I want a parameter of some input and
it needs to be of type string. And so our line down here will utilize that
variable $SomeInput and provide it the word test and we'll walk through
what this is going to accomplish using begin, process, and end. So I'm going
to run this singular line using f8. And we'll evaluate this output that we have
down here on the console. So notice that our begin section right here is that
begin the input is some input. And our input was test. That was the string we
passed in to that. And we see here that begin has access to that variable and
says begin the input is test. Note that begin only ran one time even though we
passed in three values into the pipeline. This is a one-shot deal and will run on
execution of the function, but only once. So it's a way for you to scaffold your
function or initiate a couple of different things inside of that begin
section. Once we hit the process we can use dollar sign underscore to access the
current value in the pipeline and notice that we output the value is and we see
the numbers 1 2 3 because that is what we put inside of the pipeline here at
the front. The numbers 1 2 & 3 and notice that because we put in three numbers
that process ran three times. The value is 1, the value is 2, and the value is 3.
And then finally we wrap up with the end section which just like the begin
running at the very end of the function once it's processed all of the pipeline
input. So this is when you would use begin, process, and end is when you expect
your function to take pipeline input and do some type of processing or function
for every single thing that comes into your function via the pipeline. Again I'm
going to click ctrl + n on my VSCode and open up a new tab and now we're going to
explore one of the more frustrating pieces about PowerShell functions, and
that is function scope. So I'm going to paste in a function here and this is
going to be Get-NumberTimesTwo. This is an advanced function we can see from the
CmdletBinding and it's going to take one parameter input of number. And if we take a brief look at the PowerShell functionality it's going to take that
number and multiply it by 2 and then return it to us. So I'm going to load
this into memory, click f8,
and I've got a line down here that says call the function, give it the number two,
and we'll leave the debug off for right now. And I'll click f8 and unsurprisingly
because I passed in the number two and it multiplied it by two, we get the number
four back. So pretty straightforward function, very basic. What I want to draw
your attention to is the scope of this function inside the console. We have a
variable here inside this function called $total. And if we pound out $total
on the console right now it's going to be null. And that's because our console
does not have access to this particular variable. It is not scoped to the console,
it is scoped the function. And so we do not have access to see what's going on
inside of that particular variable. If you're new to writing PowerShell
functions this can be a little bit frustrating because when you write a lot
of logic in here it can be troublesome to figure out where your bug is, or why a
value is a particular way when you don't expect it to be. Because you can't reach
into the code to determine what is $total actually at. There are a variety of ways
around this, and if you ask a bunch of different PowerShell people you'll get
different opinions about the best way to handle this. One thing you could do is
simply remove the function wrapper like this. And write your PowerShell logic
inside of a script type format until you feel like the logic is good.
That's one methodology. If you ran this in a script format you would have access
to all the variables, you would be able to troubleshoot a little bit easier, and
once you feel confident the code is solid you could re add your function
wrapper, and press on. If you're not at the point where you're writing tests for
your functions a better way is to use Write-Debug. So here because we're using
CmdletBinding we do get some additional advanced functionality and
one of those is Write-Debug. And Write-Debug can be used with the slash debug line and you can put in Write-Debug whatever you want.
So if you need to know at certain times during debugging what the value of $total
is, you can put that inside Write-Debug here and when you run the debug code
using f8 you'll notice that debug has the value of $total. So while you cannot
access $total because of the scope you can get access to the total value by
using Write-Debug. You can use other methodologies as well such as using
Write-Output or Write-Host to output the value if you need a quick way to
determine what's inside of that particular variable, but as a general
rule Write-Debug is probably the best way to go. OK we've covered a lot of
information let's wrap things up with a final example. And based on what we've
learned so far we should be able to evaluate this new example I've pasted in here pretty easily. Again we're not focusing on the
PowerShell logic, we're focusing on the constructs of the functions and how to
read them and understand them and set them up. So over here on the left in VSCode note that I can I get these drop down arrows when I hover over here on
the side. And I can minimize the entire function by clicking on that. I can even
minimize the help if I want to. And notice in this final example that I
actually have two functions. Remember functions can reference each
other and remember a function should do one thing and do it well. And so it's
very common practice when you're writing PowerShell functions that you might have
multiple functions inside of your script. So let me blow up this help real quick
and let's see what these two functions are all about. So based on the synopsis I
see that this function is named Get-Reddit and its purpose is to allow me to interactively browse reddit using PowerShell. That's further elaborated by
looking at the description which tells me that it makes a connection to Reddit,
it pulls down a JSON payload for a specified subreddit, cool! I've got some
examples. It looks like I can use Get-Reddit to surf the subreddit PowerShell,
or I could use it to surf the subreddit aww, or I'm imagining any other subreddit
that is on the reddit website. So based on the help I have some good
ideas about what this function is going to do. The second function Show-Pics the
synopsis says that it's going to launch the default browser to display reddit
pictures. Sso it looks like this particular function is the main function
that is going to be surfing reddit and it looks like based on this information
that the Show-Pics function will probably be engaged if what I'm surfing
contains a picture. Let's dive further into this function and we can see here
that because there is a CmdletBinding and a param block that this is an
advanced function. So it's going to support things like Write-Verbose, Write-Debug, and other advanced functionality. Looking over our param block very briefly
we have
a subreddit parameter which is mandatory. We have a threads parameter which is the
number of threads that will be pulled down, so the number of posts. It is not
mandatory and it will be set to default 3. And then we have a switch parameter of
show pics and it will show us pictures if they're available. While in this
episode we're not focusing on the PowerShell functionality it's always a
good idea when you're engaging a new function that you've never used before
to take a brief look over the functionality inside of the function to
determine what it's going to be doing. So we can look here in the Write-Verbose
that we're going to be visiting a URI using Invoke-WebRequest to visit the
reddit web site, doing some processing in order to convert the JSON into a
readable format, and then returning those results to the user. Let's go up and take
a look at Show-Pics now. Again this is an advanced function. We know how to determine that now.
And we know what that means. This has one parameter, it's going to be
URL. And it has a validation against it. It can only be i.redd.it, v.redd.it, or
imgur. So it must be a URL that contains a picture basically. Interesting this has
a begin, process, and end so that means this is going to support pipeline input which
means that it's going to be able to show us multiple pictures most likely. And
looking over the basic PowerShell functionality it's just going to Start-Process which is going to launch our default browser to the URL provided. So
if you wrote this and this was your PowerShell functions you could save this
as for example PS reddit, and this is a script. But if you were to hand this to
other people or wanted other people to engage with the script, when they load it
into their console now, this will load in PS reddit. Clear this out. Notice that
again the script doesn't do anything so your users are not surprised by it
automatically kicking off and prompting them. The functions are loaded into
memory but the users have to call them. And I think this provides a much better
experience for people that are engaging with your functionality. OK so we can do
Get-Help because this has help at the top of our functions and we can do Get-Reddit and I can do an example switch and I can see here that I can write Get-Reddit -subreddit PowerShell. I'll just go ahead and copy that example and paste it
into my window and click enter. And looking at this it looks like it gives
us back some text-based browser information from reddit. So today's top
posts are what have you done with PowerShell this month.
It's got 56 upvotes and 144 comments and if I find this interesting I could go
ahead and visit this particular link by clicking ctrl click. If we click our up
arrow and do - again we can specify the number of threads so we could pull
in for example 15 different posts from today's reddit PowerShell group and see
even more about what's going on in the PowerShell community.
Another parameter I have is show pics we'll go ahead and add that. And notice
that nothing happened most likely because none of these contain a picture.
Let's visit a subreddit that most likely will contain a picture, so we'll go to
get reddit subreddit aww, we'll do threads 2 show pics. And one of those is a cute
picture of a couple puppy dogs and the other one is of a nice puppy swimming
inside of a relaxing pool. So you could use this text-based browser to use
PowerShell to browse any subreddit that you were interested in. For example if
you were interested in tractors I'm sure there's an /r/tractors and so we could
visit that particular one and if we felt like that some of those had pictures we
could put show pics, and there you have it a couple pictures with tractors. And
if we come back to our function because this is an advanced function we do of
course get that verbose output. So again quickly recapping, it's always a really
fantastic idea to add help at the top of your functions. I think in this video
you've already seen how much of a difference that makes on approaching
what the function does and how to run it. Your functions should have a name that
adheres to approved PowerShell verbs so don't use set if your function is not
going to change something, you should use get. CmdletBinding is optional but
a good idea. It adds a lot of advanced functionality. Your parameters are also
optional. You may not need parameters, but if you do you can be very basic, you can
be very advanced. Your PowerShell function logic of course is the hardest
part. If it's going to support pipeline input use that begin process and end. And
return something to the user if your function is going to return something.
Not all functions do. Maybe your function just spins up a server, or deletes a file
and doesn't actually return anything to the user. Okay keep in mind, all of these
functions belong to you now. This code is available on the Learn PowerShell GitHub.
The link is in the description below. Go play with these functions! Download
them to your computer. Adjust things! Try to break something! Try to add some
additional functionality! Maybe an additional parameter. The more time you
spend with this type of stuff the better PowerShell developer that you'll become.
I hope that you found this episode on PowerShell functions helpful. If you
have any questions hit me up in the comments section below, I'll be happy to
address any questions that you might have! If you found this video helpful go
ahead and click that like button, and subscribe for more TechThoughts videos!