3

I'm writing Cmdlets that interface with Google Apps API and require Oauth2 authentication. If a user tries to run a Cmdlet without being authenticated and it's more than just refreshing a token, I need them to enter in an access code provided from a browser. (That's all taken care of.) I'd like to pause the cmdlet when this is necessary, wait for their input and then only continue once verified.

However, I right now can only do this by throwing an error to quit the current Cmdlet and then instruct the user to pass the Access Code as an argument to another Cmdlet (clunky).

I've been looking for a way to call something like C#'s Console.Readline (this just gets skipped over when executing the cmdlet) or trying to invoke the Read-Host cmdlet; once I get the access code from the user the code could continue to execute as normal. I know it's not necessarily the 'Powershell Way' but for the sake of these tools, I feel it's the best option for the user.

If I try this method:

ReadHostCommand read = new ReadHostCommand();
read.prompt = "Access Code";
foreach (string s in read.Invoke<string>()) {
    WriteObject(s);
}

I get an error: Cmdlets derived from PSCmdlet cannot be invoked directly.

Search that error lead my to try creating a new Host object as follows I get another error (also following):

PowerShell ps = PowerShell.Create();
ps.AddCommand("Read-Host");
foreach (PSObject result in ps.Invoke()) {
    WriteObject(result);
}

Read-Host : A command that prompts the user failed because the host program or the command type does not support user interaction. Try a host program that supports user interaction, such as the Windows PowerShell Console or Windows PowerShell ISE, and remove prompt-related commands from command types that do not support user interaction, such as Windows PowerShell workflows.

Is there a way to pause the cmdlet to get a string from the user?

Edit: To clarify, I'm writing Cmdlets to be run from within Powershell Console or ISE or similar, and need to get input from the user mid-cmdlet-execution.

Community
  • 1
  • 1
squid808
  • 1,430
  • 2
  • 14
  • 31

2 Answers2

6

If your cmdlet derives from PSCmdlet then use the this.InvokeCommand.InvokeScript() to invoke the Read-Host cmdlet. Or you could use the this.Host.UI.Prompt*/Read*() methods.

Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • First method works, thank you so much! I would never have found this method, since the MSDN page only shows it as a property of PSCmdlet (I wouldn't have thought to drill down further). If anyone gets stuck like me, once I call it I'm able to access the input through something like `Collection results` and `results[0].ToString()` – squid808 Apr 04 '13 at 20:34
0

Sounds like you actually need to implement the PSHost (along with the PSHostUserInterface and PSHostRawUserInterface classes). In doing so, you will implement the functionality to interact with the user with read-host and write-host (probably using console.readline).

Mike Shepard
  • 17,466
  • 6
  • 51
  • 69
  • Would this work for Cmdlets that are already being run in Powershell? Whether I'm in the ISE or not, isn't Powershell already running it's own host? I thought going beyond what I did above was only for running PS cmdlets in C#, outside of a PS interface. – squid808 Mar 27 '13 at 20:13
  • The PowerShell engine isn't necessarily in a host. The ISE and the PS Console implement hosts, but a general C# app doesn't, unless it specifically implements those 3 interfaces. I have some tutorials (VB.NET/WinForms) on my blog at Powershellstation.com. – Mike Shepard Mar 27 '13 at 20:20
  • Right... if I understand it correctly, the PS Engine implements hosts, right? So I'm making custom Cmdlets, which will be run from within either the PS ISE or Console. In both cases, those already are implementing hosts. It's not like I'm making a C# app/program that is just calling on Cmdlets, I'm making _new_ cmdlets through C#. I also may be totally missing your point, if that's not what you're saying. Edit: I'm thinking you can't have multiple hosts at the same time. Is this the case? Can I make new hosts running parallel to what the ISE or console already run? – squid808 Mar 27 '13 at 20:23
  • Aha...if you're not running your cmdlets in your c# application then you can disregard. I misunderstood. – Mike Shepard Mar 27 '13 at 20:32
  • Gotcha. For clarification, I'm writing Cmdlets to be run in Powershell. One way or another, I need to gather input from the user; I've found that I can't simply use Console.ReadLine (boo!) and was hoping I could just invoke another cmdlet. But apparently the one cmdlet I want to invoke is just not gonna work with me. Thanks for the ideas! – squid808 Mar 27 '13 at 20:34