Справочник настроек прокси инструкции по программам

PowerShell запрос через прокси: параметры командлетов, WebProxy и планировщик заданий

PowerShell отправляет исходящие запросы командлетами Invoke-WebRequest и Invoke-RestMethod поверх сетевого стека .NET, и адрес посредника попадает в них тремя путями: параметром самого вызова, объектом прокси внутри сеанса либо переменной окружения. Страница разбирает все три пути, показывает передачу логина с паролем объектом учётных данных и переносит готовый файл сценария в задание планировщика Windows.

Я держу на рабочем сервере три сценария выгрузки. Первый снимает позиции каждые 20 минут, второй раз в сутки забирает отчёт с партнёрского API, третий обходит список из 340 адресов и складывает коды ответов в таблицу. Все три ходят наружу через посредник. Из моей консоли они отрабатывали ровно, а после переноса в планировщик начали возвращать отказы на первом же запросе, и разбираться пришлось долго. Ниже собран порядок, который эту разницу убрал.

1. Что понадобится

Подойдёт Windows PowerShell 5.1 из поставки системы либо PowerShell 7. Версию печатает одна строка:

$PSVersionTable.PSVersion

Разница между ветками сидит ровно в тех местах, про которые эта страница. В 5.1 командлеты построены на классе HttpWebRequest: переменные окружения они пропускают и смотрят на системную настройку WinINET. В 7 под капотом HttpClient, который читает HTTP_PROXY и HTTPS_PROXY сам. Держите в голове, какая версия запускает ваш файл: планировщик вызывает powershell.exe для 5.1 и pwsh.exe для 7, и подмена одного другим меняет поведение сети целиком.

Строка подключения нужна в одном из двух видов: IP:PORT при авторизации по привязанному адресу и IP:PORT:LOGIN:PASS при доступе по паре логина и пароля. Примеры ниже написаны на адресе 203.0.113.31 и порте 8000, подставляйте свои значения без изменения структуры строк. Список у меня выдаётся ссылкой сразу в обоих форматах, и я беру нужную форму без ручной разборки; под сценарии я использую приватные серверные адреса для скриптов с двумя привязками в пакете и сменой привязки в кабинете.

Для раздела про расписание понадобятся права на создание заданий и модуль ScheduledTasks, он входит в систему. Отдельно приготовьте служебную учётную запись, если сценарий будет ходить наружу ночью. Включение пакета занимает около 5 минут, поэтому отлаживать вызовы раньше, чем адрес начал отвечать, смысла нет. Бесплатный тест до 2 часов покрывает и проверку в консоли, и первый прогон по расписанию.

2. Параметры Invoke-WebRequest и Invoke-RestMethod для работы через посредник

Оба командлета делят один набор сетевых параметров. Invoke-RestMethod дополнительно разбирает ответ JSON в объект, Invoke-WebRequest отдаёт сырой ответ с заголовками и кодом. Всё, что написано про посредника, работает у обоих одинаково.

ПараметрЧто делаетГде доступенЗамечание
-Proxyадрес посредника целиком, со схемой и портом5.1 и 7принимает тип Uri, строка без http:// отвергается
-ProxyCredentialпара логина и пароля для посредника5.1 и 7ждёт объект PSCredential, обычную строку не примет
-ProxyUseDefaultCredentialsподставляет учётную запись текущего сеанса Windows5.1 и 7рассчитан на корпоративный шлюз с NTLM
-NoProxyотключает посредника для одного вызоватолько 7перебивает и переменные окружения, и настройку сеанса
-TimeoutSecверхняя граница ожидания ответа5.1 и 7значение 0 снимает ограничение сверху
-ConnectionTimeoutSecотдельная граница на установку соединения7.4 и новееотделяет закрытый порт от медленного сайта
-MaximumRetryCountчисло автоматических повторовтолько 7повторяет коды 5xx и 408, обрывы соединения пропускает
-RetryIntervalSecпауза между повторамитолько 7пауза постоянная, роста интервала нет
-UseBasicParsingразбор ответа без движка Internet Explorer5.1в 7 подразумевается всегда
-SkipCertificateCheckпропуск проверки сертификататолько 7для посредника с подменой цепочки

Схема в адресе посредника всегда http://, включая случай, когда сам запрос уходит по HTTPS. Схема описывает канал до посредника, целевой протокол задаётся адресом в параметре -Uri. Строка вида https://203.0.113.31:8000 порту не понравится, и клиент вернёт отказ на согласовании шифрования.

Повторять три одинаковых параметра в каждом вызове скучно, поэтому я собираю их в таблицу и подставляю оператором splatting:

$common = @{
    Proxy      = 'http://203.0.113.31:8000'
    TimeoutSec = 40
    UserAgent  = 'ops-fetch/1.4'
    Headers    = @{ 'Accept-Language' = 'ru-RU,ru;q=0.9' }
}

$data = Invoke-RestMethod -Uri 'https://api.example.net/v1/rows' @common
$page = Invoke-WebRequest -Uri 'https://example.net/catalog' @common -UseBasicParsing
$page.StatusCode

Такой набор правится в одном месте, и адрес посредника перестаёт расползаться по файлу копиями. Когда я перевожу сценарий с прямого канала на посредника, правка занимает одну строку.

Полезная мелочь для отладки: в PowerShell 7 добавьте к вызову ключ -SkipHttpErrorCheck, и командлет вернёт ответ с кодом 403 либо 407 обычным объектом, без исключения. Разбирать содержимое ответа посредника так удобнее.

3. Логин и пароль объектом учётных данных

Параметр -ProxyCredential принимает только объект PSCredential. Строка u7391:Kf3zR8pQ в него не пройдёт, и попытка передать её заканчивается ошибкой приведения типа. Интерактивно объект собирает Get-Credential, но в сценарии по расписанию окно запроса пароля неуместно, поэтому объект собирается кодом:

$user = 'u7391'
$pass = ConvertTo-SecureString 'Kf3zR8pQ' -AsPlainText -Force
$cred = [pscredential]::new($user, $pass)

Invoke-RestMethod -Uri 'https://api.ipify.org' `
                  -Proxy 'http://203.0.113.31:8000' `
                  -ProxyCredential $cred

Пароль открытой строкой в файле меня устраивает ровно до первого ревью. Дальше я перекладываю его в зашифрованный файл. PowerShell шифрует SecureString механизмом DPAPI, и ключ привязан к учётной записи Windows и к машине:

Read-Host -Prompt 'пароль посредника' -AsSecureString |
    ConvertFrom-SecureString |
    Set-Content -Path 'C:\ops\secure\proxy.txt' -Encoding ASCII

$pass = Get-Content 'C:\ops\secure\proxy.txt' | ConvertTo-SecureString
$cred = [pscredential]::new('u7391', $pass)

Запомните привязку ключа, к ней я вернусь в разделе про планировщик. Файл, созданный под моей учётной записью, служебная учётка прочитать не сможет: расшифровка завершится сообщением про недопустимое состояние ключа. Создавать такой файл нужно от имени той учётки, которая будет его читать.

Параметр -ProxyUseDefaultCredentials делает другое. Он отдаёт посреднику текущую учётную запись Windows по протоколу NTLM либо Kerberos, что подходит внутреннему шлюзу предприятия. Для пары логина и пароля от поставщика этот ключ бесполезен, здесь нужен именно объект учётных данных.

Есть путь короче. Привязка внешнего адреса машины в кабинете снимает вопрос о пароле целиком: порт пускает вас по адресу источника, и в файле сценария не остаётся ни одной строки с секретом. Для машины со стабильным внешним адресом я выбираю именно так, привязок в пакете две и меняются они в кабинете свободно. Ночное задание при этом стартует молча, без диалогов и без файла с ключом на диске. Так же устроены прогоны по готовым шаблонам, адреса под них я разбирал на странице про пул под запуск шаблонов ZennoPoster.

4. Класс System.Net.WebProxy, когда нужен точный контроль

Параметр -Proxy задаёт посредника на один вызов. Когда в файле сорок вызовов, а часть адресов должна идти напрямую, удобнее собрать объект прокси и повесить его на весь сеанс:

$proxy = [System.Net.WebProxy]::new('http://203.0.113.31:8000', $true)
$proxy.BypassList = @(
    'localhost',
    '127\.0\.0\.1',
    '.*\.corp\.local$',
    '^10\.'
)
$proxy.Credentials = $cred

[System.Net.WebRequest]::DefaultWebProxy = $proxy

Первая тонкость сидит в списке исключений. Записи в BypassList разбираются как регулярные выражения, звёздочка из системного поля Windows здесь работать не будет. Точку нужно экранировать, иначе она совпадёт с любым символом, и запись 10.0.0.5 неожиданно накроет 10a0b0c5. Я пишу список сразу в форме регулярных выражений и проверяю каждую запись отдельно.

Вторая тонкость в аргументе конструктора. Логическая истина вторым параметром включает обход посредника для локальных имён без точки, тот же смысл, что у флажка в графическом окне системы.

Проверить попадание конкретного адреса под исключения можно на месте:

$proxy.IsBypassed([uri]'https://api.example.net/')   # False
$proxy.IsBypassed([uri]'http://srv1.corp.local/')    # True
$proxy.GetProxy([uri]'https://api.example.net/')

Третья тонкость касается версий. Свойство DefaultWebProxy класса WebRequest управляет старым стеком, на котором работают Windows PowerShell 5.1, WebClient и HttpWebRequest. В PowerShell 7 командлеты собирают свой обработчик HttpClient, и умолчание для него живёт в другом месте:

[System.Net.Http.HttpClient]::DefaultProxy = $proxy

Обе строки в сценарии не мешают друг другу, и я ставлю их рядом в шапке файла. Тогда один и тот же текст отрабатывает и под powershell.exe, и под pwsh.exe, что для машины с двумя ветками избавляет меня от отдельных версий сценария.

Учётные данные, положенные в свойство Credentials объекта, распространяются на все вызовы сеанса. Отдельный вызов при этом можно увести мимо: явный -Proxy в командлете перебивает умолчание, а ключ -NoProxy в семёрке отправляет запрос напрямую.

5. Переменные окружения и их видимость внутри сеанса

PowerShell 7 читает HTTP_PROXY, HTTPS_PROXY, ALL_PROXY и NO_PROXY при первом обращении к умолчанию HttpClient. Значение берётся один раз за процесс, поэтому правка переменной посреди сеанса на уже выполненные вызовы никак не влияет. Windows PowerShell 5.1 эти имена пропускает полностью и смотрит на системную настройку.

Задать переменную можно четырьмя способами, и у каждого своя область видимости. Путаница между ними даёт самый частый сюжет: в консоли всё работает, из планировщика запросы уходят напрямую.

Способ задатьОбластьВидит текущая консольВидит задание планировщикаПереживает перезагрузку
присваивание через префикс env:текущий процессданетнет
SetEnvironmentVariable с областью Processтекущий процессданетнет
SetEnvironmentVariable с областью Userпрофиль учётной записинет, до перезапуска консолида, если задание идёт от той же учёткида
SetEnvironmentVariable с областью Machineвся машинанет, до перезапуска консолида, включая сеанс SYSTEMда
строка в файле профилясеансы этой учёткида, после перезапусканет при ключе -NoProfileда
присваивание в самом файле сценарияпроцесс заданиядадада

Постоянные значения ставятся так:

[Environment]::SetEnvironmentVariable('HTTPS_PROXY','http://203.0.113.31:8000','Machine')
[Environment]::SetEnvironmentVariable('HTTP_PROXY','http://203.0.113.31:8000','Machine')
[Environment]::SetEnvironmentVariable('NO_PROXY','localhost,127.0.0.1,.corp.local','Machine')

Открытая консоль новых значений не увидит. Область Machine пишется в реестр, а окружение процесса копируется в момент его старта, поэтому проверять правку нужно в заново открытом окне. Я на этом потерял вечер, пока не завёл привычку закрывать консоль после каждой правки области Machine.

Разделитель в списке исключений здесь запятая, без пробелов. Ведущая точка работает маской поддоменов, звёздочка в реализации .NET понимается не везде, поэтому я пишу и короткое имя, и форму с точкой подряд. Строка no_proxy в нижнем регистре читается тоже, и на машинах со смешанным набором утилит я задаю оба написания.

Проверить, что видит именно текущий процесс, помогает пара строк:

Get-ChildItem env: | Where-Object Name -match 'proxy'
[System.Net.Http.HttpClient]::DefaultProxy.GetProxy([uri]'https://example.net/')

Вторая строка печатает адрес, на который уйдёт запрос. Пустой ответ означает прямой канал. Для машин, где переменные раздаются групповой политикой на весь парк, я держу отдельный порт под консольные утилиты и беру HTTP-прокси для запросов из консоли: одна короткая строка ложится и в переменную окружения, и в параметр командлета без переделки.

6. Повторы и таймауты в длинном прогоне

Значение -TimeoutSec по умолчанию равно нулю, и ожидание сверху не ограничено. Для интерактивного вызова это терпимо. Для задания по расписанию это прямой путь к процессу, который висит сутки и мешает следующему запуску. Я ставлю таймаут всегда.

В PowerShell 7.4 появилась пара более точных параметров. -ConnectionTimeoutSec ограничивает установку соединения с посредником, -OperationTimeoutSec ограничивает чтение ответа. Разведя их, вы отличаете закрытый порт от медленного целевого сайта прямо по тексту ошибки, и это экономит половину времени разбора.

Встроенные повторы в семёрке срабатывают на кодах 5xx и 408. Обрыв соединения, отказ по времени и код 407 они пропускают, поэтому свой цикл всё равно нужен:

function Invoke-ViaProxy {
    param(
        [hashtable] $Call,
        [int] $Tries = 4
    )
    for ($n = 1; $n -le $Tries; $n++) {
        try {
            return Invoke-RestMethod @Call
        }
        catch {
            $pause = [math]::Min(45, 3 * $n * $n)
            Write-Warning ("попытка {0} из {1}: {2}. Пауза {3} c" -f `
                $n, $Tries, $_.Exception.Message, $pause)
            Start-Sleep -Seconds $pause
        }
    }
    throw ("адрес {0} молчит после {1} попыток" -f $Call.Uri, $Tries)
}

Invoke-ViaProxy -Call @{
    Uri        = 'https://api.example.net/v1/rows'
    Proxy      = 'http://203.0.113.31:8000'
    TimeoutSec = 30
}

Пауза растёт квадратично и упирается в 45 секунд. Постоянный интервал в 5 секунд у меня вёл к тому, что все четыре попытки укладывались в одну минуту и целевой сайт отдавал 429 на каждой.

Отдельная строка нужна на Windows PowerShell 5.1, где число одновременных соединений к одному хосту по умолчанию равно двум. Параллельный обход списка из 340 адресов упирается в это ограничение раньше, чем в порт посредника:

[System.Net.ServicePointManager]::DefaultConnectionLimit = 64
[System.Net.ServicePointManager]::Expect100Continue = $false
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

Третья строка спасает от отказов на согласовании шифрования: сборки 5.1 по умолчанию предлагают устаревший набор протоколов, и часть сайтов такое соединение закрывает молча. Ночные прогоны с сотнями страниц я гоняю на пакете, где обмен не считают, и потолок упирается только в число потоков. Ту же арифметику по потокам на массовом сборе я расписывал на странице про прокси для многопоточного сбора A-Parser.

Про потоки помните отдельно. Обычный пакет даёт 1000, корпоративный до 3000, пакеты по потокам не складываются. При двух привязанных адресах лимит делится между ними пополам, и параллельный обход на 700 потоков со второй активной привязкой начнёт получать отказы на установке соединения. Считайте параллелизм по фактической половине.

7. Запуск скрипта в планировщике заданий Windows

Задание регистрируется четырьмя объектами: действие, триггер, набор настроек и сама регистрация под учётной записью.

$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument @'
-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\ops\fetch.ps1"
'@

$trigger = New-ScheduledTaskTrigger -Daily -At 03:20

$set = New-ScheduledTaskSettingsSet `
    -MultipleInstances IgnoreNew `
    -ExecutionTimeLimit (New-TimeSpan -Hours 2) `
    -StartWhenAvailable `
    -RestartCount 2 -RestartInterval (New-TimeSpan -Minutes 10)

Register-ScheduledTask -TaskName 'ops-fetch' -Action $action -Trigger $trigger `
    -Settings $set -User 'CORP\svc-ops' -Password 'подставьте свой' -RunLevel Limited

Кавычки вокруг пути обязательны, если в нём есть пробел. Ключ -File передаёт файл целиком, -Command передаёт строку кода, и смешение этих двух форм даёт код последнего запуска 0x1 без единой подсказки в журнале.

Теперь главное про перенос. Задание стартует свежим процессом от указанной учётной записи, и почти ничего из вашей рабочей обстановки в него не попадает.

Что настроено в вашей консолиВидит ли заданиеПочему
системная страница «Параметры», ветка HKCUнет, при запуске от другой учёткинастройка лежит в профиле конкретного пользователя
netsh winhttp set proxy, ветка HKLMдамашинное хранилище WinHTTP общее для всех сеансов
переменная окружения области Userтолько при той же учётной записизначение читается из профиля владельца
переменная окружения области Machineдазначение лежит в реестре машины
строки в файле профиля PowerShellнет при ключе -NoProfileпрофиль сознательно пропускается
файл с паролем, зашифрованный DPAPIнетключ привязан к учётной записи создателя
сопоставленные сетевые дискинетбуквы дисков создаются при интерактивном входе
текущий рабочий каталогнетпроцесс стартует в C:\Windows\System32

Отсюда простое правило, к которому я пришёл: настройка посредника живёт внутри файла сценария. Никаких переменных из профиля, никакой опоры на страницу «Параметры». Шапка файла задаёт адрес, список исключений и учётные данные явно, и тогда результат прогона в консоли совпадает с результатом по расписанию.

Второе правило про относительные пути. Строка .\out\rows.csv в задании превратится в C:\Windows\System32\out\rows.csv, куда служебная учётка писать не имеет права. Я задаю рабочий каталог первой строкой файла через Set-Location либо собираю пути от корня.

Флажок «Выполнять вне зависимости от регистрации пользователя» переводит задание в фоновый режим без загруженного профиля. Обращения к ветке HKCU в таком сеансе ведут себя непредсказуемо, и ещё одна причина держать настройку в самом файле именно здесь.

8. Служебная учётная запись, привязка адреса и журнал отказов

Служебной учётной записи нужно право «Вход в качестве пакетного задания», иначе планировщик откажет ещё до старта процесса. Право выдаётся в локальной политике безопасности, раздел «Назначение прав пользователя». Без него в журнале появится код 0x80070569, а в тексте сообщения будет сказано про несостоявшийся вход.

Дальше про адрес. Посредник видит внешний адрес машины, и смена учётной записи внутри Windows на него никак не влияет. Привязывать в кабинете нужно именно внешний адрес сервера, каким его видит внешняя сторона:

Invoke-RestMethod -Uri 'https://api.ipify.org' -NoProxy
Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Dhcp, Manual |
    Select-Object IPAddress, InterfaceAlias

Первая строка печатает адрес, который увидит порт посредника. Вторая перечисляет локальные интерфейсы, они в кабинет не вносятся. У сервера за NAT эти значения различаются, и путаница между ними даёт ровный поток отказов 403 на каждом ночном прогоне.

Накопление отказов по расписанию выглядит коварно именно потому, что днём никто на него не смотрит. Задание раз в 20 минут даёт 72 прогона за ночь. Если каждый оставляет после себя незакрытое соединение или упирается в отказ авторизации, к утру в журнале лежит длинный хвост, а таблица с результатами пустая. Я закрываю это тремя приёмами.

Первый: настройка -MultipleInstances IgnoreNew запрещает наложение экземпляров. Второй: -ExecutionTimeLimit снимает зависший прогон принудительно. Третий: каждый прогон пишет строку в общий файл журнала, и утренний взгляд на этот файл занимает 10 секунд.

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$code = 0
$seen = ''
try {
    $r = Invoke-WebRequest -Uri 'https://api.ipify.org' @common -UseBasicParsing
    $code = $r.StatusCode
    $seen = $r.Content.Trim()
}
catch {
    $code = -1
    $seen = $_.Exception.Message
}
[pscustomobject]@{
    Метка   = Get-Date -Format 'MM-dd HH:mm:ss'
    Код     = $code
    Адрес   = $seen
    Секунды = [math]::Round($sw.Elapsed.TotalSeconds, 2)
} | Export-Csv -Path 'C:\ops\log\fetch.csv' -Append -NoTypeInformation -Encoding UTF8

Такая таблица показывает две вещи сразу: сколько прогонов прошло и какой внешний адрес видел каждый. Значения в колонке адреса будут разными от прогона к прогону, потому что ротация внутри пула идёт автоматически и конкретный узел на выходе меняется сам. Пул примерно на 12 000 адресов делает эту смену незаметной для сценария, пока строка подключения одна.

Полную картину по самому заданию отдаёт планировщик:

Get-ScheduledTaskInfo -TaskName 'ops-fetch' |
    Select-Object LastRunTime, LastTaskResult, NumberOfMissedRuns, NextRunTime

Get-WinEvent -LogName 'Microsoft-Windows-TaskScheduler/Operational' -MaxEvents 40 |
    Where-Object Message -match 'ops-fetch' |
    Select-Object TimeCreated, Id, LevelDisplayName, Message |
    Format-List

Поле NumberOfMissedRuns показывает пропуски, поле LastTaskResult печатает код завершения. Коды разобраны в таблице ошибок ниже. Для серверов, где такие задания крутятся месяцами без правок, я беру серверный доступ к пулу под задания планировщика: строка подключения не меняется, привязка правится в кабинете, и файл сценария остаётся нетронутым.

9. Проверка после настройки

Первым делом смотрим, что порт вообще отвечает с этой машины. Отказ на этом шаге снимает половину дальнейших вопросов:

Test-NetConnection -ComputerName 203.0.113.31 -Port 8000 -InformationLevel Detailed

Дальше сверяем два адреса подряд, прямой и через посредника:

$direct = Invoke-RestMethod -Uri 'https://api.ipify.org' -NoProxy
$viaP   = Invoke-RestMethod -Uri 'https://api.ipify.org' -Proxy 'http://203.0.113.31:8000'
"прямо: $direct ; через посредника: $viaP"

Значения обязаны различаться. Совпадение говорит, что запрос ушёл мимо посредника, и причину ищем в списке исключений либо в версии PowerShell. Повторите вызов несколько раз: второе значение будет меняться, ротация внутри пула работает без вашего участия.

Третья проверка касается заголовков ответа. Она показывает, дошёл ли запрос до цели через туннель:

$r = Invoke-WebRequest -Uri 'https://example.net/' @common -UseBasicParsing
$r.StatusCode
$r.BaseResponse.RequestMessage.RequestUri
$r.Headers['Via']

Самая полезная проверка идёт последней, и делать её нужно от имени служебной учётной записи. Заведите разовое задание, которое печатает окружение и внешний адрес в файл, запустите его руками и прочитайте результат:

@'
"учётка: $(whoami)" | Set-Content C:\ops\probe.txt
Get-ChildItem env: | Where-Object Name -match 'proxy' |
    ForEach-Object { "$($_.Name)=$($_.Value)" } | Add-Content C:\ops\probe.txt
(Invoke-RestMethod -Uri 'https://api.ipify.org' -Proxy 'http://203.0.113.31:8000') |
    Add-Content C:\ops\probe.txt
'@ | Set-Content C:\ops\probe.ps1 -Encoding UTF8

Start-ScheduledTask -TaskName 'ops-probe'
Start-Sleep -Seconds 15
Get-Content C:\ops\probe.txt

Файл покажет три строки: под какой учёткой шёл прогон, какие переменные окружения видел процесс и какой внешний адрес вернул порт. Я прогоняю эту пробу после каждой правки задания, привычка стоит 15 секунд и закрывает почти весь класс расхождений между консолью и расписанием. Для стендов, которые живут дольше пары суток, я перевожу пробу на месячный срок доступа под стенд, чтобы срок доступа перекрывал весь цикл наблюдений.

10. Разбор ошибок

СообщениеПричинаЧто делать
Cannot convert value "203.0.113.31:8000" to type "System.Uri"в -Proxy передана строка без схемынаписать http:// перед адресом
Unable to connect to the remote serverпорт посредника закрыт исходящим фильтром либо в адресе опечаткапрогнать Test-NetConnection до порта
The remote server returned an error: (407) Proxy Authentication Required.пароль не доехал до вызовадобавить -ProxyCredential либо перейти на привязку адреса
The proxy tunnel request to proxy 'http://203.0.113.31:8000' failed with status code '407'то же самое в PowerShell 7, где туннель строит HttpClientпересобрать объект учётных данных, проверить логин
The remote server returned an error: (403) Forbidden.внешний адрес машины в кабинете отсутствуетвнести внешний адрес сервера и повторить прогон
Key not valid for use in specified state.файл с паролем зашифрован под другой учётной записьюпересоздать файл от имени служебной учётки
The underlying connection was closed: An unexpected error occurred on a send.в 5.1 согласование шифрования упирается в устаревший протоколвыставить SecurityProtocol в Tls12 в шапке файла
The operation has timed out.ответ не пришёл за отведённое времяподнять -TimeoutSec, развести подключение и чтение в 7.4
Response status code does not indicate success: 429 (Too Many Requests)темп запросов выше того, что принимает целевой сайтувеличить паузу в цикле повторов, снизить параллелизм
The SSL connection could not be established, see inner exception.цепочка сертификатов неполная либо посредник подменяет еёдобавить корневой сертификат в хранилище машины
Cannot bind argument to parameter 'ProxyCredential' because it is nullфайл с паролем пуст либо путь к нему разобран от System32задать полный путь, проверить содержимое файла
Код последнего запуска 0x1, из консоли всё работаетв задании стоит -NoProfile, настройка посредника лежала в профилеперенести адрес и учётные данные в сам файл сценария
Код последнего запуска 0x41301 держится часамизапрос завис без верхней границы ожиданиязадать -TimeoutSec и ExecutionTimeLimit
Код последнего запуска 0x41303задание ни разу не запускалосьвключить триггер, проверить условие питания в настройках
Код 0x80070569 в журнале планировщикау служебной учётки нет права входа как пакетное заданиевыдать право «Вход в качестве пакетного задания»
Код 0x80070005 при записи файласлужебной учётке закрыт каталог выгрузкивыдать права на запись в каталог журнала и выгрузок

Две строки заслуживают комментария. Сообщение про недопустимое состояние ключа я получал трижды, и каждый раз причина была одна: файл с паролем я готовил под своей учётной записью, а задание шло от служебной. Порядок правильный такой: войти под служебной учёткой либо запустить консоль от её имени, там создать файл, там же проверить расшифровку.

Вторая строка про код 0x1 при рабочем ручном запуске. Отладка тут короткая: добавьте в начало файла запись окружения в текстовый файл, запустите задание и сравните вывод с тем, что печатает ваша консоль. Расхождение будет видно в первой же строке, и дальше остаётся перенести недостающие значения внутрь сценария.

Смежные сценарии серверной стороны разобраны на соседних страницах справочника: контейнеры и общая сборка описаны в материале про прокси в Docker и docker-compose, обмен с внешними сервисами из учётной системы в статье про настройку посредника в 1С, выгрузка результатов наружу в инструкции про Google Таблицы и Apps Script. Если тот же сервер должен отдавать посредника всем программам сразу, начните с раздела про системные сетевые параметры Windows 11 и команду netsh winhttp.