Как разбить вывод команды powershell на ключи с значениями node js по типу ключ

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

![Invoke-Webrequest With a Proxy](https://ru-brightdata.com/wp-content/uploads/2024/02/Invoke-Webrequest-With-a-Proxy.svg)

Командлет 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 фреймворка 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

Тестирование 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-адреса прокси-сервера для запроса.

:/>  Подготовка автоматического восстановления windows

Итак, синтаксис использования 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'

    }

...

Уже сейчас можно запустить наш сервер и увидеть результат рендера шаблона:

:/>  Компьютер с win 10 не виден в сети

Мы сразу в шаблоне обработали результат выполнения Get-Services  (строка 23 шаблона) и вывели каждую службу построчно в таблицу

Мы сразу в шаблоне обработали результат выполнения 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.

Спасибо за внимание!

Как разбить вывод команды powershell на ключи с значениями node js по типу ключ

Олег Белов

System Engineer Teamlead (ex. Kaspersky, ex. КРОК)

P.S. На данный момент я открыт к предложениям о работе. Remote позиции инженера DevOps/системного инженера. Ссылки для рекомендаций: LinkedIn, Хабр Карьера.

Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку

Оставьте комментарий