Можно настроить прохождение корпоративной авторизации с проверкой доменной учётной записи (хэша пароля пользователя в Active Directory), совмещенной с “белым списком” доверенных МАС-адресов устройств пользователя. В таком сценарии пользователь сможет пройти авторизацию только на том компьютере, адрес которого явным образом прописан в атрибутах учетной записи пользователя. Пройти авторизацию (даже с верным паролем) на соседнем доменном или не доменном устройстве не получится.
Для настройки этой функции необходимо, прежде всего, сформировать базу МАС-адресов устройств пользователей. Для этого следует воспользоваться средством автоматизации PowerShell или другими инструментами ОС Windows. Предположим, что пользователю с именем wifitest разрешено подключение с двух АРМ с адресами E4:02:9B:7B:8F:B9 и E4:02:9B:7B:8F:AA. При помощи инструментов скриптования в AD или вручную через ADSIEdit следует отредактировать значение атрибута registeredAddress у пользователя. На рисунке представлен процесс редактирования значения атрибута пользователя.
Значение МАС-адреса необходимо указывать в шестнадцатеричном виде без дополнительных знаков. После назначения адреса следует проверить, что у записи пользователя присутствуют такие атрибуты:
C:\Windows\system32>dsquery * -filter "&(objectClass=user)(sAMAccountName=wifitest)" -attr registeredAddress
registeredAddress
0xe4 0x02 0x9b 0x7b 0x8f 0xb9 ;0xe4 0x02 0x9b 0x7b 0x8f 0xaa ;
Дополнительно следует настроить взаимодействие сервера системы WNAM с доменом, как это описано в соответствующем разделе настоящей документации (Способ интеграции системы WNAM с доменом) и подключиться к домену (инструкция по подключению представлена в разделе Подключение к Active Directory). Далее необходимо обновить список атрибутов, взяв за основу запроса данные пользователя:
После изменения настроек следует сохранить настройки домена. осле этого можно использовать атрибут registeredAddress при формировании профиля аутентификации. Следует создать профиль, в котором присутствуют следующие данные:
- ссылка на домен;
- опция “Совпадение в атрибуте в Active Directory (домен выбран ниже)”;
- атрибут и правило сравнения его значения.
Следует учитывать, что МАС-адрес подключающегося устройства (эндпоинта) передается в RADIUS-атрибуте Calling-Station-Id.
После внесения изменений в настройки профиль аутентификации следует нажать кнопку “Сохранить” и затем произвести попытку сетевого подключения. Система WNAM запросит для подключающегося пользователя список его доменных атрибутов и затем попытается провести сравнение:
filterForAdAttribute user: wifitest@lab.wnam.ru, dom_req: null, attrs: [DomainAttribute [name=registeredAddress, value=[ "e4029b7b8fb9", "e4029b7b8faa" ]]]filterForAdAttribute user: wifitest@lab.wnam.ru, rule: Contains, attr: DomainAttribute [name=registeredAddress, value=[ "e4029b7b8fb9", "e4029b7b8faa" ]], compare to: E4:02:9B:7B:8F:B9, result: match
Если пытаться произвести подключение с другого компьютера, результат проверки будет следующий:
Данное сообщение означает, что профиль будет исключен из списка, и авторизация завершится неудачно (отказ, помещение в карантин).
четверг, 4 апреля 2024 г., 00:51:35
Изучение методов пересылки электронной почты в PowerShell с использованием Office365 Graph API
В мире автоматизированной обработки и управления электронной почтой PowerShell выделяется как универсальный инструмент, особенно при интеграции с Graph API Office365. Возможность программно читать, фильтровать и манипулировать электронной почтой дает значительное преимущество как администраторам, так и разработчикам. Однако возникают уникальные проблемы, такие как пересылка определенного электронного письма, идентифицируемого его идентификатором сообщения. Эта операция не так проста, как можно было бы надеяться, что приводит к вопросам о возможностях и ограничениях API Graph в сценариях пересылки электронной почты.
Этот сценарий становится особенно актуальным, когда требуется устранение неполадок или аудит, например, исследование ошибок в производственных процессах, отмеченных уведомлениями по электронной почте. Наличие технических знаний по пересылке электронного письма самому себе для более тщательного изучения может оказаться неоценимым. Это руководство призвано пролить свет на эту проблему, предоставляя идеи и решения для пересылки электронных писем с помощью PowerShell и Graph API, даже когда прямые методы кажутся неуловимыми. Он устраняет пробелы в документации и упрощает процесс для тех, кто хочет улучшить свои стратегии управления электронной почтой.
Команда | Описание |
---|---|
Invoke-RestMethod | Отправляет запрос HTTP или HTTPS веб-службе RESTful. |
@{…} | Создает хеш-таблицу для хранения пар ключ-значение, используемую здесь для построения тела веб-запроса. |
Bearer $token | Метод авторизации, включающий токены безопасности, называемые токенами на предъявителя. Используется для доступа к защищенным ресурсам. |
-Headers @{…} | Указывает заголовки веб-запроса. Здесь он используется для включения токена авторизации в вызов API. |
-Method Post | Определяет метод веб-запроса, при этом «Post» указывает, что данные отправляются на сервер. |
-ContentType “application/json” | Указывает тип носителя запроса, указывая, что текст запроса имеет формат JSON. |
$oauth.access_token | Получает доступ к свойству access_token из ответа аутентификации OAuth, используемому для выполнения аутентифицированных запросов. |
“@{…}”@ | Определяет здесь строку — функцию PowerShell для объявления многострочных строк, часто используемую для полезных данных JSON. |
Углубленное погружение в автоматизацию пересылки электронной почты с помощью PowerShell и Graph API
Предоставленные сценарии предназначены для автоматизации процесса пересылки одного электронного письма по его идентификатору с помощью PowerShell и Microsoft Graph API — мощного инструмента для взаимодействия со службами Office 365. Первый скрипт ориентирован на получение токена аутентификации, который имеет решающее значение для безопасного доступа к API Graph. Он начинается с определения идентификатора клиента приложения, идентификатора клиента и секрета клиента, которые являются важными учетными данными для потока аутентификации OAuth. Эти переменные используются для создания тела запроса POST, направленного к конечной точке Microsoft OAuth2. Этот запрос возвращает токен доступа после успешной аутентификации. Этот токен затем используется в заголовке последующих запросов для аутентификации пользователя и авторизации действий в Office 365, таких как пересылка электронной почты.
Пересылка электронной почты в Office365 через PowerShell и Graph API
Сценарии PowerShell для пересылки электронной почты
$clientId = "your_client_id"
$tenantId = "your_tenant_id"
$clientSecret = "your_client_secret"
$scope = "https://graph.microsoft.com/.default"
$body = @{grant_type="client_credentials";scope=$scope;client_id=$clientId;client_secret=$clientSecret;tenant_id=$tenantId}
$oauth = Invoke-RestMethod -Method Post -Uri https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token -Body $body
$token = $oauth.access_token
$messageId = "your_message_id"
$userId = "your_user_id"
$forwardMessageUrl = "https://graph.microsoft.com/v1.0/users/$userId/messages/$messageId/forward"
$emailJson = @"
{
"Comment": "See attached for error details.",
"ToRecipients": [
{
"EmailAddress": {
"Address": "your_email@example.com"
}
}
]
}
"@
Invoke-RestMethod -Headers @{Authorization="Bearer $token"} -Uri $forwardMessageUrl -Method Post -Body $emailJson -ContentType "application/json"
Настройка OAuth для доступа к API Graph в PowerShell
Настройка аутентификации с помощью PowerShell для Graph API
$clientId = "your_client_id"
$tenantId = "your_tenant_id"
$clientSecret = "your_client_secret"
$resource = "https://graph.microsoft.com"
$body = @{grant_type="client_credentials";resource=$resource;client_id=$clientId;client_secret=$clientSecret}
$oauthUrl = "https://login.microsoftonline.com/$tenantId/oauth2/token"
$response = Invoke-RestMethod -Method Post -Uri $oauthUrl -Body $body
$token = $response.access_token
function Get-GraphApiToken {
return $token
}
# Example usage
$token = Get-GraphApiToken
Write-Host "Access Token: $token"
Изучение расширенного управления электронной почтой с помощью PowerShell и Graph API
Углубляясь в управление электронной почтой с помощью PowerShell и API Microsoft Graph, вы обнаруживаете надежную структуру, предназначенную для сложных операций с электронной почтой, помимо простого поиска и пересылки. Эта экосистема предоставляет программируемый интерфейс для функций электронной почты Office 365, предлагая детальный контроль над взаимодействием с электронной почтой. Интеграция PowerShell с Graph API расширяет возможности сценариев для автоматизации таких задач, как пересылка электронной почты, что крайне важно для администраторов, стремящихся оптимизировать свой рабочий процесс или процессы отладки путем перенаправления электронных писем на определенные адреса для дальнейшего анализа. Такая автоматизация особенно полезна в средах, где электронная почта играет решающую роль в рабочих процессах, позволяя быстро реагировать на ошибки или исключения, отмеченные уведомлениями по электронной почте.
Использование API Graph для операций с электронной почтой подчеркивает важность понимания OAuth 2.0 для безопасной аутентификации и авторизации. Сложность управления токенами аутентификации, создания запросов API и обработки ответов требует четкого понимания как сценариев PowerShell, так и структуры Graph API. Эти знания имеют решающее значение для создания сценариев, которые могут манипулировать объектами электронной почты, фильтровать на основе определенных критериев и выполнять такие операции, как пересылка, при этом придерживаясь лучших практик безопасности. Такие возможности неоценимы для ИТ-специалистов, которым поручено поддерживать бесперебойную работу каналов связи внутри организаций, демонстрируя мощь и гибкость сочетания PowerShell с API-интерфейсом Graph для расширенного управления электронной почтой.
Основные вопросы по пересылке электронной почты PowerShell через Graph API
- Вопрос: Могу ли я переслать несколько электронных писем одновременно с помощью PowerShell и Graph API?
- Отвечать: Да, путем перебора набора идентификаторов электронной почты и отправки отдельных запросов на пересылку для каждого.
- Вопрос: Можно ли настроить тело пересылаемого сообщения?
- Отвечать: Разумеется, API позволяет вам включать в запрос на пересылку собственное тело и тему сообщения.
- Вопрос: Как убедиться, что мой сценарий использует последний токен доступа?
- Отвечать: Реализуйте логику обновления токена в своем скрипте, чтобы запросить новый токен до истечения срока действия текущего.
- Вопрос: Могу ли я пересылать электронные письма нескольким получателям одновременно?
- Отвечать: Да, вы можете указать несколько получателей в полезных данных запроса на пересылку.
- Вопрос: Необходимо ли иметь права администратора для использования PowerShell для пересылки электронной почты?
- Отвечать: Не обязательно, но вам необходимы соответствующие разрешения для доступа и пересылки электронной почты из соответствующего почтового ящика.
Завершение расширенных операций с электронной почтой
В ходе исследования использования PowerShell в сочетании с API Graph для пересылки электронной почты в Office 365 мы обнаружили сочетание технической сложности и эксплуатационной необходимости. Это путешествие подчеркивает важность надежных навыков написания сценариев, глубокого понимания возможностей Graph API и пристального внимания к механизмам аутентификации, особенно в безопасных средах. Возможность программного управления электронной почтой — в частности, пересылки их на основе уникального идентификатора — демонстрирует значительный прирост эффективности при выполнении административных задач, устранении неполадок и управлении процессами. Более того, исследование проливает свет на более широкую применимость этих инструментов для автоматизации и оптимизации операций, связанных с электронной почтой, демонстрируя их потенциал для повышения производительности и непрерывности работы в различных бизнес-контекстах. По мере того, как мы продолжаем преодолевать сложности цифровой коммуникации, интеграция языков сценариев, таких как PowerShell, с API-интерфейсами, предназначенными для управления электронной почтой, становится краеугольным камнем стратегии для ИТ-специалистов, стремящихся использовать технологии для достижения организационных целей.
Все привет! В уходящем году хочу оставить небольшой след про такой сильный язык программирования, как PowerShell. Вероятно, уже в следующем году, ввиду тенденции отказа от Windows систем в нашей стране, моя практика в этой области закончится, а за пару лет активности, так и не собрался с силами опубликовать что-то подобное. Кода тут не будет, для этого у меня есть отдельная работа с заметками, цель статьи, еще раз подчеркнуть реальные возможности данного языка, где я буду ссылаться на работы, которые я старался делать универсальными, а так же сделаю акцент на полезных модулях.
Буквально два года назад, имея базовые знания навигации в консоли Linux, написание несложных batch-файлов и небольшой опыт VBScript открыл для себя PowerShell, и после этого я уже в прямом смысле этого слова, не мог остановиться реализовывать свои идеи, правда, такое дело очень затягивает. В один момент решился завести канал на GitHub и там же по сей день виду работу с заметками, где за это время накопилось более 6 тысяч строк из описания работы cmdlet (PowerShell-команд) и утилит для Системного Администратора (AD, Exchange, VMWare, MSSQL и т.д.) с примерами, ведь далеко не все получалось найти в интернете, порой, только изучая на практике свойства и методы объектов, можно получить желаемый результат. Так же набралась небольшая коллекция модулей и тестовый стенд WinForms с примерами работы различных методов, на котором я в дальнейшем базировался для написания приложений с графическим интерфейсом. Все работы писал по большей части для себя с целью автоматизировать и разгрузить текущий рабочий процесс, иногда помочь коллегам, именно по этому мне хочется поделиться своими наработками, возможно кому-то это еще сможет пригодиться.
Так сложилось, что многие с кем я общался, недолюбливают данный язык, порой, не воспринимают за язык вовсе, на рынке и правда для написания скриптов есть более функциональные конкуренты, но когда ты являешься администратором Windows систем, то это однозначно лучший, а порой незаменимый инструмент под рукой. Во-первых, язык является объектно-ориентированным, что сильно упрощает работу, и хотя в последнее время активно пишу на Bash, где преимущества в работе с текстом очевидны, но в силу привычки, мне очень не хватает данной модели, прибегая к сторонним инструментам, например jq и xmllint, которые нужно изучать отдельно, формировать массивы используя свой синтаксис. В PowerShell весь процесс автоматизирован и привычен. Во-вторых, язык на прямую интегрирован с платформой .NET, что дает возможности, как создание графических форм, используя WinForms и WPF, сетевые сокеты (например, можно написать свой syslog сервер), использовать различные библиотеки (изначально написанные на, или для C#), вплоть до создания и манипуляциями с графикой (создание или редактирование изображений).
Думаю очевидно, что для системного администратора графический интерфейс дает много возможностей, например, можно оптимизировать работу консольной команды или целого ряда cmdlet, не запоминая все ключи, имея возможность менять параметры наглядно в формах интерфейса. Автоматизировать процесс создания учетных записей в Active Directory, или формирование динамических табличный отчет состояния инфраструктуры VMWare (помимо набора модулей из PowerCLI присутствует целый ряд встроенных командлетов для Hyper-V) или Exchange (EMShell), где с помощью нескольких кликов можно узнать состояние всех баз данных и групп доступности в удобном формате, просмотреть message tracking log и выгрузить PST (возможность, которая отсутствует в консоли управления). Все это позволяет автоматизировать рутину, а главное, оптимизировать работу инструментов под себя. Естественно, никто не мешает выбрать другой интерфейс взаимодействия, например написать своего собственного бота Telegram или использовать Pipeline в Jenkins, где вся логика будет на PowerShell, и вероятно, для многих решения на базе скриптов могут казаться костыльными, тем не менее, это работает, а порой очень хорошо и других вариантов попросту может и не быть. Но в конечно итоге можно создать свою службу (используя бесконечный цикл и NSSM) или даже конвертировать скрипт в исполняемый exe-файл используя модуль ps2exe.
Фоновые задания (Jobs). Здесь смотря с кем сравнивать, например в Bash для управления jobs слишком мало функционала, они есть и работают неплохо, но по факту сами задания непредсказуемы и нуждаются в дополнительном логирование. По времени выполнения, тут все не очень хорошо, но наглядно, т.к. задания выполняются упорядочено (в отличие от Thread в Python). Для сравнения на ping 256 машин занимает примерно 2 минуты используя встроенный модуль, если воспользоваться модулем ThreadJob, время сокращается в среднем до 26 секунд, а вот задания через PoshRSJob уже 13 секунд.

А вот в Python это занимает всего 5 секунд уже с resolve именем хоста, где можно так же создать отдельный поток для графического интерфейса (в примере, TKInter).

Но эту проблему можно исправить, используя классы .NET, например, System.Net.Sockets.TcpClient для проверки портов, где можно определить timeout ожидания ответа в 100мс и ситуация становится более наглядной, и демонстрирует разницу, при проверки одного порта на всех 254 хостах, создание заданий с использования ThreadJob занимает в среднем на 30-40% меньше времени, чем пауза (Start-Sleep) в 100 миллисекунд.

Управлять же заданиями максимально удобно, т.к. есть возможность отслеживать их статус, узнать состояние и время работы, читать вывод, ставить на паузу и даже отслеживать историю выполненных заданий.

Далее мне хотелось увеличить возможности удаленного администрирования без прямого взаимодействия с удаленным пользователем и добавить инструментов, за время работы у меня выработался определенный алгоритм, который я выполняю при анализе проблем в работоспособности операционной системы – это оценка нагрузки процессов, состояние служб, проверка uptime, последних обновлений и установленных приложений, состояние синхронизации компьютерных часов (ntp) и лицензий (kms), анализ сетевых настроен, подключений и конечно логов. Всем этим и не только возможно управлять удаленно, и у меня это получилось поместить в один интерфейс.
Работа с REST API. Есть одна ключевая особенность, PowerShell способен на прямую конвертировать вывод JSON и XML в объект (в обоих направлениях), что сильно оптимизирует работу для написания своих собственных скриптов взаимодействия с различными сервисами, для меня это остается самым удобно читаемым форматом, на фоне альтернатив, например, модулей для IDE. В остальном, преимуществ у Invoke-RestMethod или Invoke-WebRequest над curl особых нет. А вот для создания собственного REST API сервера вариантов наберется даже несколько. Имеется полноценный кроссплатформенный Web Framework Pode для создания REST API, веб-сайтов и серверов TCP/SMTP. Можно воспользоваться встроенным классом .NET HttpListener, на базе которого можно обработать все условия кодов возврата и использоваться базовую авторизация (Base64), и допустим создать возможность управления Windows системой на платформе Linux без необходимости конфигурирования WinRM – WinAPI, пример такой реализации.
В копилку кроссплатформенности, PowerShell Core очень неплохо работает в системе Linux, пусть и реализовано малая часть функционала, тем не менее можно комбинировать с другим языком, например Bash, и создать тот же простенький REST сервер.
Благодаря прямому взаимодействию PowerShell с ОС Windows и COM-объектами, вы можете создавать собственные кликеры на WScript для автоматизации любым простых действий (порой незаменимый подход по сей день, особенно, если это старые desktop Windows приложения), управлять продуктами MS Office (например, автоматическое создание подписи в Outlook с получением информации о пользователи из LDAP), создание и парсинг Excel-отчетов или автоматизация действий в браузере через Internet Explorer (альтернатива Selenium). Для примера у меня была задача, читать из smb каталога Excel-файл и смотреть на даты окончания срока доступа, по итогу такого совпадения делать рассылку на почту (указанную в столбце с Email и информацией в теле письма, о чем идет речь из столбца с описанием). Или, с указанным интервалом получать метрики измерений скорости интернета, используя Okkla Speedtest с выгрузкой их в InfluxDB и отрисовской в Grafana. Так же мне было удобно читать отчеты на почте состояния заданий и репозиториев Veeam, пока я нахожусь в дороге на работу, а позднее, написал модуль получения данных через REST API, который в дальнейшем развил до автоматизации добавления виртуальных машин в систему резервного копирования.
.NET Framework/Core. Модули есть практически для всего, можно даже создать собственный сервер мониторинга через WMI/CIM (фактически метрики дублируют Performance Counter), или библиотеку SNMP. Есть ряд библиотек для различных баз данных, таких как MS SQL (имеет 3 варианта взаимодействия, в том числе встроенный класс System.Data.SqlClient), MySQL Connector, SQLite, для других же присутствует стандартизированный программный интерфейс взаимодействия ODBC (присутствуют драйверы для PostgreSQL, Firebird, Elastic и т.д.). Примеры для работы с различными модулями можно посмотреть тут. У меня как-то была задача, реализовать рассылку оповещений об окончании срока действия лицензий из ITInvent (данная программа мне очень нравится своим удобством удобной, т.к. работал с ней в разных компаниях, но к сожалению данный функционал отсутствовал), где база крутилась в MS SQL Express на Oracle Linux, разобравшись во взаимосвязях между таблицами эту задачу получилось выполнить за 3 дня и на выходе всего 70 строк кода.
Selenium. Очень удобный и простой инструмент в первую очередь для функционального тестирования, но так же может быть полезен при автоматизации тех действий, для которых не предусмотрено API. Ключевой проблемой в PowerShell является отсутствие актуального готового решения подготовки всех зависимостей для работы с данным инструментом. Данный процесс подготовки у меня получилось автоматизировать, и как мне кажется, требует отдельного внимания, по этому планирую написать отдельную статью.
На этом пожалуй все. Осознаю, что многие решения нужно было описывать в свое время отдельной статьей, все работы это только энтузиазм, не являюсь разработчиком, весь опыт исключительно на практике. В освоение большинства тем очень помогали статьи различных источников, в т.ч. Habr и большая база знаний Microsoft, где так же присутствует браузер модулей PowerShell и .NET API.
Все привет! В продолжение статьи о возможностях PowerShell, хочу поделиться несложной реализацией создания REST API и простого Web-сервера, используя только PowerShell на базе класса .NET HttpListener. Такая реализация позволяет настроить endpoint-ы (конечные точки) для обработки GET и POST запросов, которые принимают параметры в заголовке запроса, использовать Basic-авторизацию (на основе Base64), обрабатывать любые коды возврата, а так же без лишнего кода, отдавать информацию в разных форматах: json, xml, html, csv. Хочу акцентировать, что данное решение, возможно, не является самым правильным, тем не менее успешно помогло мне покрыть потребности нескольких задач и хотел лишний раз продемонстрировать возможности языка.
Для начала расскажу, кому и зачем данная реализация может понадобиться, далее приведу пример работы с готовым решение и разберем основные моменты для создания своего сервера. Стоит сразу упомянуть, что существует кросс-платформенный Web Framework Pode, и это не единственное решение, успел попробовать как минимум три, но, пожалуй, самое интересное, поддерживаемое и задокументированное. Мне же хотелось иметь свою реализацию, где будут отсутствовать сторонние зависимости, и немного больше понимать, что происходит на стороне сервера во время его работы, в частности, для отладки.
Кому и зачем данная реализация может понадобиться? Приведу свой пример, у меня была задача удаленно реализовать доступ к десктопному приложению, у которого для специфического взаимодействия с ним была возможность выполнять только локальные команды через консоль. Из условий, не было возможности настроить и использовать на машинах WinRM и OpenSSH, ввиду ограничений в компании со стороны ИБ, в то же самое время HTTP был валидным и стандартизированным (после HTTPS) решением. В результате, выполнение и вывод команд получилось автоматизировать, а в дополнение к этому, добавить настройки реестра, чистку temp и логов, что расширило возможности и позволило инженеру DevOps внедрить их в свой Pipeline, используя привычный интерфейс.
Во-вторых, работая системным администратором, в качестве интерфейса для автоматизации задач я использовал WinForms, редко Telegram. Тогда мне очень хотелось попробовать реализовать свой Web-интерфейс для визуализации подобных задач. Важно заметить, что не обладаю сильными познаниями в области систем CI/CD, и конечно, рациональнее использовать, например, интерфейс Jenkins для подобных целей. Тем не менее подобное решение имеет место, т.к. Jenkins все таки не содержит такой кастомизации, как собственный интерфейс.
В-третьих. У меня есть небольшой проект, целью которого является поиск и доставка контента из конкретного torrent-трекера Кинозал до телевизора с Plex (на эту тему у меня есть отдельная статья на Habr). Так сложилось, что за основу я выбрать язык Bash, т.к. планировал запускать бота удаленно и использовать только REST API интерфейс для взаимодействия со всеми сервисами. В течении всего времени эксплуатации, мне не хватало такого функционала, как остановка и повторный запустить torrent-клиента (qBittorrent), или просматривать свободное место на диске, узнать размер конкретных директорий и файлов, а так же возможности их удаления. По аналогии с другими сервисами, мне хотелось использовать единый интерфейс (REST API), в попытках найти готовое решение в виде десктопного приложения для Windows, вспоминая, что уже взаимодействовал с Open Hardware Monitor, используя его как клиент (в режиме HTTP), уже писал модуль для получения метрик через REST API (с возможностью отправки их в InfluxDB и визуализацией в Grafana). Но этого было мало, например для просмотра и удаления файлов можно настроить сервер Everything (который тоже имеет HTTP-сервер). Тут я понял, для покрытия нескольких специфических и не сложных задачи устанавливать дополнительно 2-3 сервиса нерационально, по этому решил написать отдельное решение.
Далее, речь пойдет про WinAPI, решение, с помощью которого у меня получилось покрыть все мои потребности, а конкретно: удаленная остановка и запуск служб и процессов, вывод метрик, максимально приближенных к диспетчеру задач (список физических и логических дисков, показатели IOps, потребление RAM, нагрузка CPU, количество запущенных процессов, потоков, дескрипторов и т.п.), а так же просматривать список директорий и файлов, их размер и количество, с возможностью удаления.
Как это выглядит на практике. Для установки и запуска данного сервиса я попробовал реализовать два решения. Запуск в виде исполняемого файла (используя модуль ps2exe), в таком варианте можно запускается отдельная консоль, где можно наблюдать весь лог и завершать процесс при закрытии консоли. Второй вариант, это запуск фонового процесса, в таком случае для чтения лога используется файл. Но такое решение не самое удачное, т.к. модуль имеет ограничение, которое позволяет запускать любой скрипт только в PowerShell 5.1 и командлеты из версии Core попросту не буду работать.
Второй вариант, это запуск в качестве службы, процесс установки получилось так же автоматизировать, достаточно на любой машине, где есть доступ в интернет запустить этот скрипт. Настроить свои данные для авторизации и задать свой номер порта (предварительно открыть его в firewall) в конфигурационном файле, после чего, начать взаимодействовать на любой системе, используя Invoke-RestMethod или Curl:

lifailon@hv-devops-01:~$ user="rest"
lifailon@hv-devops-01:~$ pass="api"
lifailon@hv-devops-01:~$ curl -s -X GET -u $user:$pass http://192.168.3.100:8443/api/service/winrm # запрашиваем статус службы WinRM
{
"Name": "WinRM",
"DisplayName": "Служба удаленного управления Windows (WS-Management)",
"Status": "Stopped",
"StartType": "Automatic"
}
lifailon@hv-devops-01:~$ curl -s -X POST -u $user:$pass --data '' http://192.168.3.100:8443/api/service/winrm -H "Status: Start" # запускаем службу
{
"Name": "winrm",
"DisplayName": "Служба удаленного управления Windows (WS-Management)",
"Status": "Running",
"StartType": "Automatic"
}
Пример простого Web-сервера был скорее эксперимент, чем необходимость (очень уж хотелось закрыть старый гештальт). Тем не менее выглядит решение так:

Естественно, для обработки кнопок в браузере не обошлось без JavaScript, особых познаний языка тут не требуется, нашел буквально первый пример в интернете как создать кнопки и обработать их действие при нажатии, ознакомившись с основами HTML-синтаксиса и додумав логику, все получилось сделать достаточно просто. Вот пример с комментариями:
# Типовое условие для проверки вхождения на соответствие метода (GET) и конечной точки (/service)
elseif ($context.Request.HttpMethod -eq "GET" -and $context.Request.RawUrl -eq "/service") {
# Получаем массив из списока служб, используя кастомную функцию для вывода с подробной информацией
$Services = Get-ServiceDescription *
# Формируем текст HTML-документа, задаем заголовок страницы и открываем тело страницы
$GetService = "<html><head><title>Service</title></head><body>"
# Добавляем заготовленные кнопки, которые перенаправляет на другие url
$GetService += $BodyButtons
# Указываем на создание таблицы и задаем имена столбцов
$GetService += "<table border='1'>"
$GetService += "<tr><th>Name</th><th>Status</th><th>Action</th><th>Start Type</th></tr>"
# Передаем в цикл список служб и забираем значения
foreach ($Service in $Services) {
$name = "<b>$($Service.Name)</b>"
$status = $Service.Status
# Проверяем статус службы, если работает, красим в зеленый цвет
if ($status -eq "Running") {
$status = "<font color='green'><b>$status</b></font>"
} else {
$status = "<font color='red'><b>$status</b></font>"
}
$StartType = $Service.StartType
# Заполняем значения столбцов, по анологии с наименованием столбцов (в блоке <tr>)
$GetService += "<tr><td>$name</td><td>$status</td>"
# Создаем кпноки, которые при нажатии ссылаются на функции startService и stopService, которые в качестве параметра передают наименование службы
$GetService += "<td><button onclick='startService(""$($Service.Name)"")'>Start</button> "
$GetService += "<button onclick='stopService(""$($Service.Name)"")'>Stop</button></td>"
$GetService += "<td>$StartType</td></tr>"
}
$GetService += "</table>"
$GetService += '
# Формируем в блоке <script> функции, для обработки нажатия на кнопки
<script>
function startService(serviceName) {
sendServiceAction("Start", serviceName);
}
function stopService(serviceName) {
sendServiceAction("Stop", serviceName);
}
# Данная функция принимает действие и отправляет соответствующий POST-запрос, для его обработки другой конечной точкой
function sendServiceAction(action, serviceName) {
var request = new XMLHttpRequest();
request.open("POST", "/api/service/" + serviceName, true);
# В заголовок запроса передаем статус с содержимым действия (Status: <Stop/Start>) и обновляем страницу (reload)
request.setRequestHeader("Status", action);
request.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
console.log("True");
location.reload();
}
};
request.send();
}
</script>
</body></html>
'
# Передаем сформированные данные и код ответа в функцию, для отправки ответва клиенту
Send-Response -Data $GetService -Code 200 -v2
}
Для сравнения интерфейса, приведу пример управления службами, используя простой Jenkins Pipeline. Из явных преимуществ, такой интерфейс универсален для обеих систем (Windows и Linux), логика преимущественно на PowerShell и Bash (в моем случае), а доступ настраивается централизованно через Ansible, где в свою очередь используя ssh и winrm. Такой доступ можно заменить на REST-запросы, при наличии подобного сервера на каждой удаленной машине (например, в виде установленной службы). Безусловно, это более современное и правильное решение, но не взаимозаменяемое, речь только про интерфейс взаимодействия, где мы можем в одном интерфейсе управлять сразу с несколькими машинами.

По аналогии со службами, обработал остановку и запуск процессов.

Из интересного на мой взгляд, написал простую функцию для поиска исполняемого файла в системе, который отвечает за запуск процесса конкретного приложения. Если такой процесс не получается найти, то мы получим в ответ код 400: Bad Request. Process <$ProcessName> could not be found. В таком случае, можно воспользоваться заголовком Path, который принимает путь до исполняемого файла.
function Find-Process {
param (
$ProcessName
)
$ProcessPath = (Get-ChildItem "C:\Program Files" | Where-Object Name -match $ProcessName).FullName
if ($null -eq $ProcessPath) {
$ProcessPath = (Get-ChildItem "C:\Program Files (x86)" | Where-Object Name -match $ProcessName).FullName
}
if ($null -eq $ProcessPath) {
$ProcessPath = (Get-ChildItem "$home\AppData\Roaming" | Where-Object Name -match $ProcessName).FullName
}
$ProcessNameExec = "$ProcessName"+".exe"
(Get-ChildItem $ProcessPath -Recurse | Where-Object Name -eq $ProcessNameExec).FullName
}
> Find-Process qbittorrent
C:\Program Files\qBittorrent\qbittorrent.exe
> Find-Process nmap
C:\Program Files (x86)\Nmap\nmap.exe
Find-Process telegram
C:\Users\lifailon\AppData\Roaming\Telegram Desktop\Telegram.exe
Для сбора метрик используется CIM (Common Information Model). Сам скрипт сервера, описание с примерами, как и набор функций опубликованы на GitHub.

Так как PowerShell Core является кросс-платформенным решением, class System.Net.HttpListener работает и в системе Linux, используя такую же логику и возможности сразу нескольких языков (например, Bash), можно управлять службами на платформе Windows через systemctl используя REST API.
Что важно, при возникновении ошибки, мне хотелось, что бы данный сервер только логировал ее, но при этом продолжал функционировать (фактически, перезапускался). Для это достаточно вынести слушателя с циклом в отдельную функцию и запускать ее внутри еще одного бесконечного цикла, где присутствует дополнительная обработка ошибок в блоках try-catch-finally.
Вот базовый пример, без лишнего кода с описанием:
# Заполняем переменны с номером порта и данными для авторизации
$port = 8443
$user = "rest"
$pass = "api"
# Формируем строку Base64 из данных логина и пароля
$cred = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("${user}:${pass}"))
# Функция для логирования запросов
function Get-Log {
### Debug (Get all Request, Headers and Response parameters):
# Используя содержимое запросов (Request), чтение передаваемых заголовков и ответа (Response), можно расширить возможности логирования для отладки процесса
# $context.Request | Out-Default
# foreach ($header in $context.Request.Headers) {
# Write-Host "$header = $($context.Request.Headers[$header])"
# }
# $context.Response | Out-Default
# Забираем содержимое из запроса: адрес клиента, наименование агента, метод и url конечной точки
$remote_host = $context.Request.RemoteEndPoint
$client_agent = $context.Request.UserAgent
$method = $context.Request.HttpMethod
$endpoint = $context.Request.RawUrl
$response_code = $context.Response.StatusCode
$date = Get-Date -Format "dd.MM.yyyy hh:mm:ss"
# Выводим в консоль или в файл
"$date $remote_host $client_agent => $method $endpoint => $response_code"
# "$date $remote_host $client_agent => $method $endpoint => $response_code" | Out-File $Log_Path -Encoding utf8 -Append
}
# Функция для ответа клиенту
function Send-Response {
param (
$Data,
[int]$Code
)
# Проверяем код ответа, если он равен 200 (успех), то конвертируем данные перед отправкой клиенту
if ($Code -eq 200) {
# Дополнительно можем проверить название агента на клиентской стороне, который может выступать в роли браузера или явно задан тип данных HTML
if (($context.Request.UserAgent -match "Chrome") -or ($context.Request.ContentType -match "html")) {
# Конвертируем полученные данные в HTML и указываем тип контента в ответе
$Data = $Data | ConvertTo-Html
$context.Response.ContentType = "text/html; charset=utf-8"
}
# Далее проверяем только тип контента из заголовка (если он задан явным образом), и конвертируем вывод в соответствующий тип данных
elseif ($context.Request.ContentType -match "xml") {
$Data = ($Data | ConvertTo-Xml).OuterXml
$context.Response.ContentType = "text/xml; charset=utf-8"
}
elseif ($context.Request.ContentType -match "csv") {
$Data = $Data | ConvertTo-Csv
$context.Response.ContentType = "text/csv; charset=utf-8"
}
# По умолчанию, конвертируем в JSON
else {
$Data = $Data | ConvertTo-Json
$context.Response.ContentType = "text/json; charset=utf-8"
}
}
# Указываем код статуса для ответа
$context.Response.StatusCode = $Code
# Преобразуем данные в массив байтов, используя кодировку UTF-8 (особенно важно, при передачи в формате HTML)
$buffer = [System.Text.Encoding]::UTF8.GetBytes($Data)
# Выполняем функцию логирования
Get-Log
# Забираем число количества байт буффера для записи в поток, который передается в параметр ответа. Это является важным условием, что все даныне были переданы и прочитаны на стороне клиента.
$context.Response.ContentLength64 = $buffer.Length
# Передаем массив байтов (наш буффер ответа с данными) в поток ответа, обязательно нужно передать параметры смешения (если бы нужно было начать запись с определенного места в массиве) и длинны буффера
$context.Response.OutputStream.Write($buffer, 0, $buffer.Length)
# Данный метод обновляет буфер вывода, убеждаясь, что все данные из буфера отправлены клиенту
$context.Response.OutputStream.Flush()
# Закрываем поток ответа
$context.Response.OutputStream.Close()
}
# Создаем сокет слушателя
Add-Type -AssemblyName System.Net.Http
$http = New-Object System.Net.HttpListener
# Указываем адрес слушателя (+ что бы слушать на всех интерфейсах) и порт
$http.Prefixes.Add("http://+:$port/")
# Указываем использование базового метода аутентификации
$http.AuthenticationSchemes = [System.Net.AuthenticationSchemes]::Basic
# Запускаем сокет (начинаем слушать запросы на указанном порту)
$http.Start()
# Обработчик try-finally нужен для закрытия сокета в случае его непредвиденного завершения
try {
# Отправляем в бесконечный цикл прослушивание входящих запросов, пока свойство IsListening объекта $http равно true
while ($http.IsListening) {
# Используем асинхронный режим, для ожидания новых запросов
$contextTask = $http.GetContextAsync()
# Синхронно ожидает завершения асинхронной задачи, чтобы дождаться завершения асинхронной операции, прежде чем продолжить выполнение кода
while (-not $contextTask.AsyncWaitHandle.WaitOne(200)) { }
# Получение результата асинхронной задачи
$context = $contextTask.GetAwaiter().GetResult()
# Проверяем полученные данные авторизации (в формате Base64) из заголовка запроса на соответветствие переменной $cred
$CredRequest = $context.Request.Headers["Authorization"]
# Write-Host $CredRequest
$CredRequest = $CredRequest -replace "Basic\s"
if ( $CredRequest -ne $cred ) {
# Если авторизационные данные не прошли проверку (неверно передан логин или пароль), передаем в функцию ответа параметры с текстом ошибки и кодом возравата 401
$Data = "Unauthorized (login or password is invalid)"
Send-Response -Data $Data -Code 401
}
else {
# Если авторизация прошла, проверяем метод и url конечной точки на соответветствие, что бы его обработать
if ($context.Request.HttpMethod -eq "GET" -and $context.Request.RawUrl -eq "/api/service") {
$GetService = Get-Service -ErrorAction Ignore
Send-Response -Data $GetService -Code 200
}
# Дальше по аналогии дополнительными условиями (elseif) добавляем обработку других конечных точек
elseif ($context.Request.HttpMethod -eq "GET" -and $context.Request.RawUrl -eq "/api/process") {
$GetService = Get-Process
Send-Response -Data $GetService -Code 200
}
# Если не одно из методов не прошел соответветствие, отправляем ответ с кодом 405
elseif ($context.Request.HttpMethod -ne "GET") {
$Data = "Method not allowed"
Send-Response -Data $Data -Code 405
}
# Если не одно из условий не подошло, отправляем ответ с кодом 404
else {
$Data = "Not found endpoint"
Send-Response -Data $Data -Code 404
}
}
}
}
finally {
# Освобождаем сокет
$http.Stop()
}

Итог. Взяв за основу такой скелет и доработав под себя логику, можно автоматизировать процессы конкретного приложения, у которого не предусмотрен внешний интерфейс для подобных задач. В свою очередь, на стороне клиента взаимодействовать с ним, используя любой удобный интерфейс с REST-клиентом.