Запуск PowerShell из VBA с правами администратора

Вопрос или проблема

Мне не удалось успешно запустить мой скрипт PowerShell из VBA с правами администратора.

Я изучил различные ответы на этом форуме и пытался адаптировать, но безуспешно.

Когда я выполняю скрипт VBA ниже, открывается окно, которое открывается менее чем на секунду и затем закрывается (и я не знаю, является ли оно окном PowerShell или командным окном), и скрипт не выполняется, что подтверждается тем, что он не выполняет свою работу и завершается сразу (не дожидаясь десяти секунд). Также ничего не записывается в StdOut или StdErr

Если скрипт запущен из повышенного командного окна или повышенного окна PowerShell, он работает как задумано.

Скрипт предназначен для временного отключения сетевого адаптера. На данном этапе он упрощен и работает только с адаптером, названным Ethernet.

Скрипт PowerShell

Disable-NetAdapter -name Ethernet -confirm:$false
Start-Sleep -Seconds 10
Enable-NetAdapter -name Ethernet -confirm:$false

Код работающего повышенного командного окна:
C:\PowerShell Scripts>"disable network temporarily.ps1"
вставьте описание изображения здесь

Не работающий код VBA

' Требуется ссылка на модель объекта Windows Script Host
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
  • Windows 11 Pro
    • Версия 10.0.22631 Сборка 22631
  • PSVersion 7.4.6
  • Microsoft® Excel® для Microsoft 365 MSO (Версия 2409 Сборка 16.0.18025.20160) 64-бит
  • VBA 7.1.1143
  • непосредственная проблема с вашим подходом в том, что вы пропустили экранированную закрывающую ":[1]

    ' Обратите внимание на \"" перед закрывающим "
    ' Чтобы запустить процесс pwsh.exe с повышенными правами *синхронно*, т.е. дождаться
    ' его завершения, добавьте -Wait перед -Verb RunAs
    strCommand = "pwsh.exe -c Start-Process -Verb RunAs pwsh.exe \""-ExecutionPolicy Unrestricted -NoExit -f `\""C:\PowerShell Scripts\Disable Network Temporarily.ps1`\""\"""
    
    • Обратите внимание, что если вы запускаете полученную командную строку из процесса без повышенных прав, вы получите интерактивное UAC сообщение для авторизации создания повышенного процесса (процесса, работающего с административными правами, как запрошено через Start-Process -Verb RunAs).

    • Единственный способ избежать запросов UAC – это либо запускать изначально в повышенном процессе, либо отключить (части) UAC полностью, что настоятельно не рекомендуется.
      В противном случае существует только ограниченный обходной путь: Предполагая, что вы администратор по сути, вы можете создать плановую задачу с предварительно настроенной командой для выполнения с повышенными правами, которую вы затем можете вызывать по мере необходимости из вашего процесса без повышенных прав, не вызывая запроса UAC.
      См. этот ответ для получения дополнительной информации.

  • Тем не менее, это представляет собой последующую проблему: вы читаете потоки stdout и stderr внешнего, временного pwsh.exe, который будет пустым, так как вложенный, повышенный pwsh.exe процесс запускается в новом окне, что значит, что его выходные потоки не связаны с внешним процессом.

    • Примечание:

      • Ни ваш внешний вызов pwsh.exe (который облегчает запуск другого процесса pwsh.exe с повышенными правами), ни ваш скрипт *.ps1 не предназначены для вывода, так что, возможно, вам не нужно решать эту проблему – при условии, что нет ошибок; если вы хотите захватить любые ошибки, проблема действительно требует решения.
    • Ваш единственный вариант – захватить выходные потоки повышенного процесса в (временные) файлы.

    • Хотя Start-Process и предлагает параметры -RedirectStandardOutput и -RedirectStandardError, которые принимают пути к файлам, они не могут быть использованы в вашем случае, потому что, предположительно, по соображениям безопасности, процессам без повышенных прав не разрешается захватывать вывод из повышенных.

    • Таким образом, вам нужно будет захватить вывод внутри повышенного процесса, используя перенаправления там, что требует переключения с вызова командной строки на основе -f (-File) на вызов на основе -c (-Command).

    • Затем вам нужно будет прочитать содержимое целевых файлов в вашем коде VBA после выполнения.

' Обратите внимание на добавление -Wait к вызову Start-Process, 
' переключение на -c во вложенном вызове, 
' использование & оператора вызова для вызова скрипта, 
' переключение на '...' заключение вокруг пути к файлу скрипта,
' и перенаправления > и 2> с примерами имен выходных файлов
' stdout.txt и 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\"""

' ...

' После синхронного выполнения (из-за -Wait) прочитайте выходные файлы, stdout.txt и stderr.txt
' Без -Wait вам придется вручную отслеживать время жизни повышенного
' процесса.

[1] Хотя ваш оригинальный код имел синтаксическую ошибку, способ, которым pwsh.exe тихо игнорировал ее, на самом деле является ошибкой в нем, начиная с PowerShell 7.4.x; простой репродукция: pwsh -nop -c "\"hi" (снаружи PowerShell; из PowerShell или POSIX шелла используйте pwsh -nop -c '"hi'); см. GitHub issue #14284.
Тем не менее, сбой отражается – абстрактно – в коде выхода процесса, который установлен в 1.

Ответ или решение

Запуск PowerShell из VBA с правами администратора

Запуск PowerShell скрипта с привилегиями администратора из VBA может стать непростой задачей, особенно если ваша первоначальная попытка не увенчалась успехом. Давайте рассмотрим, как правильно организовать этот процесс с учётом всех необходимых деталей. Эта инструкция будет полезна для разработчиков и системных администраторов, работающих с VBA, PowerShell и Windows.

Шаг 1: Подготовка вашего PowerShell скрипта

Исходный скрипт, который вы хотите выполнить, выглядит следующим образом:

Disable-NetAdapter -name Ethernet -confirm:$false
Start-Sleep -Seconds 10
Enable-NetAdapter -name Ethernet -confirm:$false

Этот скрипт отключает и затем включает сетевой адаптер с именем "Ethernet". Убедитесь, что он работает правильно при запуске из командной строки или PowerShell с правами администратора.

Шаг 2: Структура вашего VBA кода

Ваша задача состоит в том, чтобы грамотно запустить PowerShell из Excel VBA с нужными правами. Вот пример кода, который был скорректирован для корректного выполнения:

Option Explicit

Sub TempNetworkDisable()
    Dim wshShell As Object
    Dim strCommand As String
    Dim strOutput As String

    ' Путь к вашему PowerShell скрипту
    Dim scriptPath As String
    scriptPath = "C:\PowerShell Scripts\Disable Network Temporarily.ps1"

    ' Формирование команды для запуска PowerShell с правами администратора
    strCommand = "pwsh.exe -c Start-Process -Wait -Verb RunAs pwsh.exe ""-ExecutionPolicy Unrestricted -NoExit -c & '" & scriptPath & "' > stdout.txt 2> stderr.txt"""

    ' Создаем объект оболочки Windows
    Set wshShell = CreateObject("WScript.Shell")

    ' Выполняем команду
    wshShell.Run strCommand, 1, True ' 1 - нормальное окно, True - ждать завершения

    ' Чтение выходных данных
    strOutput = ReadFile("stdout.txt")
    Debug.Print "StdOut: " & strOutput
    strOutput = ReadFile("stderr.txt")
    Debug.Print "StdErr: " & strOutput
End Sub

Function ReadFile(filePath As String) As String
    Dim fileContent As String
    Dim fileNumber As Integer

    fileNumber = FreeFile
    Open filePath For Input As #fileNumber
    fileContent = Input(LOF(fileNumber), fileNumber)
    Close #fileNumber
    ReadFile = fileContent
End Function

Ключевые изменения и пояснения

  1. Корректное экранирование строк: Важно, чтобы вы использовали правильные кавычки и экранирование при формировании строки команды. Это позволяет системе правильно интерпретировать путь к скрипту и параметры.

  2. Использование редиректов: Для захвата стандартного и потокового вывода (stdout и stderr) используются редиректы > stdout.txt 2> stderr.txt. PowerShell не позволяет неэлементарному процессу извлекать данные из вышестоящего процесса, поэтому вывод следует учитывать внутри самого Elevated PowerShell процесса.

  3. Функция для чтения файла: Функция ReadFile открывает текстовый файл и считывает его содержимое. Это позволяет вам захватывать и выводить результаты выполнения вашего PowerShell скрипта в Debug окно для проверки.

  4. Синхронное выполнение: Параметр Wait в Start-Process заставляет VBA ждать завершения выполнения PowerShell скрипта.

Учет UAC

При запуске скрипта с повышенными правами может возникнуть запрос на подтверждение разрешения (UAC). Если вам необходимо избежать этого, можно рассмотреть возможность создания запланированной задачи с преднастроенной командой, которую можно вызвать без дополнительных подтверждений.

Заключение

Выполнение PowerShell скриптов с правами администратора через VBA требует внимательного подхода к формированию команд и обработке их выполнения. С использованием указанных выше методов вы сможете успешно выполнять ваши скрипты, обеспечивая нужные права и контролируя вывод. Не забудьте тестировать ваш код, чтобы убедиться, что все работает корректно и по задуманному сценарию.

Оцените материал
Добавить комментарий

Капча загружается...