1

First of all, the commands that i want to execute are get-vm and start-vm, which are need admin permission. In my app , i will check the hyper-v vm state to see it Running or Off. thus , i need to invoke get-vm {vmName} the get the state. after got the vm state , if the state is Off, then i will invoke start-vm {vmName} to start the vm.

secondly, i use Process to start PowerShell with UserName and Password. the code is below:

        var tmp = CShortPath.GetShortPath(System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName()));

        var processInfo = new ProcessStartInfo
        {
            Verb = "runas",
            LoadUserProfile = true,
            CreateNoWindow = true,

            FileName = "powershell.exe",
            //Arguments = "Start-VM -name 'win11-Lite'",
            Arguments = $"get-VM 'win11-Lite' >{tmp}",
            RedirectStandardOutput = false,
            UseShellExecute = false,
            UserName = "{AdminUserName}",
            Password = MakePwd("AdminPwd")

        };
        Process.Start(processInfo);

as you see, when executed , the output file {tmp} is empty, and there will not display UAC Permission dialog. what should do ?

mklement0
  • 382,024
  • 64
  • 607
  • 775
goldii
  • 242
  • 2
  • 18
  • What is the exitcode of the resulting process? – Mathias R. Jessen Aug 08 '23 at 13:13
  • 1
    `UseShellExecute` needs to be `true` - see [this question](https://stackoverflow.com/questions/3596259/elevating-privileges-doesnt-work-with-useshellexecute-false) – stuartd Aug 08 '23 at 13:14
  • 1
    @stuartd, no UserName/Password needs UseShellExecute = false – goldii Aug 08 '23 at 13:19
  • @MathiasR.Jessen, the exitcode is 1. no error. – goldii Aug 08 '23 at 13:23
  • @goldii exitcode 1 corresponds with a conflict between the requested verb and lack of shell execute :) – Mathias R. Jessen Aug 08 '23 at 13:25
  • @MathiasR.Jessen, even-if i remove Verb = "runas", the exit code is still 1. please give more details. thanks. – goldii Aug 08 '23 at 13:30
  • Don't use `runas` in the first place. You aren't making a DDE call so that can only cause bugs. You don't need to start PowerShell itself, you can host it inside your application, with `PowerShell.Create`. Check the [PowerShell Host Quickstart](https://learn.microsoft.com/en-us/powershell/scripting/developer/hosting/windows-powershell-host-quickstart?view=powershell-7.3) – Panagiotis Kanavos Aug 08 '23 at 13:36
  • @PanagiotisKanavos, then you will got an error "Exception thrown: 'System.Management.Automation.Runspaces.PSSnapInException' in System.Management.Automation.dll An unhandled exception of type 'System.Management.Automation.Runspaces.PSSnapInException' occurred in System.Management.Automation.dll System error." – goldii Aug 08 '23 at 13:39
  • @PanagiotisKanavos, this is demo code from mcrosoft , which caused error; ` using (PowerShell powershell = PowerShell.Create().AddCommand("get-process")) { WriteLog("Process HandleCount"); WriteLog("--------------------------------"); foreach (PSObject retx in powershell.Invoke()) { var txt = $"{retx.Members["ProcessName"].Value} - {retx.Members["HandleCount"].Value}"; WriteLog(txt); } } return;` – goldii Aug 08 '23 at 13:50
  • That means you have no permissions to use Powershell. Because the sample works just fine. Is that why you try to pass a username and password? You can't just pass a password to `Process` to start it a process as another user. That's not how Windows works. – Panagiotis Kanavos Aug 08 '23 at 13:51
  • @PanagiotisKanavos, when execute demo code , it will failed . PowerShell.Create().AddCommand("Get-Process").AddParameter("Name", "PowerShell").Invoke(); i don't know why. the code is from https://learn.microsoft.com/en-us/powershell/scripting/developer/hosting/windows-powershell-host-quickstart?view=powershell-7.3 – goldii Aug 08 '23 at 14:07
  • @PanagiotisKanavos, the PowerShell code must be run from an _elevated_ process, hence goldii's attempt to use `.Verb = "RunAs"`. The problem is that this fundamentally _cannot be combined with providing user credentials_ (`.UserName`, `.Password`), i.e you cannot directly launch an elevated process with a given (usually different) user identity. Here, the idea behind providing credentials may be to _bypass UAC_, which is (sensibly) not supported either. – mklement0 Aug 08 '23 at 14:27
  • @PanagiotisKanavos: There is no need to use different credentials: just combine `.Verb = "RunAs"` with `.UseShellExecute = true`, and the UAC dialog will do the rest (mere _confirmation_ if the current user is an administrator or prompt for an _administrator's password_ ). Of course, the alternative is to launch the C# application _from an already elevated session_ (which the application could enforce). – mklement0 Aug 08 '23 at 14:32

2 Answers2

2

First of all, you need to check the powershell commands/scripts first.

Then you need to know that the admin permissions are not mandatory to control vms. You can grant any user or group the required rights to control vms.

Then you can redirect the standard output instead of using an intermediate file (Microsoft sample below):

      p.StartInfo.UseShellExecute = false;  
      p.StartInfo.RedirectStandardOutput = true;  
      p.StartInfo.FileName = "Write500Lines.exe";  
      p.Start();  

      // To avoid deadlocks, always read the output stream first and then wait.  
      string output = p.StandardOutput.ReadToEnd();  
      p.WaitForExit();
rotabor
  • 561
  • 2
  • 10
2

Note:

  • If the intent behind the unsupported attempt to supply credentials programmatically via .UserName and .Password in combination with creating an elevated process (.Verb = "RunAs") is to bypass UAC, i.e. to avoid an interactive confirmation/authorization prompt, this fundamentally won't work, for security reasons.[1]

  • Launching an elevated process may not even be necessary if you have the option to modify permissions to also allow non-administrators to control Hyper-V VMs - see rotabor's answer.


When using a ProcessStartInfo instance to launch a process:

  • .Verb = "RunAs" (starting a process with elevation (elevated privileges, as administrator) requires .UseShellExecute = true
  • However, .UseShellExecute = true cannot be combined with .UserName and .Password

This means:

  • You can only launch an elevated process (.Verb = "RunAs", combined with .UseShellExecute = true) as the current user.

    • To launch an elevated process as a different user, you need nested processes:

      • A non-elevated helper process that you launch with the desired credentials. That is, omit .Verb = "RunAs".

      • A nested, elevated process created in the context of the non-elevated process' user identity. Since you're calling PowerShell, you can use Start-Process -Verb RunAs in your PowerShell CLI call.[2]

  • However, you will ultimately invariably get a UAC dialog.

    • That is, by design you can never bypass UAC programmatically, even if you supply an administrator's credentials.

    • If you still decide to specify administrator credentials programmatically, the only thing you gain is that the unavoidable UAC prompt becomes a mere confirmation prompt rather than having to provide an administrator's (username and) password when prompted.


[1] It is technically possible to disable UAC, but doing is strongly advised against, as it would leave your system highly vulnerable.

[2] Start-Process builds on the .NET ProcessStartInfo class, so it is subject to the same fundamental limitation: -Verb RunAs cannot be combined with -Credential.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • thanks for your detailed information。 It's very important to me. as you said, it means there's no way to ignore UAC dialog. It's very bad when i want to start a vm , it will need confirm twice , it the vm is Off. – goldii Aug 08 '23 at 14:04
  • @goldii, hypothetically you can turn off UAC, though that would be very ill-advised. I think the answer now has all angles covered. Let us know if you think something's still missing. – mklement0 Aug 08 '23 at 14:42