## Командлет Invoke-WebRequest в PowerShell: использование с прокси-сервером

Командлет Invoke-WebRequest в PowerShell — удобный инструмент для отправки HTTP-запросов на веб-сайты. Если вы уже пользуетесь прокси-сервисами Bright Data, вы можете использовать этот командлет с прокси-сервером, указав параметр -Proxy, а затем сведения о прокси-сервере.
## Описание командлета Invoke-WebRequest
Invoke-WebRequest — это командлет PowerShell для отправки запросов HTTP, HTTPS и FTP веб-серверам и веб-службам. По умолчанию он автоматически анализирует ответ, полученный от сервера, и возвращает подборки форм, ссылок, изображений и других важных элементов HTML.
Обычно он используется для доступа к интерфейсам REST API, загрузки файлов из Интернета или взаимодействия с веб-службами.
## Базовый синтаксис запроса Invoke-WebRequest
Приведен базовый синтаксис запроса Invoke-WebRequest:
Invoke-WebRequest [-Uri] <Uri> [-Method <WebRequestMethod>] [-Headers <IDictionary>] [-Body <Object>]
Ключевые параметры:
- -Uri: URI веб-ресурса, на который отправлен запрос.
- -Method: HTTP-метод для использования в запросе (например, GET, POST, PUT, DELETE).
- -Headers: дополнительные HTTP-заголовки, которые необходимо включить в запрос.
- -Body: тело запроса для отправки на сервер.
## Дополнительные возможности командлета
Таким образом, самый простой синтаксис для выполнения запроса GET к заданному URI:
Invoke-WebRequest <Uri>
Командлет был добавлен в PowerShell 3.0 в 2012 году.
## Изменение атрибутов в Active Directory через сайт
Наш третий сервис будет получать и изменять атрибут пользователя в Active Directory. Изменяемым атрибутом будет номер телефона. Маршруты:
1. /change-attribute
2. /set-phone
Структура приложения:
```powershell
Start-PodeServer {
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
Set-PodeViewEngine -Type Pode
Add-PodeRoute -Method get -Path /change-attribute -ScriptBlock {
Write-PodeViewResponse -Path change-attribute
}
Add-PodeRoute -Method post -Path /set-phone -ScriptBlock {
Move-PodeResponseUrl -Url /change-attribute
}
}
Шаблон делаем максимально простым.
Set-ADUser $login -Replace @{telephoneNumber = $phone_number}
$message = Телефон пользователя $login успешно изменен
}
else {
$message = Необходимо ввести логин и номер телефона
}
Write-PodeViewResponse -Path change-attribute -Data @{ login = $login; phone = $phone_number; message = $message }
}
Start-PodeServer
}
Get-ADUser -Identity $login | Set-ADUser -replace @{telephonenumber=$phone_number}<title>Changing attributes in Active Directory through the website</title><!–Поле для отображения сообщений–><p>Привет, как у вас дела?</p><!–Форма для получения номера телефона. Используя метод GET.–><form action="/change-attribute" method="get">
<div>
<label for="login">Login: </label>
<!— Field for login —>
<!— In value, we write the value from Powershell —>
<input type="text" name="login" value="$($data.login)" placeholder="Enter" login="">
<!— Form submission button to get the phone number —>
<input type="submit" value="Get">
</div>
</form><!–Форма для изменения номера телефона. Используя метод POST.–>
## Изменение номера телефона
<form action=/set-phone method=post>
<div>
<label for=phone>Телефон:</label>
<input type=number name=phone value=$($data.phone) placeholder=Введите телефон />
<input name=login type=hidden value=$($data.login)>
<input type=submit value=Изменить />
</div>
</form>
## Реализация сервисов
Закончив реализацию трёх сервисов в минимально рабочей версии, есть возможность запуска ваших Powershell сервисов с Pode в Docker, на IIS или как службу. Рекомендую к прочтению раздел документации по этому вопросу.
## Прокси HTTPS и SOCKS в PowerShell
Для использования прокси-серверов HTTPS или SOCKS в PowerShell, необходимо обновить его до версии 7.x и выше. В противном случае, Invoke-WebRequest завершится ошибкой.
### Пример прокси-запроса Invoke-WebRequest SOCKS:
Invoke-WebRequest -Proxy socks5://94.14.109.54:3567 http://httpbin.org/ip
Ожидаемый результат:
StatusCode : 200
StatusDescription : OK
Content : { origin: 94.14.109.54 }
RawContent : HTTP/1.1 200 OK
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: 31
Content-Type: application/json
Date: Thu, 01 Feb 2024 12:47:56 GMT
## Установка Invoke-WebRequest
Чтобы использовать Invoke-WebRequest, вам понадобится PowerShell. Итак, давайте узнаем, как установить PowerShell и получить доступ к командлету Invoke-WebRequest!
Windows
Во-первых, вы должны понять, что Windows PowerShell и PowerShell — это две разных программы. Windows PowerShell — это версия PowerShell, поставляемая вместе с Windows, и ее последняя версия — 5.1. Windows PowerShell содержит командлет Invoke-WebRequest. Это означает, что если вы используете современную версию Windows, вы готовы сразу же приступить к делу! При наличии более старых версий следуйте указаниям в официальном руководстве по установке PowerShell.
В то же время некоторые функции Invoke-WebRequest доступны только в версии PowerShell 7.x и более поздних версиях. Дополнительные сведения о том, как установить PowerShell 7.x, приводятся в официальном руководстве по миграции с Windows PowerShell 5.1 на PowerShell 7. Обратите внимание, что PowerShell 7.x устанавливается в новый каталог и работает параллельно с Windows PowerShell 5.1.
Вы можете узнать, какая версия PowerShell установлена на вашем компьютере под управлением ОС Windows, с помощью следующей команды:
$PSVersionTable
В PowerShell 7.x нужно ввести что-то вроде этого:
PSVersion 7.4.1
PSEdition Core
GitCommitId 7.4.1
OS Microsoft Windows 10.0.22631
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
macOS и Linux
Hello World
Для начала работы c Pode нам необходимо произвести его установку:
Install-Module -Name Pode
Прикладываю ссылку на PowerShell Gallery. Далее создаём в рабочем каталоге файл server.ps1 и сохраняем простейшую конфигурацию, которая поднимет веб на порту 8080 и будет отдавать JSON по пути /ping
.
# Запуск работы сервера
Start-PodeServer {
# Cоздаём конечную точку для принятия входящих запросов
# https://badgerati.github.io/Pode/Functions/Core/Add-PodeEndpoint/
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
# Добавляем маршрут для определённого метода
# https://badgerati.github.io/Pode/Functions/Routes/Add-PodeRoute/
Add-PodeRoute -Method Get -Path '/ping' -ScriptBlock {
# Записываем JSON в ответ
Write-PodeJsonResponse -Value @{ 'value' = 'pong' }
}
}
В результате, при обращении на http://localhost:8080/ping
получим в ответ значение pong
:
Ответ Powershell фреймворка Pode в JSON формате
Мы запустили веб на Powershell, ура!
Приступим к реализации обещанных трёх сервисов. Для учебной цели я придумал два сервиса с интерфейсом и с одним будем работать из консоли. Ниже ссылки для быстрого перехода:
Добьёмся минимально рабочей версии каждого сервиса и на одном из примеров познакомимся с некоторыми важными фичами фреймворка: аутентификацией, логированием, лимитами на обращения и middleware.
Убедительно прошу не упускать из внимания политики безопасности ваших компаний, а так же здравый смысл и не допускать, чтобы инструменты для администратора становились публичными для неограниченного круга лиц внутри компании. Делайте сервисы безопасными, пожалуйста.
Какой прокси-сервер PowerShell следует использовать?
Ответ на этот вопрос меняется в зависимости от того, чего вы хотите достичь с помощью запроса Invoke-WebRequest. Чтобы найти подходящий прокси-сервер PowerShell для ваших нужд, ознакомьтесь с различными типами доступных прокси-серверов:
Прокси-серверы центров обработки данных: они быстрые и дешевые, но сайты могут легко обнаружить и заблокировать их из-за их идентифицируемых диапазонов IP-адресов.
Резидентные прокси-серверы: они предлагают ротацию подлинных IP-адресов с реальных устройств в определенных местах. Это означает, что они могут гарантировать высокий уровень анонимности. Резидентные прокси-серверы идеально подходят для доступа к сайтам, использующим географические ограничения, или для обхода мер по борьбе с ботами.
Прокси-серверы интернет-провайдеров (ISP): они безопасны, быстры и очень надежны, поскольку предоставляют статические IP-адреса устройств, зарегистрированных у интернет-провайдеров. Прокси-серверы ISP также называют резидентными статическими прокси-серверами, и они являются идеальным решением для мониторинга оптимизации поисковых систем (SEO) и исследования рынка.
Мобильные прокси-серверы: они предоставляют IP-адреса с реальных мобильных устройств для обеспечения высокого уровня анонимности. Они полезны для доступа к приложениям, сайтам или контенту, специально разработанному для мобильных устройств.
API для работы с директорией
Наш второй сервис будет выполнять определённые действия в директории при обращении по API. Попробуем запускать удаление файлов *.log
и получать информацию о всех файлах в этой директории в формате JSON. Здесь же попробуем реализовать аутентификацию по API-ключу, добавим логирование и ограничение по количеству обращений.
Какие маршруты сделаем?
Запуск удаления файлов,
Remove-Item
Получение информации о файлах,
Get-ChildItem
Пишем каркас будущего сервиса:
Start-PodeServer {
# Запускаем сервер на http://localhost:8080
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
# TODO: Аутентификация
# TODO: Логирование
# TODO: Ограничение по количеству обращений
# Запуск удаления файлов
Add-PodeRoute -Method Delete -Path '/api/remove-logs' -ScriptBlock {
# TODO: Получить файлы в директории для удаления, удалить
# и вернуть список удалённых файлов
pass
}
# Получение информации о файлах
Add-PodeRoute -Method Get -Path '/api/get-childitem' -ScriptBlock {
# TODO: Получить список файлов в директории и вернуть список в JSON
}
}
HTML шаблонов тут не будет, здесь нет никаких взаимодействий с пользователем в браузере. Приступаем к написанию серверной части.
Аутентификация
В этом сервисе реализуем аутентификацию по API-ключу (документация). При обращении к маршруту пользователь должен будет передать ключ в заголовке X-API-KEY. Посмотрим на пример реализации из документации:
Start-PodeServer {
New-PodeAuthScheme -ApiKey | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($key)
# check if the key is valid, and get user
return @{ User = $user }
}
}
Ага, то есть нам необходимо придумать проверку ключа. Попробуем реализовать что-нибудь по-быстрому:
...
# Аутентификация
New-PodeAuthScheme -ApiKey | Add-PodeAuth -Name 'ApiKeyAuthenticate' -Sessionless -ScriptBlock {
param($key)
# В переменной $key будет API-ключ с которым пришёл пользователь
# Реализуем проверку API ключей
# Для примера будем проверять ключи обращаясь к csv файлу
$file_with_keys = "$PSScriptRoot\api_keys.csv"
# Ищем совпадение входящего ключа с ключом в файле
$user = Import-Csv $file_with_keys -Delimiter ";" | where key -eq "$key"
# Если нашли, записываем пользователя в кастомный лог возвращаем пользователя
if ($user) {
Write-CustomAccessLog
return @{ User = $user.user }
} else {
# Если не нашли, вернём сообщение
return @{ Message = "Access denied!" }
}
}
...
Рядом с файлом сервера создадим файл api_keys.csv
с ключами, указанный нами в коде и сгенерируем рандомные значения и имена пользователей:
key;user
rU6LCqwcYcvPJnYNuK2yxgMwMALU2f7qpp5DrTcvxWf3aKFZmb;john.doe
Y2wAkaUhY6uQT9Z5DrbxfGZka3kfdCFvPx5TE1os64YYUwvvvR;oleg
RBX8LuwM9tv53jaRrdN3DbinZicjASRcR9NXuUBYXGTcB7SiTx;habr
Для тестирования нам хватит, поэтому, с аутентификацией закончим. Достаточно просто можно было бы добавить сроки действия ключей и т.п., но это уже не столько обзор возможностей фреймворка.
Я писал выше, что теперь, при отправке запросов к серверу, необходимо будет отправлять ключ в заголовке X-API-KEY
. Это так, но важно ещё указать имя только что созданной аутентификации (Add-PodeAuth
) в маршрутах в ключ -Authentication
. В коде это будет выглядеть так:
...
Add-PodeRoute -Method Get -Path '/api/get-childitem' -Authentication 'ApiKeyAuthenticate' -ScriptBlock {
...
Логирование
По умолчанию доступны request и error логи (документация). Включим оба и создадим ещё свой собственный, access лог.
Логи можно писать в файл, Event Viewer, прямо в консоль, в S3 Bucket или можно самостоятельно реализовать запись, например, в LogStash или Splunk.
Мы будем писать логи в файлы (документация). Для этого в New-PodeLoggingMethod
укажем ключ -File
, пропишем путь сохранения в папку logs в корне нашего сервиса, а так же сразу укажем сколько хранить логи и в файлах какого размера. Для создания собственного access лога воспользуемся Add-PodeLogger
.
...
# Логирование
# Включаем requests лог
New-PodeLoggingMethod -File -Name 'requests' -Path "$PSScriptRoot\logs" -MaxDays 30 -MaxSize 10MB | Enable-PodeRequestLogging
# Включаем error лог
New-PodeLoggingMethod -File -Name 'error' -Path "$PSScriptRoot\logs" -MaxDays 30 -MaxSize 10MB | Enable-PodeErrorLogging
# Создаём собственный access лог
New-PodeLoggingMethod -File -Name 'access' -Path "$PSScriptRoot\logs" -MaxDays 30 -MaxSize 10MB | Add-PodeLogger -Name 'access' -ScriptBlock {
param($item)
# Формат вывода параметров в файл
return "$($item.DateTime), $($item.user)"
# Для вывода в консоль пишем:
# $item | out-default
}
# Пишем функцию для записи в собственный лога
function Write-CustomAccessLog($user, $message)
{
Write-PodeLog -Name 'access' -InputObject @{
DateTime = $(Get-Date -Format "dd-MM-yyyy HH:mm:ss");
user = $user.user;
}
}
...
Ограничение по количеству обращений
При создании сервисов с Pode обязательно стоит подробнее посмотреть на работу Middleware (документация). Под middleware подразумевается то же самое, что и во многих других фреймворках, некий код или программное обеспечение, которое можем обработать запросы и ответы. Можно написать собственные middleware (Add-PodeMiddleware
), а можно использовать уже встроенные (access rules, body parsing, CSRF, rate limiting, security headers, sessions).
В этом сервисе реализуем ограничение по количеству обращений с одного IP через rate limiting. Зачем это надо? Во-первых, это защитит от быстрого перебора ключей. Во-вторых, даже если пользователи наши коллеги, никто не может гарантировать, что что-то не сломается и не начнёт спамить сервис.
Для настройки органичений воспользуемся Add-PodeLimitRule
(документация). Ограничим, что с одного IP будет приниматься не более 6 обращений за минуту:
...
# Ограничение по количеству обращений
Add-PodeLimitRule -Type IP -Values all -Limit 6 -Seconds 60
...
Перейдём к написанию основных функций нашего API. Для удаления рекомендуется использовать метод DELETE
. Получать информацию будем методом GET
.
Так как мы хотим использовать аутентификацию, добавляем ключ -Authentication
к каждому из маршрутов и указываем имя нашего метода, заданное в Add-PodeAuth
ранее.
По логике всё просто, будем делать Get-ChildItem
и Remove-Item
. В начало ещё добавим рабочую директорию $work_directory
, с которой будет работать наше API.
Start-PodeServer {
# Рабочая директория для действий API
$work_directory = "C:\TestFolder"
...
# Запуск удаления файлов
Add-PodeRoute -Method Delete -Path '/api/remove-logs' -Authentication 'ApiKeyAuthenticate' -ScriptBlock {
# Получаем список файлов для удаления
$files_for_delete = Get-ChildItem $using:work_directory -Recurse -Include "*.log"
# Удаляем файлы
$files_for_delete | Remove-Item -Force
# Возвращаем список удалённых файлов
Write-PodeJsonResponse -Value $($files_for_delete | select FullName | ConvertTo-Json)
}
# Получение информации о файлах
Add-PodeRoute -Method Get -Path '/api/get-childitem' -Authentication 'ApiKeyAuthenticate' -ScriptBlock {
# Получаем список файлов в директории
$result = Get-ChildItem $using:work_directory -Recurse -File | select FullName | ConvertTo-Json
# Возвращаем список в JSON
Write-PodeJsonResponse -Value $result
}
...
Ура, собираем код воедино, запускаем сервис и тестируем API:
Тестирование API для работы с директорией в Postman
$api_key = 'Y2wAkaUhY6uQT9Z5DrbxfGZka3kfdCFvPx5TE1os64YYUwvvvR'
$url = 'http://localhost:8080/api/get-childitem'
Invoke-WebRequest -H @{'X-API-KEY' = $api_key} -Method Get $url -SkipCertificateCheck
Тестирование API для работы с директорией через Invoke-WebRequest
Так же посмотрим на логи. Видим, что рядом с файлов server.ps1 создалась папка logs и в ней дефолтный request лог и кастомный, созданный нами, access лог:
Логи обращений к сервису
На этом мы закончим работу над API и попробуем реализовать ещё один сервис с пользовательским интерфейсом, где попробуем отправлять данные на сервер.
Ссылка на это демо в GitHub.
Итоговый код server.ps1
Start-PodeServer {
# Рабочая директория для действий API
$work_directory = "C:\TestFolder"
# Запускаем сервер на http://localhost:8080
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
# Аутентификация
New-PodeAuthScheme -ApiKey | Add-PodeAuth -Name 'ApiKeyAuthenticate' -Sessionless -ScriptBlock {
param($key)
# В переменной $key будет API-ключ с которым пришёл пользователь
# Реализуем проверку API ключей
# Для примера будем проверять ключи обращаясь к csv файлу
$file_with_keys = "$PSScriptRoot\api_keys.csv"
# Ищем совпадение входящего ключа с ключом в файле
$user = Import-Csv $file_with_keys -Delimiter ";" | where key -eq "$key"
# Если нашли, записываем пользователя в кастомный лог возвращаем пользователя
if ($user) {
Write-CustomAccessLog
return @{ User = $user.user }
} else {
# Если не нашли, вернём сообщение
return @{ Message = "Access denied!" }
}
}
# Логирование
# Включаем requests лог
New-PodeLoggingMethod -File -Name 'requests' -Path "$PSScriptRoot\logs" -MaxDays 30 -MaxSize 10MB | Enable-PodeRequestLogging
# Включаем error лог
New-PodeLoggingMethod -File -Name 'error' -Path "$PSScriptRoot\logs" -MaxDays 30 -MaxSize 10MB | Enable-PodeErrorLogging
# Создаём собственный access лог
New-PodeLoggingMethod -File -Name 'access' -Path "$PSScriptRoot\logs" -MaxDays 30 -MaxSize 10MB | Add-PodeLogger -Name 'access' -ScriptBlock {
param($item)
# Формат вывода параметров в файл
return "$($item.DateTime), $($item.user)"
# Для вывода в консоль пишем:
# $item | out-default
}
# Пишем функцию для записи в собственный лога
function Write-CustomAccessLog($user, $message)
{
Write-PodeLog -Name 'access' -InputObject @{
DateTime = $(Get-Date -Format "dd-MM-yyyy HH:mm:ss");
user = $user.user;
}
}
# Ограничение по количеству обращений
Add-PodeLimitRule -Type IP -Values all -Limit 6 -Seconds 60
# Запуск удаления файлов
Add-PodeRoute -Method Delete -Path '/api/remove-logs' -Authentication 'ApiKeyAuthenticate' -ScriptBlock {
# Получаем список файлов для удаления
$files_for_delete = Get-ChildItem $using:work_directory -Recurse -Include "*.log"
# Удаляем файлы
$files_for_delete | Remove-Item -Force
# Возвращаем список удалённых файлов
Write-PodeJsonResponse -Value $($files_for_delete | select FullName | ConvertTo-Json)
}
# Получение информации о файлах
Add-PodeRoute -Method Get -Path '/api/get-childitem' -Authentication 'ApiKeyAuthenticate' -ScriptBlock {
# Получаем список файлов в директории
$result = Get-ChildItem $using:work_directory -Recurse -File | select FullName | ConvertTo-Json
# Возвращаем список в JSON
Write-PodeJsonResponse -Value $result
}
}
Итоговый код api_keys.csv
key;user
rU6LCqwcYcvPJnYNuK2yxgMwMALU2f7qpp5DrTcvxWf3aKFZmb;john.doe
Y2wAkaUhY6uQT9Z5DrbxfGZka3kfdCFvPx5TE1os64YYUwvvvR;oleg
RBX8LuwM9tv53jaRrdN3DbinZicjASRcR9NXuUBYXGTcB7SiTx;habr
Как указать прокси-сервер HTTP в Invoke-WebRequest
Прежде чем начать, запустите следующую команду в PowerShell:
Invoke-WebRequest "https://httpbin.org/ip"
That should print something like:
StatusCode : 200
StatusDescription : OK
Content : {
"origin": "194.34.233.12"
}
RawContent : HTTP/1.1 200 OK
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: 32
Content-Type: application/json
Date: Thu, 01 Feb 2024 10:46:14 GMT...
Forms : {}
Headers : {[Connection, keep-alive], [Access-Control-Allow-Origin, *], [Access-Control-Allow-Credentials,
true], [Content-Length, 32]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 32
Если вы хотите получить доступ только к полю «Контент», вы можете сделать это следующим образом:
$response = Invoke-WebRequest "https://httpbin.org/ip"
$response.Content
This would print:
{
"origin": "194.34.233.12"
}
Использование параметра командной строки
Invoke-WebRequest предлагает флаг -Proxy для указания URL-адреса прокси-сервера для запроса.
Итак, синтаксис использования Invoke-WebRequest на прокси-сервере выглядит следующим образом:
Invoke-WebRequest -Proxy "<PROTOCOL>://[<USERNAME>:<PASSWORD>]@<HOST>[:<PORT>]" <Uri>
Если теперь вы выполните эту команду PowerShell:
Invoke-WebRequest -Proxy "http://190.6.23.219:999" "https://httpbin.org/ip"
Invoke-WebRequest -Uri "http://httpbin.org/ip" -Proxy "http://brd.superproxy.io:22225" -ProxyCredential (New-Object System.Management.Automation.PSCredential("brd-customer-CUSTOMER_ID-zone-ZONE’S_NAME", ("ZONE’S_PASSWORD" | ConvertTo-SecureString -AsPlainText -Force)))
The result should be:
StatusCode : 200
StatusDescription : OK
Content : {
"origin": "190.6.23.219"
}
RawContent : HTTP/1.1 200 OK
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: 31
Content-Type: application/json
Date: Thu, 01 Feb 2024 12:36:56 GMT...
Forms : {}
Headers : {[Connection, keep-alive], [Access-Control-Allow-Origin, *], [Access-Control-Allow-Credentials,
true], [Content-Length, 31]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 31
Примечание: не забывайте, что бесплатные прокси-серверы недолговечны! К тому времени, когда вы прочитаете это руководство, маловероятно, что вышеуказанный сервер все еще будет работать. В случае ошибки замените его новым прокси-сервером.
Использование переменных окружения
Начиная с версии PowerShell 7.0, Invoke-WebRequest поддерживает конфигурацию прокси-сервера с помощью переменных окружения.
Таким образом, другой способ использования прокси-сервера PowerShell в Invoke-WebRequest заключается в установке следующих двух окружений:
HTTP_PROXY — это URL-адрес прокси-сервера, используемый для HTTP-запросов;
HTTPS_PROXY — это URL-адрес прокси-сервера, используемый для HTTPS-запросов.
В Windows можно задать две переменные окружения, используя следующий синтаксис PowerShell:
$env:HTTP_PROXY = "<PROTOCOL>://[<USERNAME>:<PASSWORD>]@<HOST>[:<PORT>]"
$env:HTTPS_PROXY = "<PROTOCOL>://[<USERNAME>:<PASSWORD>]@<HOST>[:<PORT>]"
В нашем примере команды будут выглядеть следующим образом:
$env:HTTP_PROXY = "http://190.6.23.219:999"
$env:HTTPS_PROXY = "http://190.6.23.219:999"
В macOS и Linux вам необходимо использовать следующий синтаксис:
export HTTP_PROXY="<PROTOCOL>://[<USERNAME>:<PASSWORD>]@<HOST>[:<PORT>]"
export HTTPS_PROXY="<PROTOCOL>://[<USERNAME>:<PASSWORD>]@<HOST>[:<PORT>]"
Итак, две команды будут следующими:
export http_proxy="http://190.6.23.219:999"
export https_proxy="http://190.6.23.219:999"
Отныне каждый запрос Invoke-WebRequest будет проходить через указанные прокси-серверы без необходимости добавлять опцию -Proxy. После настройки envs запустите следующую команду:
Invoke-WebRequest "https://httpbin.org/ip"
You will get the same result as before:
StatusCode : 200
StatusDescription : OK
Content : {
"origin": "190.6.23.219"
}
RawContent : HTTP/1.1 200 OK
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: 31
Content-Type: application/json
Date: Thu, 01 Feb 2024 12:36:56 GMT...
Forms : {}
Headers : {[Connection, keep-alive], [Access-Control-Allow-Origin, *], [Access-Control-Allow-Credentials,
true], [Content-Length, 31]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 31
Чтобы отключить прокси-серверы Invoke-WebRequest, отключите переменные окружения с помощью:
$env:HTTP_PROXY = ""
$env:HTTPS_PROXY = ""
Or on macOS and Linux:
unset HTTP_PROXY
unset HTTPS_PROXY
Перезапуск службы на сервере по клику на сайте
Наш первый сервис должен выдавать таблицу со списком из нескольких выбранных нами служб на сервере с их статусом и предоставлять возможность их остановить, запустить и перезапустить.
Из Hello World примера выше мы увидели, что работа фреймворка построена на создании маршрутов (Add-PodeRoute
), которые принимают запросы каким-либо методом (на самом деле могут принимать даже несколько методов). Для сервиса работающего со службами мы с лёгкостью можем описать необходимые маршруты:
Для запуска службы, командлет
Start-Service
Для остановки службы, командлет
Stop-Service
Для перезапуска службы, командлет
Restart-Service
Напишем каркас сервиса:
Start-PodeServer {
# Запускаем сервер на http://localhost:8080
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
# Маршрут для главной страницы с таблицей
Add-PodeRoute -Method Get -Path '/restart-services' -ScriptBlock {
# TODO: Получать информацию о запущенных службых и передавать на страницу
pass
}
# Маршрут для запуска службы
Add-PodeRoute -Method Get -Path '/start' -ScriptBlock {
# TODO: Запустить переданную в параметре службу
pass
}
# Маршрут для остановки службы
Add-PodeRoute -Method Get -Path '/stop' -ScriptBlock {
# TODO: Остановить переданную в параметре службу
pass
}
# Маршрут для запуска службы
Add-PodeRoute -Method Get -Path '/restart' -ScriptBlock {
# TODO: Перезапустить переданную в параметре службу
pass
}
}
Чтобы отрисовать главной страницу с таблицей нам необходимо сделать шаблон. В Pode такой шаблон называется view (документация). Процесс похож на то, как это реализовано в других фреймворках, на тот же Django.
Создадим шаблон и сохраним его в views/restart-services.pode
:
<html>
<head>
<title>Перезапуск службы на сервере по клику на сайте</title>
<!--- Стили, чтобы у таблицы отображались границы --->
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
padding: 7px;
}
</style>
</head>
<body>
<table>
<!--- Первая строка с заголовками столбцов таблицы --->
<tr>
<th>Display Name</th>
<th>Name</th>
<th>Status</th>
<th>Actions</th>
</tr>
<!--- Формируем остальные строки таблицы --->
<!--- Здесь начинается Powershell --->
$(foreach ($service in Get-Service) {
"<tr>
<td>$($service.DisplayName)</td>
<td>$($service.Name)</td>
<td>$($service.Status)</td>
<td>
<!--- Отправляем на маршруты с действиями имя службы --->
<a href=`"/start?name=$($service.name)`">Start</a>
<a href=`"/stop?name=$($service.name)`">Stop</a>
<a href=`"/restart?name=$($service.name)`">Restart</a>
</td>
</tr>"
})
<!--- Здесь заканчивается Powershell --->
</table>
</body>
</html>
Мы назвали шаблон restart-services.pode
и сохранили в правильную директорию, значит теперь мы можем объявить его в коде:
...
# Включаем возможность рендеринга и использования .pode файлов
Set-PodeViewEngine -Type Pode
# Маршрут для главной страницы с таблицей
Add-PodeRoute -Method Get -Path '/restart-services' -ScriptBlock {
# Указываем, что мы обращаемся к шаблону restart-services.pode
# в директории /views
Write-PodeViewResponse -Path 'restart-services'
}
...
Уже сейчас можно запустить наш сервер и увидеть результат рендера шаблона:
Мы сразу в шаблоне обработали результат выполнения Get-Services
(строка 23 шаблона) и вывели каждую службу построчно в таблицу
На этом шаге предлагаю изменить шаблон и передавать в него уже готовый список служб из скрипта. Заменяем foreach ($service in Get-Service)
на foreach ($service in $data.services)
. А в код сервера для маршрута с таблицей добавляем список служб (переменная $services
), получение информации о службах (в переменную $data
) и передачу их в шаблон (ключ -Data
у Write-PodeViewResponse
):
Start-PodeServer {
# Ограничиваем список служб
$services = "AdobeARMservice", "bits", "spooler", "wuauserv"
# Запускаем сервер на http://localhost:8080
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
# Включаем возможность рендеринга и использования .pode файлов
Set-PodeViewEngine -Type Pode
# Маршрут для главной страницы с таблицей
Add-PodeRoute -Method Get -Path '/restart-services' -ScriptBlock {
# Получаем информацию о службах
$data = Get-Service $($using:services)
# Указываем, что мы обращаемся к шаблону restart-services.pode
# в директории /views и передаём в шаблон хэш-таблицу
# с результатом выполнения Get-Service
Write-PodeViewResponse -Path 'restart-services' -Data @{ "services" = $data }
}
...
Для кнопки старта службы необходимо сделать так, чтобы выполнялся командлет Start-Service -name имя_службы
при поступлении запроса с именем службы в параметре name
на маршрут /start
.
Как нам получить параметр name
в коде сервера? Для чтения переменной в скрипте на серверной стороне надо использовать WebEvent
(документация). Это хеш-таблица в которой содержится информация о запросах и ответах.
...
# Маршрут для запуска службы
Add-PodeRoute -Method Get -Path '/start' -ScriptBlock {
# Получаем значение параметра name
# В коде шаблона это ?name=$($service.name)
# При переходе в браузере, например, http://localhost:8080/start?name=spooler
$service_name = $WebEvent.Query["name"]
# Проверяем, есть ли служба в списке
if ($($using:services).Contains($service_name)) {
# Если есть, выполняем старт службы
Start-Service -name $service_name
}
# Перенаправляем пользователя на страницу с таблицей
Move-PodeResponseUrl -Url '/restart-services'
}
...
Для того, чтобы получить доступ к переменной объявленной в файле скрипта за пределами маршрута, используется конструкция $using:название_переменной
(документация). Мы воспользовались $using:services
для получения списка сервисов из переменной $services
, объявленной сразу после старта сервера.
Название служб мы тут захардкодили. Если вы передаёте какие-то изменяемые данные, то лучше делать по-другому. Можно из кода сервера обращаться к файлу конфигурации через Get-Content
, импортировать csv файл или может быть вы вообще подключите базу данных.
Аналогичным образом добавляем логику для остальных кнопок:
...
# Маршрут для остановки службы
Add-PodeRoute -Method Get -Path '/stop' -ScriptBlock {
$service_name = $WebEvent.Query.name
if ($($using:services).Contains($service_name)) {
Stop-Service -name $service_name
}
Move-PodeResponseUrl -Url '/restart-services'
}
# Маршрут для перезапуска службы
Add-PodeRoute -Method Get -Path '/restart' -ScriptBlock {
$service_name = $WebEvent.Query.name
if ($($using:services).Contains($service_name)) {
Restart-Service -name $service_name
}
Move-PodeResponseUrl -Url '/restart-services'
}
...
Запускаем сервер, переходим по ссылке http://localhost:8080/restart-service
и проверяем работоспособность.
Клик на сайте превращается в выполнение Powershell команды на сервере
При клике на «Stop» на нашей машине останавливается служба, при клике на «Start» запускается, а при «Restart» перезапускается. Программа минимум сделана, сервис работает.
Ссылка на это демо в GitHub.
Итоговый код server.ps1
Start-PodeServer {
# Ограничиваем список служб
$services = "AdobeARMservice", "bits", "spooler", "wuauserv"
# Запускаем сервер на http://localhost:8080
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
# Включаем возможность рендеринга и использования .pode файлов
Set-PodeViewEngine -Type Pode
# Маршрут для главной страницы с таблицей
Add-PodeRoute -Method Get -Path '/restart-services' -ScriptBlock {
# Получаем информацию о службах
$data = Get-Service $($using:services)
# Указываем, что мы обращаемся к шаблону restart-services.pode
# в директории /views и передаём в шаблон хэш-таблицу
# с результатом выполнения Get-Service
Write-PodeViewResponse -Path 'restart-services' -Data @{ "services" = $data }
}
# Маршрут для запуска службы
Add-PodeRoute -Method Get -Path '/start' -ScriptBlock {
# Получаем значение параметра name
# В коде шаблона это ?name=$($service.name)
# При переходе в браузере, например, http://localhost:8080/start?name=spooler
$service_name = $WebEvent.Query["name"]
# Проверяем, есть ли служба в списке
if ($($using:services).Contains($service_name)) {
# Если есть, выполняем старт службы
Start-Service -name $service_name
}
# Перенаправляем пользователя на страницу с таблицей
Move-PodeResponseUrl -Url '/restart-services'
}
# Маршрут для остановки службы
Add-PodeRoute -Method Get -Path '/stop' -ScriptBlock {
$service_name = $WebEvent.Query.name
if ($($using:services).Contains($service_name)) {
Stop-Service -name $service_name
}
Move-PodeResponseUrl -Url '/restart-services'
}
# Маршрут для перезапуска службы
Add-PodeRoute -Method Get -Path '/restart' -ScriptBlock {
$service_name = $WebEvent.Query.name
if ($($using:services).Contains($service_name)) {
Restart-Service -name $service_name
}
Move-PodeResponseUrl -Url '/restart-services'
}
}
Итоговый код views/restart-services.pode
<html>
<head>
<title>Перезапуск службы на сервере по клику на сайте</title>
<!--- Стили, чтобы у таблицы отображались границы --->
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
padding: 7px;
}
</style>
</head>
<body>
<table>
<!--- Первая строка с заголовками столбцов таблицы --->
<tr>
<th>Display Name</th>
<th>Name</th>
<th>Status</th>
<th>Actions</th>
</tr>
<!--- Формируем остальные строки таблицы --->
<!--- Здесь начинается Powershell --->
$(foreach ($service in $data.services) {
"<tr>
<td>$($service.DisplayName)</td>
<td>$($service.Name)</td>
<td>$($service.Status)</td>
<td>
<!--- Отправляем на маршруты с действиями имя службы --->
<a href=`"/start?name=$($service.name)`">Start</a>
<a href=`"/stop?name=$($service.name)`">Stop</a>
<a href=`"/restart?name=$($service.name)`">Restart</a>
</td>
</tr>"
})
<!--- Здесь заканчивается Powershell --->
</table>
</body>
</html>
Необходимые советы и рекомендации
Ознакомьтесь с полезными уловками и ценными советами по работе с прокси-сервером PowerShell Invoke-WebRequest на профессиональном уровне.
Игнорирование конфигурации прокси-сервера PowerShell
Если вы хотите запретить Invoke-WebRequest использовать настроенный прокси-сервер PowerShell для чтения из переменных окружения, вы можете воспользоваться параметром -NoProxy следующим образом:
Invoke-WebRequest -NoProxy <Uri>
Эта команда предписывает Invoke-WebRequest связаться с без использования прокси-сервера.
Чтобы убедиться в работоспособности данного подхода, настройте прокси-сервер в envs и запустите его:
Invoke-WebRequest -NoProxy "https://httpbin.org/ip"
Avoid SSL Certificate Errors
При использовании прокси-сервера HTTP ваши запросы могут не выполняться из-за ошибок SSL-сертификата. Чтобы избежать этого, задайте параметр -SkipCertificateCheck:
Invoke-WebRequest -SkipCertificateCheck -Proxy "<PROTOCOL>://[<USERNAME>:<PASSWORD>]@<HOST>[:<PORT>]" <Uri>
– SkipCertificateCheck помогает избежать ошибок сертификатов, разрешая небезопасные подключения к серверу. Имейте в виду, что использование этого параметра небезопасно. Задавайте его только при работе с известными хостами.
Например, вы можете связываться с HTTPBin через прокси-сервер, обходя проблемы с SSL, с помощью следующего:
Invoke-WebRequest -SkipCertificateCheck -Proxy "http://190.6.23.219:999" "https://httpbin.org/ip"
Заключение
Из этого руководства по прокси-серверу PowerShell вы узнали, что представляет собой инструмент Invoke-WebRequest, как он работает и как его использовать с прокси-сервером HTTP/HTTPS/SOCKS. Как оказалось, нельзя полагаться на прокси-серверы от бесплатных поставщиков. Поэтому единственное решение, которое необходимо принять, — это выбор поставщика услуг прокси-серверов. Экономьте время и энергию и обращайтесь непосредственно к Bright Data — лучшей компании на рынке в этой области.
Bright Data управляет лучшими прокси-серверами в мире, обслуживая компании из списка Fortune 500 и более 20 000 клиентов. Ее всемирная сеть прокси-серверов включает в себя:
В целом, это одна из крупнейших и самых надежных на рынке прокси-сетей, ориентированных на скрейпинг.
Поговорите с одним из наших торговых представителей и узнайте, какие продукты Bright Data лучше всего отвечают вашим потребностям.
Заключение
Надеюсь, мы провели время с пользой и вы убедились, что веб с интеграцией Powershell не такой страшный и реализуется за адекватное время. В комментариях буду рад услышать про успешные кейсы ваших интерфейсов администрирования или проектов с использованием Pode. А так же способы реализовать веб Powershell ещё проще или почему не надо использовать фреймворк Pode.
Спасибо за внимание!
Олег Белов
System Engineer Teamlead (ex. Kaspersky, ex. КРОК)
P.S. На данный момент я открыт к предложениям о работе. Remote позиции инженера DevOps/системного инженера. Ссылки для рекомендаций: LinkedIn, Хабр Карьера.
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку