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:
- 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?
- 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
, andStart-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.