I have not been able to successfully run my PowerShell script from VBA with administrator privileges.
I have examined the various answers in this forum and attempted to adapt with no success.
When I execute the VBA script below, there is a window that will open for less than one second and then close (and I don't know if it is a Powershell or a Command window) and the script does not run as evidenced by not doing its job, and finishing immediately (not waiting the ten seconds). Also, there is nothing written to StdOut
or StdErr
If the script is run from an elevated command window, or an elevated Powershell window, it runs as designed.
The script is designed to temporarily disconnect the network adapter. It is simplified at this stage and only works on an adapter named Ethernet
.
Powershell Script
Disable-NetAdapter -name Ethernet -confirm:$false
Start-Sleep -Seconds 10
Enable-NetAdapter -name Ethernet -confirm:$false
Functioning elevated command window code:
C:\PowerShell Scripts>"disable network temporarily.ps1"
Non-Functioning VBA Code
'Needs reference to Windows Script Host Object Model
Option Explicit
Sub TempNetworkDisable()
Dim wshShell As wshShell
Dim wshShellExec As Object
Dim strCommand As String
Dim strOutput
strCommand = "pwsh.exe -c Start-Process -Verb RunAs pwsh.exe \"" -ExecutionPolicy Unrestricted -NoExit -f `\""C:\PowerShell Scripts\Disable Network Temporarily.ps1`\"""
Set wshShell = New wshShell
Set wshShellExec = wshShell.Exec(strCommand)
strOutput = wshShellExec.StdOut.ReadAll()
Debug.Print "StdOut:", strOutput
strOutput = wshShellExec.StdErr.ReadAll()
Debug.Print "StdErr:", strOutput
End Sub
The immediate problem with your approach is that you were missing an escaped closing "
:[1]
' Note the \"" before the closing "
' To run the elevated pwsh.exe process *synchronously*, i.e. to wait
' for it to exit, add -Wait before -Verb RunAs
strCommand = "pwsh.exe -c Start-Process -Verb RunAs pwsh.exe \""-ExecutionPolicy Unrestricted -NoExit -f `\""C:\PowerShell Scripts\Disable Network Temporarily.ps1`\""\"""
Note that if you run the resulting command line from a non-elevated process, you'll get an interactive UAC prompt to authorize creation of an elevated process (a process running with administrative privileges, as requested via Start-Process -Verb RunAs
).
The only way to avoid UAC prompts is to either run in an elevated process to begin with or to disable (parts of) UAC altogether, which is strongly discouraged.
Otherwise, there's only a limited workaround: Assuming that you are an administrator in principle, you can set up a scheduled task with a preconfigured command to be run with elevation, which you can then invoke on demand from a non-elevated process of yours without triggering a UAC prompt.
See this answer for more information.
However, this presents a follow-up problem: you're reading the stdout and stderr streams of the outer, transitory pwsh.exe
, which will be empty, given that the nested, elevated pwsh.exe
process launches in a new window, which means that its output streams aren't connected to the outer process.
Note:
pwsh.exe
call (the one that facilitates launching another pwsh.exe
process with elevation) nor your *.ps1
script are designed to produce output, so perhaps you don't need to solve this problem - assuming that no errors occur; if you do want to capture any errors, however, the problem does need solving.Your only option is to capture the elevated process' output streams in (temporary) files.
While Start-Process
does offer -RedirectStandardOutput
and -RedirectStandardError
parameters that accept file paths, they can not be used in your case, because these parameters syntactically cannot be combined with an elevation request (-Verb RunAs
), a restriction that is also reflected in the underlying .NET APIs (see this answer for details).
Thus, you'll need to capture output from inside the elevated process, by using redirections there, which necessitates switching from a -f
(-File
)-based to a -c
(-Command
)-based CLI call.
You'll then have to read the target files' content in your VBA code after the fact.
' Note the addition of -Wait to the Start-Process call,
' the switch to -c in the nested call,
' the use of & the call operator to invoke the script, the
' switch to '...' quoting around the script-file path,
' and the > and 2> redirections with sample output file names
' stdout.txt and stderr.txt
strCommand = "pwsh.exe -c Start-Process -Wait -Verb RunAs pwsh.exe \""-ExecutionPolicy Unrestricted -NoExit -c & 'C:\PowerShell Scripts\Disable Network Temporarily.ps1' >stdout.txt 2>stderr.txt\"""
' ...
' After synchronous execution (due to -Wait), read the output files, stdout.txt and stderr.txt
' Without -Wait, you'd have to track the lifetime of the elevated
' process manually.
[1] While your original code therefore had a syntax error, the way pwsh.exe
quietly ignored it is actually a bug in the latter, as of PowerShell 7.4.x; a simple repro: pwsh -nop -c "\"hi"
(from outside PowerShell; from PowerShell or a POSIX shell, use pwsh -nop -c '"hi'
); see GitHub issue #14284.
However, the failure is - abstractly - reflected in the process exit code, which is set to 1
.