PowerShell и Microsoft Graph API: автоматизация Microsoft 365

Что такое Microsoft Graph API

Microsoft Graph — это единая точка входа ко всем сервисам Microsoft 365: Azure AD, Exchange Online, SharePoint, Teams, OneDrive, Intune. На практике это означает, что вместо целого зоопарка модулей PowerShell — MSOnline, AzureAD, ExchangeOnlineManagement и других — достаточно одного универсального SDK. Меньше зависимостей, меньше головной боли.

Чем Microsoft Graph лучше старых модулей — и почему мы давно перешли на него:

  • Единый API — один модуль вместо пяти для всех сервисов M365
  • Активная разработка — MSOnline и AzureAD уже объявлены deprecated, Microsoft открыто говорит: пора уходить
  • Гранулярные разрешения — запрашиваете ровно те права, которые нужны, и ни байтом больше
  • REST API — при желании вызываете напрямую через Invoke-RestMethod, без лишних абстракций
  • Поддержка приложений — автоматизация работает без интерактивного входа, ночью, по расписанию, без человека

Первый шаг — установить модуль Microsoft Graph PowerShell SDK:

Install-Module Microsoft.Graph -Scope CurrentUser

# Или установите только нужные подмодули
Install-Module Microsoft.Graph.Users
Install-Module Microsoft.Graph.Groups
Install-Module Microsoft.Graph.Mail
Install-Module Microsoft.Graph.Teams

Аутентификация в Microsoft Graph

Graph API поддерживает два режима аутентификации. Делегированный — скрипт работает от имени конкретного пользователя. Серверный — от имени приложения, без участия человека. Для автоматизации нужен второй вариант.

Интерактивная аутентификация — подходит для ручных скриптов, когда за клавиатурой сидит живой человек:

Connect-MgGraph -Scopes "User.Read.All","Group.ReadWrite.All","Mail.Send"

# Проверка подключения
Get-MgContext | Format-List Account, TenantId, Scopes

Аутентификация приложения — для автоматизации, которая должна работать без участия пользователя:

  1. Зарегистрируйте приложение в Azure AD через App registrations
  2. Создайте Client Secret или, лучше, загрузите сертификат
  3. Назначьте именно Application permissions — не Delegated
  4. Подтвердите Admin consent — без этого шага ничего не заработает
$TenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$ClientSecret = ConvertTo-SecureString "секрет" -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($ClientId, $ClientSecret)

Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $Credential

Аутентификация по сертификату

Сертификат надёжнее Client Secret. Секрет может утечь в логи — сертификат нет. Мы в большинстве проектов используем именно сертификаты:

# Создание самоподписанного сертификата
$Cert = New-SelfSignedCertificate -Subject "CN=GraphApp" `
    -CertStoreLocation "Cert:\CurrentUser\My" `
    -KeyExportPolicy Exportable `
    -KeySpec Signature `
    -NotAfter (Get-Date).AddYears(2)

# Экспорт публичного ключа для загрузки в Azure AD
Export-Certificate -Cert $Cert -FilePath "C:\GraphApp.cer"

# Подключение по отпечатку сертификата
Connect-MgGraph -TenantId $TenantId `
    -ClientId $ClientId `
    -CertificateThumbprint $Cert.Thumbprint

Файл GraphApp.cer загрузите в Azure AD → App registrations → Certificates & secrets → Upload certificate.

Управление пользователями

Базовые операции с пользователями Azure AD через Graph — то, с чего начинается большинство проектов автоматизации:

# Получить всех пользователей
$Users = Get-MgUser -All -Property DisplayName, Mail, UserPrincipalName, AccountEnabled
$Users | Format-Table DisplayName, Mail, AccountEnabled

# Найти пользователя
$User = Get-MgUser -Filter "userPrincipalName eq 'ivanov@company.ru'"

# Создать нового пользователя
$PasswordProfile = @{
    Password = "TempP@ss2024!"
    ForceChangePasswordNextSignIn = $true
}

New-MgUser -DisplayName "Петров Иван" `
    -UserPrincipalName "petrov@company.ru" `
    -MailNickname "petrov" `
    -PasswordProfile $PasswordProfile `
    -AccountEnabled:$true `
    -UsageLocation "RU" `
    -Department "IT" `
    -JobTitle "Системный администратор"

Массовое обновление пользователей из CSV — удобно, когда нужно разом поправить атрибуты для сотни сотрудников:

Import-Csv "C:\users-update.csv" -Encoding UTF8 | ForEach-Object {
    Update-MgUser -UserId $_.UPN `
        -Department $_.Department `
        -JobTitle $_.JobTitle `
        -OfficeLocation $_.Office
    Write-Host "Обновлён: $($_.UPN)"
}

Управление группами и членством

Группы безопасности и Microsoft 365 — ещё одна частая задача:

# Создание группы безопасности
New-MgGroup -DisplayName "IT-Admins" `
    -MailEnabled:$false `
    -MailNickname "it-admins" `
    -SecurityEnabled:$true `
    -Description "Группа ИТ-администраторов"

# Создание группы Microsoft 365 (с почтой и SharePoint)
New-MgGroup -DisplayName "Проект Альфа" `
    -MailEnabled:$true `
    -MailNickname "project-alpha" `
    -SecurityEnabled:$false `
    -GroupTypes @("Unified") `
    -Description "Рабочая группа проекта"

# Добавление участников
$GroupId = (Get-MgGroup -Filter "displayName eq 'IT-Admins'").Id
$UserId = (Get-MgUser -Filter "userPrincipalName eq 'ivanov@company.ru'").Id

New-MgGroupMember -GroupId $GroupId `
    -DirectoryObjectId $UserId

Аудит членства в группах — клиенты периодически просят выгрузить, кто вообще куда входит:

# Все участники группы с должностями
Get-MgGroupMember -GroupId $GroupId -All | ForEach-Object {
    $User = Get-MgUser -UserId $_.Id -Property DisplayName, JobTitle, Department
    [PSCustomObject]@{
        Name       = $User.DisplayName
        Title      = $User.JobTitle
        Department = $User.Department
    }
} | Format-Table

Работа с почтой Exchange Online

Для базовых операций с почтой Microsoft Graph вполне заменяет ExchangeOnlineManagement. Не для всего, но для большинства типовых задач — точно:

# Отправка письма от имени пользователя (delegated permission)
$Message = @{
    Subject = "Отчёт за неделю"
    Body = @{
        ContentType = "HTML"
        Content = "<h2>Еженедельный отчёт</h2><p>Все системы работают штатно.</p>"
    }
    ToRecipients = @(
        @{ EmailAddress = @{ Address = "director@company.ru" } }
    )
}

Send-MgUserMail -UserId "admin@company.ru" -Message $Message

# Чтение почтового ящика
$Messages = Get-MgUserMessage -UserId "admin@company.ru" -Top 10 `
    -OrderBy "receivedDateTime desc" `
    -Property Subject, From, ReceivedDateTime

$Messages | Format-Table Subject, @{N='From';E={$_.From.EmailAddress.Address}}, ReceivedDateTime

В серверных сценариях с разрешением Mail.Send на уровне приложения используйте параметр -UserId — это позволяет отправлять письма от имени любого ящика в тенанте без интерактивного входа.

Создание правил почтового ящика

Автоматизация создания почтовых правил для новых сотрудников — избавляет от ручной работы при онбординге:

# Создание правила перенаправления
$Rule = @{
    DisplayName = "Пересылка уведомлений"
    Sequence = 1
    IsEnabled = $true
    Conditions = @{
        SubjectContains = @("[ALERT]", "[URGENT]")
    }
    Actions = @{
        ForwardTo = @(
            @{ EmailAddress = @{ Address = "sms-gateway@company.ru" } }
        )
    }
}

New-MgUserMailFolderMessageRule -UserId "admin@company.ru" `
    -MailFolderId "inbox" -BodyParameter $Rule

Управление лицензиями

Назначение и отзыв лицензий M365 через скрипт — то, что раньше делалось руками в портале:

# Получить доступные лицензии (SKU)
Get-MgSubscribedSku | Format-Table SkuPartNumber, `
    @{N='Total';E={$_.PrepaidUnits.Enabled}}, `
    @{N='Used';E={$_.ConsumedUnits}}, `
    @{N='Free';E={$_.PrepaidUnits.Enabled - $_.ConsumedUnits}}

# Назначить лицензию пользователю
$SkuId = (Get-MgSubscribedSku | Where-Object SkuPartNumber -eq "O365_BUSINESS_PREMIUM").SkuId

Set-MgUserLicense -UserId "petrov@company.ru" `
    -AddLicenses @(@{SkuId = $SkuId}) `
    -RemoveLicenses @()

# Массовое назначение из группы AD
$GroupMembers = Get-MgGroupMember -GroupId $GroupId -All
foreach ($Member in $GroupMembers) {
    $CurrentLicenses = (Get-MgUserLicenseDetail -UserId $Member.Id).SkuId
    if ($SkuId -notin $CurrentLicenses) {
        Set-MgUserLicense -UserId $Member.Id `
            -AddLicenses @(@{SkuId = $SkuId}) `
            -RemoveLicenses @()
        Write-Host "Лицензия назначена: $($Member.Id)"
    }
}

Если лицензии нужно назначать группам, удобнее настроить Group-based licensing прямо в портале Azure — там это делается через Azure AD группы и работает автоматически.

Отчёты и аналитика

Graph API отдаёт подробную статистику по использованию M365 — Teams, почта, SharePoint, OneDrive:

# Отчёт о входах пользователей (требует AuditLog.Read.All)
$SignIns = Get-MgAuditLogSignIn -Top 100 -Filter "status/errorCode ne 0" `
    -OrderBy "createdDateTime desc"

$SignIns | Select-Object UserDisplayName, AppDisplayName, `
    @{N='Error';E={$_.Status.FailureReason}}, `
    CreatedDateTime, IPAddress | Format-Table

# Неактивные пользователи (не входили более 90 дней)
$Threshold = (Get-Date).AddDays(-90).ToString("yyyy-MM-ddTHH:mm:ssZ")
$Inactive = Get-MgUser -All -Property DisplayName, UserPrincipalName, SignInActivity | 
    Where-Object {
        $_.SignInActivity.LastSignInDateTime -lt $Threshold -or 
        $null -eq $_.SignInActivity.LastSignInDateTime
    }

$Inactive | Select-Object DisplayName, UserPrincipalName, `
    @{N='LastSignIn';E={$_.SignInActivity.LastSignInDateTime}} | 
    Export-Csv "C:\inactive-users.csv" -NoTypeInformation -Encoding UTF8

На практике эти отчёты окупаются быстро. Мы регулярно помогаем клиентам выявить пользователей, которые не заходили в M365 по 60–90 дней, отозвать лицензии — и сэкономить 20–30% бюджета на подписке.

Часто задаваемые вопросы

Модуль AzureAD (и MSOnline) работает поверх устаревшего Azure AD Graph API, который Microsoft закрывает. Microsoft Graph PowerShell SDK — официальная замена. Он покрывает не только Azure AD, но и Exchange, Teams, SharePoint, Intune. Если у вас ещё живут скрипты на AzureAD — самое время мигрировать на Microsoft.Graph, пока это не стало срочной проблемой.

Для делегированного доступа от имени пользователя достаточно разрешения Mail.Send. Для серверного — Mail.Send как Application permission плюс Admin consent. Важный момент: серверное разрешение даёт приложению право отправлять от имени любого пользователя в тенанте. Если такая широта вас пугает — ограничьте через Application Access Policy, оставив доступ только к нужным ящикам.

Microsoft Graph применяет throttling — при слишком частых запросах вернётся HTTP 429 Too Many Requests. PowerShell SDK обрабатывает это автоматически, но знать об этом всё равно нужно. Для массовых операций используйте batch-запросы через Invoke-MgGraphRequest с JSON batch — до 20 запросов за один вызов, это ощутимо ускоряет работу со списками.

Да, Graph API — чистый REST, и никто не запрещает обращаться к нему напрямую через Invoke-RestMethod. Получаете токен через MSAL или client credentials и шлёте HTTP-запросы: Invoke-RestMethod -Uri 'https://graph.microsoft.com/v1.0/users' -Headers @{Authorization = "Bearer $token"}. Для простых сценариев, где тащить весь SDK нет смысла, это вполне рабочий подход.

Нужна помощь с настройкой?

Специалисты АйТи Фреш помогут с внедрением и настройкой — 15+ лет опыта, обслуживание от 15 000 ₽/мес

📞 Связаться с нами
#PowerShell Graph API#Microsoft Graph#автоматизация Microsoft 365#Microsoft Graph PowerShell#управление Azure AD#PowerShell Teams#автоматизация Exchange Online#Graph API примеры
Комментарии 0

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

загрузка...