Summary: Learn how to write Windows PowerShell functions that are reusable so that you increase your productivity.
Microsoft Scripting Guy, Ed Wilson, here. Our guest blogger today is Andy Schneider.
Andy Schneider is a systems engineer at Avanade, a company that was started in 2000 to merge leading enterprise technology solutions with cutting-edge business consulting capabilities. Andy is a member of the IT Infrastructure Hosting team for Avanade's IT services. He focuses on virtualization using Hyper-V and has done a lot of work with NetApp storage devices. Andy uses Windows PowerShell on a daily basis to manage Hyper-V, Microsoft clusters, and NetApp storage devices. When Andy is not writing Windows PowerShell code, you can find him spending time with his beautiful wife and two daughters or working out at Kirkland Crossfit.
The Scripting Games, year after year, continue to be a great opportunity for people to learn new skills, get involved in a great community, and possibly win some really cool prizes. In the last several years, I have had the privilege of participating in the games as a contestant and as an author of an “Experts Answer.” This year, though, I had the privilege of judging the entries. I saw a lot of great scripts, innovative problem solving, and definitely learned a lot myself.
One of the key criterion judges were focusing on this year is reusable code. The idea of reusable code is fairly straightforward. If you are writing a function, could you, or someone else, use the function in other scripts or modules?
There is a little bit of Windows PowerShell history that can provide a bit more context. When Windows PowerShell was first released as a Community Technology Preview (CTP), its name was “Monad.” There was a lot of thought and reasoning behind this name. Gottfried Leibniz was a German mathematician and philosopher. He wrote a paper called Monadology. The idea of Monadology is fundamental to how Windows PowerShell works. For more information, see Monad Manifesto—the Origin of Windows PowerShell as posted by Jeffrey Snover. To quote a footnote from Jeffrey:
“Monads are Leibniz’s term for the fundamental unit of existence that aggregates into compounds to implement a purpose. In this philosophy, everything is a composition of Monads. This captures what we want to achieve with composable management.”
So what does all this have to do with reusable code? Functions, like cmdlets, are “Monads.” They represent some small piece of functionality that can be combined together with other pieces of functionality to create a solution. The beauty of Windows PowerShell is that it allows you to string together all these pieces of basic functionality in new and innovative ways to solve really large, complex problems.
Here are a few things I try to think about as I am writing a function that will be reusable:
- Functions should be short and sweet. Functions should do one thing and do it very well. If your function is implementing multiple layers of processing and logic, you should probably break it down into smaller functions. Excluding parameters and comments, I would suggest that the main body of a function should rarely be more than 10 or 12 lines of code. If it is longer than that, ask yourself if the steps can be broken down further.
- Functions should be pipeline friendly. Functions should take input objects from the pipeline and also write objects to the pipeline. A reusable function should never use Write-Host, unless you are writing a function specifically to format some special format. Remember that something that is output with Write-Host is not put in the pipeline.
- Use Parameters wisely. Parameters should generally always have default values. For example, a Get- function should always return a value. Parameters can be used to allow a user to filter the output from the cmdlet. Think about how Get-Process works. You can specify a particular process, but if you do not, they are all returned. As you are writing your function and you declare a variable, you should ask yourself if this is something the end user may want to change. If so, provide a good default value that most people will use, but allow the end user to change it if they want to.
- Functions should be named properly. Make sure that you use an accepted verb and well thought out noun for your function. If you adhere to these standards, it will be easier for others to incorporate your functions into their code. Also, it will make your function more discoverable and the end users will have a good idea of what to expect when they see your function’s name.
I will admit that there is a bit of a learning curve here. You will find that after you write 10 or 20 functions, you will start to develop your own style. Your 20th function will be a lot cleaner and more reusable than your first. The great thing about code is you can go back to your first functions and update them with the knowledge you have accumulated.
As an example, several years ago, NetApp released a .NET SDK that could be used to manage their SANs. This was a year or so before they released their Windows PowerShell Toolkit. I ended up writing a couple functions that used the SDK to do two different things on a NetApp. The first version of this code was extremely rough. I had a lot of information hard coded into my functions, and they were not pipeline friendly. Also, if I wanted to add a new function to take advantage of another part of the SDK, I had to basically start from scratch.
Over a period of three or four months, I ended up writing a few basic helper functions that could be used generically across the entire SDK. When I did this, I was able to leverage the NetApp SDK with incredible ease. Rather than spending two or three hours figuring out some new API call, I was able to write a complete function for that API in under 15 minutes. It took me three months to write three or four functions, but then I was able to write the next 15 functions in a matter of hours.
There might be some frustration up front, but the process of going back and updating code and learning and improving over time will absolutely pay off in the end. Just know that it may be weeks or months for the payback to kick in. Trust me though—it is well worth the investment.
Thank you, Andy, for this pragmatic discussion on writing reusable code.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at firstname.lastname@example.org, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy