- Create a certificate-signed RDP shortcut via Group Policy - Fri, Aug 9 2019
- Monitor web server uptime with a PowerShell script - Tue, Aug 6 2019
- How to build a PowerShell inventory script for Windows Servers - Fri, Aug 2 2019
A good PowerShell automation script typically requires no interactivity. After all, if a script is interactive, that means a human must be in front of it to press a key. We try to stay away from that by defining all the scenarios ahead of time and feeding parameters into our scripts at runtime with no interaction. However, there are times when we need to gather input where we can't define scenarios ahead of time or we're building scripts for end users.
PowerShell has a few ways to introduce interactivity into our scripts. Today, we're going to discuss two of them: using the Read-Host command and creating our custom selection menu using the .NET System.Management.Automation.Host.ChoiceDescription object.
Prompting for user input with Read-Host ^
The easiest and most common way to add interactivity to your scripts is by using the built-in Read-Host command. This command pauses the script wherever it occurs and expects some input. By default, it will simply stop code execution with no indication of what's going on besides a flashing prompt. So it's important to use the Prompt parameter, which displays some text before it prompts for input.
You can see from the screenshot above that when input occurs, the command then returns that input. I typed Red as input, and it returned Red. This doesn't do much good if we're not capturing that input somehow and making a decision from it. It's best we send that output to a variable. At that point, we can then perform a series of checks against the input provided.
PS C:\> $favColor = Read-Host -Prompt 'What is your favorite color?' What is your favorite color?: Red PS C:\> $favColor Red
Now you can see it didn't simply return Red to the console but captured it with the $favColor variable. At this point, we can add some logic in there to do different things based on that input.
if ($favColor -eq 'Red') { 'My favorite too!' } else { "Eww...$favColor is not a nice color!" } My favorite too!
Create a menu with the .NET ChoiceDescription class ^
Read-Host is versatile and can prompt for any text, but it doesn't look "official." It doesn't look like it is a part of PowerShell itself. For a more professional way of asking for input (multiple inputs), we can use the .NET ChoiceDescription object. This object allows us to define different options and then use a .NET method called PromptForChoice to display those options to the user.
This method of asking for input requires defining all the options ahead of time by creating multiple ChoiceDescription .NET objects. We then pass those objects to another .NET object that displays them in the console and asks the user to choose.
The .NET method that shows the options is called $host.ui.PromptForChoice(). This method requires four arguments to run: the title for the menu of options, the message to display, an array of System.Management.Automation.Host.ChoiceDescription objects, and a default choice if the user simply hits Enter.
First, we'll define all the options. Using the previous example, let's limit the number of favorite colors to Red, Blue, and Yellow. To do that, we'll have to create three objects each representing a choice.
$red = New-Object System.Management.Automation.Host.ChoiceDescription '&Red', 'Favorite color: Red' $blue = New-Object System.Management.Automation.Host.ChoiceDescription '&Blue', 'Favorite color: Blue' $yellow = New-Object System.Management.Automation.Host.ChoiceDescription '&Yellow', 'Favorite color: Yellow' Once we've defined all the options, we'll group them all into an array. $options = [System.Management.Automation.Host.ChoiceDescription[]]($red, $blue, $yellow)
Finally, we'll display them to the user, first defining the prompt title and message.
$title = 'Favorite color' $message = 'What is your favorite color?' $result = $host.ui.PromptForChoice($title, $message, $options, 0)
When run from the console, it'll then display a prompt that looks like it came from PowerShell itself.
Favorite color What is your favorite color? [R] Red [B] Blue [Y] Yellow [?] Help (default is "R"):
And if you run the above code in PowerShell ISE, a graphical dialog box will appear:
The value of $result isn't the text specified; it's an index number starting at 0. So, for example, if you chose Red, the result would be 0, or if you chose Yellow, the value would be 2. You could make this distinction by using a switch statement to make a decision based on the input you provided.
Subscribe to 4sysops newsletter!
switch ($result) { 0 { 'Your favorite color is Red' } 1 { 'Your favorite color is Blue' } 2 { 'Your favorite color is Yellow' } }
Fantastic, Complicated scenario explained in a simple way. Thanks Adam for your post
This is very helpful! Is there any way to order the choices in a list vs a table?
Very helpful! I have also found Out-GridView with text files to be extremely helpful for a quick and easy menu based system including nested groups (plus it allows changes to the menu/text file without having to open your script). It’s also flexible in that you can limit the response to a single selection or multiple, depending on your needs.
For example, to force a single selection you could use:
To allow multiple selections using the standard CTRL-click/SHIFT-click options you could use:
Or use both of these in combination (first the user selects the group which determines which file to display its contents). It’s not perfect but very flexible. You’ll need to include error handling (e.g. verify the files exist; was anything selected at all) but otherwise it works in a pinch.
I didn’t get any pop-up box (using PS 5.0 on Windows 10).
A simpler and less redundant way to provide switch UI, this works in the CLI as a choice, or in ISE as a message box. There are other ways to pull this, you can use one of the other methods PSCmdlet SupportsShouldProcess I think does this same thing, but I'm not super certain.
“`
switch ( $host.UI.PromptForChoice(
'Stuff-Happening' ,
'Do you want to make stuff happen?' ,
[ System.Management.Automation.Host.ChoiceDescription [] ]@(
'&Heck yeah!' ,
'&Not really'
'&Only if it works) ,
[int] 0 ) )
{
0 { echo "You chose Heck yeah!" }
1 { echo "You chose Not really, you might like doing things the long way. And, that's ok." }
2 { echo "If it works, then I guess I may use this method instead. Arrays are pretty cool." }
}
“`
Tried to post this on Stack Overflow and it got about -8 points in about 45 minutes, not sure why. It's less redundant and does the same thing without tying up any variables.
With the myriad of enteprise extensible applications, integrations/services, etc. assigned to an Engineer/Administrator spectrum of responsibility(ies)…Automation using PowerShell scripts/modules can lighten the workload, sometimes removing a tedious "hat" from being worn; provided the/a script/module has been supplied with succinct and exacting documentation, written to allow the "proverbial" least-experienced script-user/co-worker a means to suss out before execution.
Perhaps that explains the diminutive feedback via stackOverflow?
With the myriad of enterprise extensible applications, integrations/services, etc. assigned to an Engineer/Administrator spectrum of responsibility(ies)…Automation using PowerShell scripts/modules can lighten the workload, sometimes removing a tedious "hat" from being worn; provided the/a script/module has been supplied with succinct and exacting documentation, written to allow the "proverbial" least-experienced script-user/co-worker a means to suss out before execution.
Perhaps that explains the diminutive feedback via stackOverflow?
Rather than using a switch to get the original label text, you can instead look back at the $options variable and pick the index of the result from it: