PowerShell Functions

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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!
Info
Channel: TechThoughts
Views: 11,921
Rating: 4.9717317 out of 5
Keywords: PowerShell, Learn PowerShell, PowerShell Training, learn powershell online free, learning PowerShell, PowerShell Tutorial, PowerShell Examples, Writing PowerShell, How To Write PowerShell, PowerShell Functions, PowerShell Function, How to Write PowerShell Functions, Function Help, CmdletBinding, parameters
Id: qrwPvbCWRtI
Channel Id: undefined
Length: 36min 5sec (2165 seconds)
Published: Sat Jul 04 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.