Как настроить отправку вывода команды в Telegram?
$token = «*******» $chat_id = «********» $text = Search-ADAccount -LockedOut | Select Name,SamAccountName $URI = «https://api.telegram.org/bot» + $token + «/sendMessage?chat_id=» + $chat_id + «&text spoiler»>spoiler
Invoke-WebRequest : <"ok":false,"error_code":400,"description":"Bad Request: message text is empty">
At line:16 char:12
+ $Request = Invoke-WebRequest -URI ($URI)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
"ok":false,"error_code":400,"description":"Bad>
Пробовал записать заблокированных пользователей в csv файл и отправить его, но файл даже не заполняется. Использовал: Search-ADAccount -LockedOut | Select Name,SamAccountName | Export-Csv C:\temp\1.csv Get-content -Path C:\temp\1.csv
- Вопрос задан более года назад
- 155 просмотров
1 комментарий
Средний 1 комментарий
Как настроить отправку вывода команды в Telegram?
$token = «*******» $chat_id = «********» $text = Search-ADAccount -LockedOut | Select Name,SamAccountName $URI = «https://api.telegram.org/bot» + $token + «/sendMessage?chat_id=» + $chat_id + «&text spoiler»>spoiler
Invoke-WebRequest : <"ok":false,"error_code":400,"description":"Bad Request: message text is empty">
At line:16 char:12
+ $Request = Invoke-WebRequest -URI ($URI)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
"ok":false,"error_code":400,"description":"Bad>
Пробовал записать заблокированных пользователей в csv файл и отправить его, но файл даже не заполняется. Использовал: Search-ADAccount -LockedOut | Select Name,SamAccountName | Export-Csv C:\temp\1.csv Get-content -Path C:\temp\1.csv
- Вопрос задан более года назад
- 155 просмотров
1 комментарий
Средний 1 комментарий
Как отправить сообщение в Telegram из PowerShell
Уведомления, получаемые от систем мониторинга, стали неотъемлемой частью специалистов занимающихся сопровождением информационных систем. Однако, мир не стоит на месте и все чаще появляются новые способы оповещения. К традиционным средствам оповещения о состоянии информационных систем: электронная почта и SMS добавились новые средства доставки уведомлений, такие как мессенджеры. Наибольшей популярностью, для этой цели, у системных администраторов стал мессенджер Telegram. А в системах мониторинга появилась возможность настраивать интеграцию с мессенджерами, с целью оперативной рассылки уведомлений.
Но что делать если централизованной системы мониторинга нет, или в ней нет особой необходимости, а потребность получать уведомления в Telegram есть.
Ниже описан один из способов пошаговой настройки для отправки сообщений в Telegram используя PowerShell.
- используя свой аккаунт Telegram написать специальному служебному боту @BotFather команду «/Start» или любое сообщение — в ответ придет список доступных команд.
- отправить команду /newbot — и ввести уникальное название бота, а затем идентификатор, который обязательно должен заканчиваться символами «bot».
Допустим наш бот будет называться UserMan.ru и иметь идентификатор UserManRu_bot.
В ответ придет уникальный token для созданного бота, который будет использоваться при отправке сообщений.
Например боту UserManRu_bot был присвоен токен: 819090298:AAGjXP6QLPMkem9t2LflIHOAiE_Q58bA43g
2. Создать группу в Telegram и получить ее идентификатор
Для получения идентификатора группы необходимо создать новую группу в Telegram (или использовать имеющуюся группу) и добавить туда созданного бота UserManRu_bot. А для получения идентификатор учетной записи следует отправить боту UserManRu_bot сообщение /Start и любое тестовое сообщение.
Затем необходимо проверить историю обращений которые были к боту, используя запрос ниже:
https://api.telegram.org/bot/getUpdates
В случае с ботом UserManRu_bot это будет:
https://api.telegram.org/bot819090298:AAGjXP6QLPMkem9t2LflIHOAiE_Q58bA43g/getUpdates
В результате мы увидим уникальный идентификатор пользователя (наш идентификатор), он нам понадобится при отправке сообщений
А также уникальный идентификатор группы
<"ok":true,"result":[<"update_id":146972760, "message":<"message_id":2,"from":172489755,"is_bot":false,"first_name":"Test","last_name":"Test","username":"mynick","language_code":"en">,"chat":,"date":1553259454,"text":"Test">>,,"chat":-347310642,"title":"UserMan.ru","type":"group","all_members_are_administrators":true>,"date":1553257968,"group_chat_created":true>>]>
Из этого набора символов в формате JSON определяем ID пользователя, который отправил сообщение «Test»: 172489755
и Id группы в которую добавили бота: -347310642
- token — Уникальный идентификатор, который был получен при создании бота
- chat_id — уникальный идентификатор пользователя или группы. Он определяется запросом getUpdates
- text — текст сообщения, которое необходимо направить
https://api.telegram.org/bot/sendMessage?chat_id=&text=
Например, отправка сообщения Hello в группу с идентификатором -347310642 от имени бота UserManRu_bot будет выглядеть так:
https://api.telegram.org/bot819090298:AAGjXP6QLPMkem9t2LflIHOAiE_Q58bA43g/sendMessage?chat_id=-347310642&text=Hello
Вставляем этот запрос в адресную строку браузера и переходим по ссылке. Сообщение Hello должно прийти сообщение в группу.
4. Сформировать скрипт для отправки сообщения
Чтобы сформировать и выполнить Web запрос выше, используя PowerShell, потребуется небольшой скрипт. Пример скрипта показан ниже:
$token = "819090298:AAGjXP6QLPMkem9t2LflIHOAiE_Q58bA43g" $chat_id = "-347310642" $text = "Hello World!" $URI = "https://api.telegram.org/bot" + $token + "/sendMessage?chat_id=" + $chat_id + "&text quote">ID чата всегда начинается со знака минус, а ID пользователя всегда без знака минус
5. Использование Proxy для отправки запроса
Если вы используете в своей сети прокси сервер или ваш провайдер запрещает доступ к telegram, то можно добавить к PowerShell командлету Invoke-WebRequest параметр -Proxy с указанием прокси сервера и порта, с которых разрешен доступ к Telegram. В этом случае скрипт будет иметь вид:
$token = "819090298:AAGjXP6QLPMkem9t2LflIHOAiE_Q58bA43g" $chat_id = "-347310642" $text = "Hello World!" $URI = "https://api.telegram.org/bot" + $token + "/sendMessage?chat_id=" + $chat_id + "&text=" + $text $Request = Invoke-WebRequest -URI ($URI) -Proxy "http://134.19.218.94:3128"
Где, 134.19.218.94:3128 - это один из примеров открытого прокси сервера.
Microsoft Message Center в Telegram через PowerShell и Azure Automation
И плюс, и минус любой SaaS системы в том, что она управляется не нами, и мы никак (в большинстве случаев) не можем повлиять на цикл обновлений основного функционала и добавление новых фич. Однако эти обновления могут носить собой как информативный характер и не нести никаких серьезных изменений в функционал, так могут и быть критическими для инфраструктуры, что в свою очередь несет собой дополнительные риски для бизнеса, а стало быть, и для нашего спокойствия, как для IT инженеров все это дело поддерживающих. О том, как получать все необходимые сообщения об обновлениях в Microsoft 365 не устанавливая для этого никаких дополнительных приложений будет эта статья. Из всего что нам понадобится, это зарегестированное приложение для доступа в API в Azure Active Directory, Azure Automation, PowerShell и бот в Телеграм.
Написать скрипт, который будет раз в час стучаться в API M365, проверять там наличие новых сообщений и присылать их нам в Teams канал или в Telegram.
Информацию обо всех грядущих изменениях можно получить из двух основных источников:
- Microsoft 365 Roadmap
- Microsoft 365 Message Center
Чтобы получить информацию из первого варианта, никаких дополнительных учетных записей не требуется, т.к. инфа в открытом виде и имеется RSS feed, что подтолкнуло к написаю простенького скрипта для мониторинга RSS фида и направления его в Telegram канал. Найти этот канал вы можете по этой ссылке.
Стоит заметить, что информация доступная в Roadmap не является исчерпывающей и плюс к тому, обновления не накатываются на все тенанты разом, плюс эффект от обновления на тот или иной тенант может быть разным, что побудило Microsoft запустить Message Center который бы давал информацию об обновлениях конкретно вашего тенанта. Помимо всего прочего, там бывают и сообщения, имеющие общий характер.
Для простоты разработки, был выбран PowerShell, т. к. он нативно поддерживается Azure Automation чуть ли не с самого начала. Чтобы получить доступ к информации внутри тенанта не вводя логин и пароль, нужно зарегистрировать приложение в Azure Active Directory и дать приложению соответствующие права.
1. Заходим на сайт Azure и в строке поиска вводим Active Directory
2. В левом боковом меню в разделе Manage выбираем App registrations
3. Здесь нажимаем на кнопку New Registration и попадаем в меню создания нового приложения внутри Azure Active Directory
4. В поле Name необходимо ввести уникальное имя для приложения внутри тенанта, остальные же поля можно оставить как есть.
5. Нажимаем Register и попадаем в созданное приложение. Здесь присутствуют данные, которые понадобятся нам в дальнейшем, а именно:
- Application ID
- Directory ID
Их желательно заранее куда ни будь сохранить.
6. Для авторизации посредством приложения, нужно сгенерировать Client Secret, без которого токен не получить. Для этого в левом боковом меню выбираем Certificates & Secrets и нажимаем на New Client Secret. Как понятно из названия здесь так же присутствует возможность авторизации посредством сертификата, но в данном примере будет использоваться именно Client Secret.
7. В появившемся окне нужно добавить Description к создаваемому секрету и указать его срок жизни.
8. Value секрета лучше сохранить сразу, ибо после обновления или закрытия данного окна, значение секрета будет увидеть невозможно и придется создавать новый.
9. Далее необходимо предоставить приложению права читать Message Center. Для этого переходим в меню API Permissions
10. Здесь присутствуют стандартные права в Graph API User.Read, которые отвечают за доступ к информации об аккаунте авторизовавшегося юзера.
Давайте на этом пункте остановится чуточку подробнее.
В M365 есть два основных вида прав, это Delegated Permissions, которые могут быть использованы только при авторизации в приложении пользователем, и Application Permissions которые можно использовать без авторизации человеком. Это очень полезно, когда необходимо что-то автоматизировать, а у вас в тенанте настроен обязательный MFA для всех учетных записей, однако Application Permissions требуют подтверждение Global администратора. User.Read права доступные по умолчанию как раз первого типа, потому их можно сразу же удалить. Щелкаем на права и нажимаем Remove permission.
Теперь же нужно добавить права для чтения Message Center. Нажимаем Add Permission > Скролим вниз и находим Office 365 Management API
11. Выбираем Application Permissions > ServiceHealth.Read и нажимаем Add Permissions
12. Далее если есть роль Global Admin, нажимаем кнопку Grant admin consent, либо просим одобрить права того, у кого эта роль имеется
13. После получения подтверждения, напротив прав должна появится надпись Granted for
На этом история с регистрацией приложения закончена и можно перейти к скрипту.
Первое что необходимо написать, это функцию для авторизации в M365 API, назовем ее Get-APIToken. Функция должна принимать в себя три значения:
- Application ID
- Tenant ID (directory ID)
- App Secret (Client Secret)
Первые два параметра отображались выше в пункте 5 при создании приложения
Функция представляет собой Rest запрос с определенными параметрами на URL вида:
“https://login.microsoftonline.com/” + $TenantID + “/oauth2/v2.0/token”
В итоге функция будет выглядеть следующим образом
Function Get-ApiToken < [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [String]$AppId, [Parameter(Mandatory=$True)] [String]$AppSecret, [Parameter(Mandatory=$True)] [String]$TenantID ) $AuthUrl = "https://login.microsoftonline.com/$TenantID/oauth2/v2.0/token" $Scope = "https://manage.office.com/.default" $Body = @< client_id = $AppId client_secret = $AppSecret scope = $Scope grant_type = 'client_credentials' >$PostSplat = @ < ContentType = 'application/x-www-form-urlencoded' Method = 'POST' Body = $Body Uri = $AuthUrl >try < Invoke-RestMethod @PostSplat -ErrorAction Stop >catch < Write-Warning "$(Get-Date): Exception was caught: $($_.Exception.Message)" >>
Теперь можно получить Token и проверить что все прошлые действия были выполнены правильно.
try < $Token = Get-ApiToken -AppId $ClientId -AppSecret $ClientSecret -TenantID $TenantId -ErrorAction Stop Write-Output "$(Get-Date): Token successfully issued" >catch
В результате значение токена должно напоминать следующее содержание:
Для того, чтобы получить сообщения из Message Center понадобится две функции, Get-MCMessages и Get-ApiRequestResult
Начнем с функции Get-ApiRequestResult.
Она будет принимать URL запроса, метод и токен.
Из токена формируется header запроса и все это оформляется в Splat.
Function Get-ApiRequestResult < [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [String]$Url, [Parameter(Mandatory=$True)] [String]$Method, [Parameter(Mandatory=$True)] [String]$Token ) $Header = @< Authorization = "$($Token.token_type) $($Token.access_token)" >$PostSplat = @ < ContentType = 'application/json' Method = $Method Header = $Header Uri = $Url >try < Invoke-RestMethod @PostSplat -ErrorAction Stop >catch < $Ex = $_.Exception $ErrorResponse = $ex.Response.GetResponseStream() $Reader = New-Object System.IO.StreamReader($errorResponse) $Reader.BaseStream.Position = 0 $Reader.DiscardBufferedData() $ResponseBody = $Reader.ReadToEnd(); Write-Output "$(Get-Date): Response content:`n$responseBody" -f Red throw Write-Error "$(Get-Date): Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)" >>
Далее необходимо написать функцию для получения сообщений из Message Center.
Function Get-MCMessages < [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [string]$APIUrl, [Parameter(Mandatory=$True)] [string]$TenantId ) $ApiVersion = "v1.0" $MS_resource = "ServiceComms/Messages?&`$filter=MessageType%20eq%20'MessageCenter'" $Uri = "$APIUrl/$ApiVersion/$($TenantId)/$MS_resource" $Method = "GET" try < Get-ApiRequestResult -Url $Uri -Token $Token -Method $Method -ErrorAction Stop Write-Output "$(Get-Date): New messages successfully collected" >catch < $Ex = $_.Exception $ErrorResponse = $ex.Response.GetResponseStream() $Reader = New-Object System.IO.StreamReader($errorResponse) $Reader.BaseStream.Position = 0 $Reader.DiscardBufferedData() $ResponseBody = $Reader.ReadToEnd(); Write-Output "$(Get-Date): Response content:`n$responseBody" -f Red throw Write-Error "$(Get-Date): Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)" >>
Небольшое отступление
Зачем сам запрос делится на две функции? Все дело в том, что принцип запросов к различным API M365 выглядит по большей части одинаково, отличаются только ссылки куда следует запрос, а также ресурсы, которые идут после основной ссылки. Для того чтобы переиспользовать последние две функции, необходимо только заменить значения в переменной $MS_Resource и $URL в функции Get-MCMessages. Так например можно получить информацию о мобильниках заэнроленных в Intune, изменив имя функции на Get-IntuneManagedDevices, значение переменной MS_Resource на "deviceManagement/managedDevices", и URL на https://graph.microsoft.com, что существенно сэкономит время на будущих скриптах. Ну и соответственно у приложения должны быть права на чтение информации об устройствах
Данные из запроса приходят списком сообщений следующего вида:
Предполагается, что скрипт будет крутиться в Azure Automation, что накладывает определенные ограничения. Например, как-то нужно проверить новые сообщения на предмет «свежести» без сохранения всего этого в базу данных, плюс сообщения имеют свойство обновляться. В сообщении имеется параметр LastUpdatedTime, его мы и будем проверять.
Максимальная частота отработки Runbook в Azure Automation 1 раз в час, за этот период мы и будем проверять новые сообщения каждый час. Таким образом, нужны две временные метки: время, когда скрипт будет запущен и час назад от этого времени. Получаем переменные:
$CurrentTime = Get-Date $СontrolTime = ($CurrentTime).AddMinutes(-60)
Получаем список всех сообщений воспользовавшись написанной функцией Get-MCMessages.
$Messages = Get-MCmessages -APIUrl $APIUrl -TenantId $TenantId
Далее извлекаем из всего выше полученного только те сообщения, которые были обновлены за последний час
$NewMessages = $Messages.value | Where-Object
Необходимо проверить есть ли вообще за последний час новые сообщения
$NewMessagesCount = $NewMessages.id.count if ($NewMessagesCount -gt 0) < Write-Output "$(Get-Date): There are $NewMessagesCount new messages" >else
И таким образом, если новые сообщения все же присутствуют, тогда нужно обработать полученный массив данных. Для этого прибегаем к перебору массива новых сообщений.
if ($NewMessagesCount -gt 0) < foreach ($NewMessage in $NewMessages)< >>
Собираем нужные переменные. Для этого понадобится извлечь следующие данные для каждого нового сообщения
$MessagePreview = $NewMessage.Messages.MessageText $MessageID = $NewMessage.id $MessageTitle = $NewMessage.Title $MessageType = $NewMessage.actiontype $PublishedTime = Get-date $($NewMessage.Messages.publishedTime) $UpdatedTime = Get-Date $($NewMessage.LastUpdatedTime)
Данные в MessageText попадают в формате html, однако мы знаем, что Telegram умеет далеко не все тэги. По этой причине убираем все те тэги, которые были собраны из полученных сообщений, и оставляем только те, которые Telegram принимает. Для этого создаем функцию Remove-HtmlTags, которая принимала бы в себя html и удаляла все те тэги, которые не поддерживаются.
Для этого внутри функции создаем два одномерных массива и один двумерный. Таким образом у нас есть три категории тегов:
- Простые тэги - имеющие закрывающий тэг и которые как правило не бывают с дополнительными параметрами внутри. С ними можно использовать регулярные выражения
- Сложные тэги - не имеющие закрывающий тэг, но которые при этом могут вызвать ошибку форматирования.
- Тэги и просто символы - которые нужно заменить на что-то иное.
Далее уже в зависимости от того, из какой категории определенный тэг, его нужно определенным способом обработать. В итоге получается следующая функция:
Function Remove-HtmlTags < param ( $Text ) $SimpleTags = @( 'p', 'i', 'span', 'div', 'ul', 'ol', 'h1', 'h2', 'h3', 'div' ) $TagsToRemove = ( "\]*\>", '\', '\&rarr', 'style=""', ' target\=\"_blank\"' ) $TagsToReplace = @( @('\[',''), @('\]',''), @('\',''), @('\]*\>','[There was an image]'), @(' ',' '), @('\',' -'), @('\',"`n") ) foreach($Tag in $SimpleTags) < $Pattern $Text = $Text -replace $Pattern >foreach($Tag in $TagsToRemove) < $Text = $Text -replace $Tag >foreach($Tag in $TagsToReplace) < $Text = $Text -replace $Tag >foreach($Tag in $SimpleTags) < $Pattern $Text = $Text -replace $Pattern >$Text >
Телеграм та же не поддерживает изображения прямо в тексте, не используя при этом телеграф, по этой причине ссылки на имейджи оставлять не имеет смысла, однако имеет смысл пользователя предупредить, о том, что где-то в тексте было изображение. Данный скрипт не несет исчерпывающей информации о каком-то сообщении, однако дает представление о том, стоит ли идти ради него на админский портал, или все же нет.
Скрипт в том виде что вы его видите стал таким не сразу, и претерпел за время определенные изменения. Например, было замечено, что Microsoft особо не заморачивается со стандартизацией и форматированием контента своих сообщений, по этой причине доверять отступам и переносам строки в тексте было бы крайне опрометчиво. Это вынудило меня разбить полученный текст сначала по разрывам строк используя закрывающий тэг
, а далее обрабатывать его уже как массив, после же все это объединить в одну переменную, но уже с вынужденным переносом строк без html тэгов. Так же нужно удалить лишние пробелы в тексте.
$MessageTextWithHtmlString = $MessagePreview -split ('\') $FormattedMesssageText = $(Remove-HtmlTags $MessageTextWithHtmlString) -creplace '(?m)^\s*\r?\n',''
Далее нужно собрать шапку сообщения. Для этого выделяем жирным Title и добавляем немного служебной информации о сообщении.
$PublishingInfo = "Published: $PublishedTime `nUpdated: $UpdatedTime" $TgmMessage = "$BoldMessageTitle `n$MessageDescription `n$PublishingInfo `n$FormattedMesssageText"
Microsoft иногда добавляет еще ссылки на документацию и блог, а так же дату, когда должны быть произведены действия со стороны админа. Этот момент тоже нужно проверить в каждом сообщении.
$MessageActionRequiredByDate = $NewMessage.ActionRequiredByDate $MessageAdditionalInformation = $NewMessage.ExternalLink $MessageBlogLink = $NewMessage.BlogLink if($MessageActionRequiredByDate)< $TgmMessage + >elseif ($MessageAdditionalInformation) < $TgmMessage + >elseif ($MessageBlogLink)
Теперь полученный текст нужно как то отправить в телеграм. Как регистрировать бота и создавать канал описывать здесь излишне, поэтому перейдем к написанию функции отправки сообщений.
Функция должна принять ChatID, Token, ParsingType, на случай если функцию нужно будет где-то использовать еще, ну и сам текст сообщения.
function Send-TelegramMessage < [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [ValidateSet("html","markdown")] [String]$ParsingType, [Parameter(Mandatory=$true)] [String]$MessageText, [Parameter(Mandatory=$true)] [String]$TokenTelegram, [Parameter(Mandatory=$true)] [String]$ChatID ) $URL_set = "https://api.telegram.org/bot$TokenTelegram/sendMessage" $Body = @< text = $MessageText parse_mode = $ParsingType chat_id = $chatID >$MessageJson = $body | ConvertTo-Json try < Invoke-RestMethod $URL_set -Method Post -ContentType 'application/json; charset=utf-8' -Body $MessageJson -ErrorAction Stop Write-Output "$(Get-Date): Message has been sent" >catch < Write-Error "$(Get-Date): Can't sent message" Write-Output "$(Get-Date): StatusCode:" $_.Exception.Response.StatusCode.value__ Write-Output "$(Get-Date): StatusDescription:" $_.Exception.Response.StatusDescription throw >>
И, собственно, сама отправка сообщения:
Send-TelegramMessage -MessageText $TgmMessage -TokenTelegram $TokenTelegram -ChatID $chatID -ParsingType 'html'
В итоге мы получаем сообщения вида:
Что касается такой служебной информации как токены, чат ID, секреты и прочее, то все это можно и нужно хранить в месте специально для этого предназначенном. В Azure Automation это Secure Assets. Более подробную информацию можно получить по ссылке.
P.S. буду рад контрибьюторам и предложениям по улучшению бота.
- PowerShell
- Microsoft Message Center
- microsoft 365