When you are building a PowerShell script, there might be situations where you want to prompt for a piece of data. Some cmdlets like Get-Credential do that sort of task implicitly. You can also get prompting if you define a mandatory parameter in a function or script. Otherwise, we tend to rely on PowerShell’s Read-Host cmdlet. With this cmdlet, you provide a prompt message and PowerShell writes the object back to the pipeline.
$r = Read-Host "Enter computername"
The variable $r now contains whatever was entered at the prompt.
By default, the cmdlet writes a string to the pipeline. Although I haven’t seen the source code for Read-Host, it’s most likely implementing the ReadLine() method of the built-in $host.ui object. I can simulate the Read-Host cmdlet:
I show this to you to not only demonstrate how using a cmdlet is better than re-inventing the wheel, but I’m also showing this to you I found another method that doesn’t appear to have any native cmdlet equivalent, and it’s something you might find useful.
The $host.ui object includes a method called Prompt(). The method requires three parameters: a prompt title, the message to display, and a key name. The last piece may puzzle you. The Prompt() method will create a Collections object, which is like a hashtable. The key name you specify will be used in the output. Here’s a simple example.
$r = $host.ui.Prompt("SERVER REPORTING","Enter a computername","Name")
I saved the results to a variable, which looks like a hashtable.
I could then use this object in my script or PowerShell session by referencing $r.Name. You have to be careful because unlike a normal hashtable, the key name is case-sensitive.
Now let’s make it interesting. It turns out you can use one prompt for multiple entries. All you need to do is specify an array of key names.
$r = $host.ui.Prompt("SERVER REPORTING","Enter values for these settings:",@("Computername","Domain","Operating System","Version"))
But wait it gets better. Even though $r is this fancy Collections object, I can still turn it into a PowerShell object using New-Object.
$s = New-Object -TypeName PSObject -Property $r
When referencing property names via the object, you also no longer have to worry about case.
Of course, what would make this all much easier to use is function, so here you go:
Function Read-MyHost { [cmdletbinding()] Param( [Parameter(Position=0,Mandatory,HelpMessage="Enter the message prompt.")] [ValidateNotNullorEmpty()] [string]$Message, [Parameter(Position=1,Mandatory,HelpMessage="Enter key property name or names separated by commas.")] [System.Management.Automation.Host.FieldDescription []]$Key, [Parameter(HelpMessage = "Text to display as a title for the prompt.")] [string]$PromptTitle = "", [Parameter(HelpMessage = "Convert the result to an object.")] [switch]$AsObject ) $response = $host.ui.Prompt($PromptTitle,$Message,$Key) if ($AsObject) { #create a custom object New-Object -TypeName PSObject -Property $response } else { #write the result to the pipeline $response } } #end function
The title, while required for Prompt method, can be an empty string, so I didn’t make it a mandatory parameter. You probably also noticed the type name for the Key parameter. The Prompt() method is looking for parameters of this type so I’ll have PowerShell force them to be so. I also added a switch to write the result as an object to the pipeline to save a step.
Here’s a simple test:
Notice that I didn’t have to specify a prompt title. But you can.
I should also point out that with both the collection output or the object, you can always change the values after the fact.
I hope you find this function useful and if so, I certainly hope you will let me know how you are using it. In the meantime, continue to poke around in PowerShell and especially with Get-Member. You never know what you might discover.