Home > OS >  Obtain and use UAC credential in Powershell to run job
Obtain and use UAC credential in Powershell to run job

Time:05-20

I'm writing a configuration script that has to do a few things that require UAC approval, and I was hoping ideally to achieve approximating "$session = sudo -v" so that I can mix elevated/non-elevated and not require the user to sit at the screen for the 15-30 minutes setup takes just in-case it needs another approval.

Ideally, I'm trying to avoid opening a new console/terminal window since this both loses output and causes screen-flashing that could be problematic for some of my users.

Which suggests that what I may want to do is Start-Job with a credential so that I can wait for completion and retrieve the output.

So now I have two problems:

  1. Is there a way to in-session store the user's UAC consent, e.g by using it to get a credential I can reuse?
  2. What Start- or Invoke- command can I use to run a scriptblock with the elevation that won't convert the block to a string?

My attempts to use a ScriptBlock failed because of the need to multiply-escape strings that are going into a ScriptBlock construction:

{ [System.Environment]::SetEnvironmentVariable("TEST", "OK", "Machine") }

fails for me when I try to pass it to Start-Process -verb runAs powershell -ArgumentList @('-NoLogo', '-NoProfile', '-Command', $ScriptBlock):

At line:1 char:46
  [System.Environment]::SetEnvironmentVariable(TEST, OK, Machine)
                                               ~
Missing ')' in method call.
At line:1 char:46
  [System.Environment]::SetEnvironmentVariable(TEST, OK, Machine)
                                               ~~~~
Unexpected token 'TEST' in expression or statement.
At line:1 char:50
  [System.Environment]::SetEnvironmentVariable(TEST, OK, Machine)
                                                   ~
Missing argument in parameter list.
At line:1 char:63
  [System.Environment]::SetEnvironmentVariable(TEST, OK, Machine)
                                                                ~
Unexpected token ')' in expression or statement.
      CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
      FullyQualifiedErrorId : MissingEndParenthesisInMethodCall

or if I try to use System.Diagnostics.Process.

CodePudding user response:

  • No, there is no way to cache a UAC confirmation, unfortunately.

    • Conceivably - though I don't think it is worth it - you could start a single, elevated session in the background in which you roll your own IPC, such as by reading commands from a file and saving the results back to a different file, in coordination with the foreground session.
  • Invocation with elevation requires the use of Start-Process, with -Verb RunAs, and Start-Process fundamentally only supports string arguments (a process command line).

    • A string that is to be passed to the PowerShell CLI's (possibly positionally implied) -Command / -c parameter requires " chars. that are to be preserved as part of the command being passed to be escaped as \" (see this answer for an explanation); applied to your call:

      Start-Process -Verb RunAs powershell -ArgumentList @(
        '-NoLogo', 
        '-NoProfile', 
        '-Command', 
        "`"$($ScriptBlock -replace '"', '\"')`""
      )
      
    • Caveat: While passing the pass-through arguments individually to -ArgumentList, as above, may be conceptually preferable, a long-standing bug unfortunately makes it better to encode all arguments in a single string - see this answer; the bug is also the reason for enclosing the -Command argument explicitly in (outer, unescaped) embedded " above; while it'll typically work without that, it prevents runs of multiple spaces from getting folded into a single space, which could matter in embedded string literals.

  • Related