Повышение производительности Microsoft Graph PowerShell с помощью пакетной обработки


Пакетную обработку (также известную как пакетная обработка JSON) можно использовать с Microsoft Graph PowerShell для повышения производительности существующих сценариев, которым необходимо взаимодействовать со службами в Microsoft 365, Microsoft Entra или Microsoft Intune.

Концепция пакетной обработки довольно проста. Вы собираете информацию для выполнения нескольких запросов, а затем отправляете их «пакетом» в API Microsoft Graph. Это позволяет вам делать много запросов одновременно, тем самым увеличивая общее время работы ваших скриптов.

Что такое пакетирование запросов?

Пакетная обработка запросов позволяет приложениям объединять несколько запросов к Microsoft Graph для повышения производительности и сокращения задержек. Его также можно использовать в Microsoft Graph PowerShell, чтобы помочь администраторам повысить производительность задач управления и сценариев.

Пакет запросов — это набор запросов внутри одного объекта JSON, который в PowerShell может храниться в переменной. Объект JSON можно создать несколькими способами, в том числе:

  • Определив его вручную между здесь-строками.
  • Определив хеш-таблицу и преобразовав ее в JSON.
  • Путем создания объекта с помощью кода и преобразования его в JSON (наиболее эффективно).

Затем пакет можно отправить в Microsoft Graph с помощью командлетов Invoke-RestMethod или Invoke-MgGraphRequest с методом POST.

Когда следует использовать пакетные запросы?

Использование механизма пакетной обработки запросов в Microsoft Graph усложняет ваши сценарии. Таким образом, хотя это может улучшить производительность вашего кода и «время вывода», оно обычно делает ваш код менее читабельным для других, которым может потребоваться его прочитать или унаследовать. Поэтому я рекомендую группировать запросы только в том случае, если вам необходимо отправить много запросов к Microsoft Graph (более 20).

Пример того, где это может принести вам пользу: вы удаляете множество пользователей или устройств из своей среды. Хотя для получения списка объектов с помощью Microsoft Graph легко использовать фильтрацию, для их удаления потребуются отдельные запросы на удаление. Предположим, вам нужно удалить 100 объектов из каталога вместо того, чтобы отправлять 100 отдельных запросов в Microsoft Graph и ждать выполнения каждого из них, прежде чем начнется следующий. В этом случае их можно объединить в группы по 20 штук, поэтому вам нужно будет сделать всего 5 запросов к Microsoft Graph.

Преимущества производительности пакетной обработки в PowerShell

Я провел небольшое практическое тестирование использования пакетной обработки и повышения производительности. Для этого я создал 2000 тестовых пользователей в Microsoft Entra (эти пользователи не лицензированы и не подготовлены). 1000 пользователей я сохранил в переменной $1to1000, а остальные 1000 - в переменной $1001to2000.

Это первый приведенный ниже сценарий, который был запущен для удаления 1000 пользователей. Этот скрипт отправил индивидуальный запрос на удаление каждого пользователя.

Foreach ($user in $1to1000){
        Remove-MgUser -UserId $user.id
}

При использовании командлета Measure-Command общее время завершения удаления пользователей составило 33 секунды.

Затем я запустил следующий скрипт, который группирует запросы в группы по 20, а затем отправляет их как один запрос.

for($i=0;$i -lt $1001to2000.count;$i+=20){
    $batch = @{}
    $batch['requests'] = ($1001to2000[$i..($i+19)] | select @{n='id';e={$_.id}},@{n='method';e={'DELETE'}},`
    @{n='url';e={"/users/$($_.id)"}})
    Invoke-mggraphrequest -Method POST -URI "https://graph.microsoft.com/v1.0/`$batch" -body ($batch | convertto-json) -OutputType PSObject
}

Вы можете видеть, что общее время завершения составляет 12 секунд, что является значительным улучшением.

Пример запроса пакетной обработки

Ниже приведен пример пакетного запроса JSON для Microsoft Graph. Вы можете видеть, что тело запроса окружено строками (@’ ‘@).

Строки здесь можно указывать с помощью одинарных кавычек (как показано ниже) или вместо них использовать двойные кавычки. Разница в том, что одинарная кавычка воспринимает текст буквально и не расширяет переменные. Однако двойная кавычка позволит использовать расширяемые переменные.

Затем командлет Invoke-MgGraphRequest используется с методом POST. Я также использовал обратную галочку ( ` ) в веб-пути URI. Это гарантирует, что строка $batch будет восприниматься буквально и не будет расширена как переменная, необходимая для пакетного запроса.

Переменная $batch, содержащая тело запроса, затем используется с переменной -body для отправки запроса.

$batch = @'
{
  "requests": [
    {
      "id": "1",
      "method": "GET",
      "url": "/users/[email "
    },
    {
      "id": "2",
      "method": "GET",
      "url": "/users/[email "
    },
    {
      "id": "3",
      "method": "GET",
      "url": "/users/[email "
    }
  ]
}
'@

$response = Invoke-MgGraphRequest `
-Method POST `
-URI "https://graph.microsoft.com/beta/`$batch" `
-body $batch `
-OutputType PSObject

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

$response.responses.body | Select UserPrincipalName, proxyaddresses | ft

Генерация пакетных запросов

В моем предыдущем примере я продемонстрировал преимущества производительности при использовании пакетных запросов. При этом я использовал цикл PowerShell For для создания пакета JSON для каждого запроса, при этом, что важно, каждый запрос повторялся для другого объекта того же типа. Это позволяет эффективно использовать пакетную обработку с помощью PowerShell, иначе это было бы непрактично. Вот код шаблона:

for($i=0;$i -lt $objects.count;$i+=20){
    $batch = @{}
    $batch['requests'] = ($objects[$i..($i+19)] | select @{n='id';e={$_.id}},@{n='method';e={'DELETE'}},`
	@{n='url';e={"/users/$($_.id)"}})
    invoke-mggraphrequest -Method POST -URI "https://graph.microsoft.com/v1.0/`$batch" -body ($batch | convertto-json) -OutputType PSObject
}

Чтобы раскрыть логику этого, цикл For (или оператор For) имеет в своем синтаксисе три ключевых фрагмента информации, которые я выделил на изображении ниже в первой строке.

$i=0. Эта команда будет выполнена до начала цикла. Он используется для создания и инициализации переменной с начальным значением 0.

$i -lt $obects.count. Этот оператор является условием (или правилом) и требует продолжать выполнение этого цикла, пока исходная переменная $i меньше общего количества объектов в коллекции (или массиве).

$i+=20. Это заявление о возврате. Это будет выполняться в каждом цикле, и к значению $i будет добавлено 20. Итак, в первом цикле $i=0, затем в следующем цикле $i=20, затем 40 и так далее.

Это позволяет коду выполняться в цикле, добавляя 20 к $i в каждом цикле, что важно на следующем шаге.

Ниже выделен массив $objects, а затем в квадратных скобках указан селектор диапазона. При первом проходе цикла $i=0 и $i+19=19 будут выбраны первые 20 объектов массива, которые будут включены в первый пакетный запрос.

Затем оператор return добавит 20 к $i, готовому ко второму циклу; во втором цикле $i=20 и $i+19=39 выбираются следующие 20 элементов из массива и добавляются в следующий пакетный запрос. Это будет продолжаться до тех пор, пока $i не станет меньше общего количества объектов в массиве. В результате все объекты в массиве будут отправлены в Microsoft Graph в рамках пакетного запроса.

Подведение итогов

Хотя пакетные запросы могут значительно повысить производительность ваших сценариев, используйте их с умом, поскольку дополнительная сложность может стать проблемой при совместной работе над сценариями, особенно если общее влияние на производительность минимально.

Существует также множество различных способов создания пакетных запросов. Выше я продемонстрировал, как программно генерировать запросы с помощью оператора For в PowerShell. Это работает для повторяющихся типов запросов. Однако альтернативным методом может быть сохранение информации запроса вне вашего скрипта (например, в файле CSV), а затем импортирование ее с помощью кода.