PowerShell How-To

Use PowerShell Parameters Instead of Interactive Prompts

Increase the usage of your code snippets by creating them with a universal approach.

Each of us has our own unique way of solving a problem with PowerShell. We all tend to code how we think; linearly. Our brains go from point A to point B -- sending those messages to our fingers -- and ultimately the code follows. The problem with this approach is that we tend to group distinct code snippets all together when we should be treating the code like distinct building blocks. Let me give you an example.

Let's say I want to query a server to determine whether any of the services are running under a certain EXE. I'm thinking ahead and I intend on making this script query not just a single server, but multiple servers and different EXEs. (I'm writing a more flexible script now in case I need this functionality later.) To do this, I'll start building my script by getting some input from the user for a group of server names and getting the EXE file name. I might as well provide the user with some feedback about what it's doing, so I'll add a Write-Host line after the script has captured the input.

$Servers = Read-Host -Prompt 'Please input one of more server  names separated by a comma'
$ExeFile = Read-Host -Prompt 'Please input the EXE file name to search for'
Write-Host "You're querying server '$Servers' for the EXE file '$ExeFile'"

When the script is run, the user can input the server name and the EXE file she's looking for. Yay!

[Click on image for larger view.] 

Now I have a way to get variable input into my script. The next step is to actually do something with this input. Because I'm trying to get the actual EXE files that are running my services, I can't use the Get-Service cmdlet, unfortunately. Instead, I'm forced to use CIM. So let's add a step to the script that finds all the services behind my EXE file on all the servers I've specified.

$Services = Get-CimInstance -Computername $Servers -ClassName  Win32_Service -Property DisplayName,PathName | Select-Object  DisplayName,PathName
$Services | Where-Object {$_.PathName -like '*$ExeFile'}

We should now have a functioning script. Let's run it again and see what I come up with.

[Click on image for larger view.] 

Great! You can see I was able to query my server, named LABDC, for any of the services running under an EXE file named Microsoft.ActiveDirectory.Webservices.exe. I've completed the task at hand, and I'm ready to go about my day.

Two months later …

Again, a need has come up to find the EXE files behind services. But this time, a coworker has given me a list of server names in a text file. So how do I feed these server names into my script? You'll recall I only had a single server previously, so I could just prompt for the name and type it in. This time, I've got dozens of server names, and I'm not about to copy and paste all those names one at a time into this script. This is the downside of creating interactive prompts inside your script. They completely box in your script, keeping it from other uses.

Here's how this script should have been written from the beginning.

param([string[]]$Servers,[string]$ExeFile)

$Services = Get-CimInstance -Computername $Servers -ClassName Win32_Service -Property DisplayName,PathName | Select-Object DisplayName,PathName
$Services | Where-Object {$_.PathName -like "*$ExeFile

Notice how I've turned the two Read-Host lines into parameters of the script itself? By converting the interactive prompts to parameters, I can now support one or many servers, which will provide just about any kind of input I need. In this case, it's a text file full of server names.

By changing those interactive prompts to parameters, I can now specify a server as well as the EXE file in a single like the below screenshot.

[Click on image for larger view.] 

I can also easily adapt our script to multiple servers in a text file as shown in the below screenshot.

[Click on image for larger view.] 

The key here is to avoid using interactive prompts unless you absolutely need them. Instead, use parameters. If you must use interactive prompts, try to separate them as much as possible from your "working" code. Perhaps you can include your prompts in a function you can easily swap out -- or put them in a separate script altogether. Go for as much separation as possible to make modifying your script later as easy as possible.

About the Author

Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.


comments powered by Disqus
Most   Popular