Представляем официальный модуль power shell oktas

PowerShellGet is a compatibility module that allows use of PowerShellGet 2.x (and below) cmdlet syntax with PSResourceGet functionality by making a best effort mapping between the cmdlet interfaces of both versions of the module.

This script uses WMI to retrieve properties needed for a customer to register a device with Windows Autopilot. Note that it is normal for the resulting CSV file to not collect a Windows Product ID (PKID) value since this is not required to register a device. Only the serial number and hardware hash will be populated.

Время на прочтение

Всем привет! В продолжении публикации о возможностях PowerShell, упомянул, что выделю в отдельную статью использование библиотеки Selenium с упомянутым языком. Этот небольшой гайд также может подойти для понимания, что из себя представляет данная библиотека и общее представление, как с ней работать, т.к. не зависимо от выбранного вами языка, принцип работы с Selenium одинаковый. Сразу отвечу на вопрос для тех, кто не знает, что это за библиотека или кому она может понадобиться. Selenium в первую очередь инструмент для функционального тестирования, это когда нужно проделать ряд действий имитируя реального пользователя и убедиться, что функционал Web-приложения работает исправно или неисправно. Реже используется для автоматизации различных задач в браузере (вызов JavaScript функций по средствам нажатий кнопок, заполнение форм и т.п.), для которых не предусмотрено API, а в некоторых случаях может выступать более удобной и полноценной альтернативой. Работая системным администратором, мне было удобно автоматизировать действия в панелях управления различных сервисах, или собирать специфические метрики с отправкой в базу данных. Selenium даже может входить в список инструментов DevOps-инженера. На мой взгляд, инструмент очень интересный с точки зрения творческого подхода к решению различных задач.

Постараюсь разложить все по полочкам, от установки всех зависимостей до примера работы с данным инструментом, также продемонстрирую альтернативный инструмент, который использовал до знакомства с Selenium. Специально для статьи подготовил модуль, который позволяет общаться с бесплатной версией ChatGPT из консоли PowerShell, который вы можете установить, используя всего две команды, а так же покажу, как просто создать такой модуль. Хочу заметить, что осознанно выделяю данную статью в средний уровень сложности Habr, по итогам ознакомления, для людей базово знакомых с PowerShell (для этого у меня есть отдельная работа с заметками), порог вхождения будет минимален. На просторах интернета не так много информации об использовании Selenium с языком PowerShell, я же узнал о данном инструменте из этой статьи, к слову, на этом ресурсе много полезного подчеркнул для себя в процессе изучения PowerShell, но несколько важных нюансов для построения собственных модулей там не было раскрыто. Также ключевой проблемой являлась установка всех зависимостей актуальной версии для дальнейшего масштабирования решений с использованием данной библиотеки, стоит упомянуть модуль selenium-powershell на GitHub, который не поддерживается с 2019 года.

Что требуется для работы:

  • Браузер. Вариантом несколько: Internet Explorer, Google Chrome, Mozilla Firefox. Важным моментом для меня было использовать конкретный браузер, базирующийся на Chrome.

  • ChromeDriver. Данный драйвер нужен для возможности Selenium управлять браузером (драйвер может быть другой, в зависимости от выбранного вами браузера). Важным моментом (и изначальной проблемой) является соответствие версии драйвера и текущей версии установленного браузера. За несколько месяцев использования я заметил тенденцию, что версия Google Chrome всегда опережает Chrome Driver на порядок релизов, из-за этого использовать драйвер с текущей версией установленного вами браузера не получится, т.к. он обновляется автоматически в целях поддержания безопасности. В поисках возможности автоматизировать данный процесс, где для сначала нужно удалить браузер из системы (тут вариантом несколько, через WMI/CIM или запускать деинсталлятор в тихом режиме) а потом устанавливать конкретную версию. Удобнее всего управлять данным процессом через менеджер пакетов, например, NuGet, Chocolatey, WinGet или Scoop. Изначально я пошел по этому пути, но тут возникали проблемы, браузер мог самостоятельно обновиться несмотря на различные запреты, а иногда вовсе отсутствовала возможность установки подходящей версии, со временем натыкаясь на различные ошибки в работе, к тому же, этот подход не позволял обновлять сразу все драйверы и приходилось производить много ручных действий. Спустя время меня посетила идея использовать Chromium, после чего весь процесс развертывания получилось очень сильно упростить и никаких проблем уже не возникало, так как у браузера имеется портативная версия. Самое приятное, Chromium имеет открытый исходный код и разрабатывается сообществом, что позволяет использовать такое решение в организации на территории РФ по сей день.

  • WebDriver. Драйвер Selenium. Так как для работы с драйвером мы используем язык PowerShell, который в свою очередь базируется на платформе .NET, то нам нужна версия для C# (всего вариантов несколько, поддерживаются такие языки, как Python, Java, JavaScript и Ruby). Также присутствует библиотека WebDriver.Support, которая нужна для обработки реже используемых действий.

Процесс установки и обновления вышеперечисленного у меня получилось автоматизировать. Если кратко, то для быстрого развертывания всех зависимостей достаточно воспользоваться одной командой в терминале, которая в свою очередь считает из репозитория GitHub PowerShell скрипт и запустит его в вашей системе:

Invoke-Expression(New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/Lifailon/Deploy-Selenium/rsa/Deploy-Selenium-Drivers.ps1")

Данный скрипт установит по пути “домашняя директория пользователя/Документы/Selenium” портативную версию браузера Chromium, chromedriver.exe, WebDriver.dll и WebDriver.Support.dll актуальных версий на текущий момент, а при повторно запуске, обновит все файлы.

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

$path = "$home\Documents\Selenium\"
$log = "$path\ChromeDriver.log"
$ChromeDriver = "$path\ChromeDriver.exe"
$WebDriver = "$path\WebDriver.dll"
$SupportDriver = "$path\WebDriver.Support.dll"
$Chromium = (Get-ChildItem $path -Recurse | Where-Object Name -like chrome.exe).FullName
Add-Type -Path $WebDriver
Add-Type -Path $SupportDriver
try { $ChromeOptions = New-Object OpenQA.Selenium.Chrome.ChromeOptions # создаем объект с настройками запуска браузера $ChromeOptions.BinaryLocation = $Chromium # передаем путь до исполняемого файла, который отвечает за запуск браузера $ChromeOptions.AddArgument("start-maximized") # добавляем аргумент, который позволяет запустить браузер на весь экран $ChromeOptions.AcceptInsecureCertificates = $True # игнорировать предупреждение на сайтах с не валидным сертификатом #$ChromeOptions.AddArgument("headless") # скрывать окно браузера при запуске $ChromeDriverService = [OpenQA.Selenium.Chrome.ChromeDriverService]::CreateDefaultService($ChromeDriver) # создаем объект настроек службы драйвера $ChromeDriverService.HideCommandPromptWindow = $True # отключаем весь вывод логирования драйвера в консоль (этот вывод нельзя перенаправить) $ChromeDriverService.LogPath = $log # указать путь до файла с журналом $ChromeDriverService.EnableAppendLog = $True # не перезаписывать журнал при каждом новом запуске #$ChromeDriverService.EnableVerboseLogging = $True # кроме INFO и ошибок, записывать DEBUG сообщения $Selenium = New-Object OpenQA.Selenium.Chrome.ChromeDriver($ChromeDriverService, $ChromeOptions) # инициализируем запуск с указанными настройками
# Далее следует код для обработки действий в браузере
}
finally { $Selenium.Close() $Selenium.Quit()
}

Теперь определяемся, что мы хотим выполнить в браузере. Для примера, поставим задачу, где мы откроем онлайн калькулятор и посчитаем сумму двух чисел, а полученный результат выведем на экран консоли. Работа с Selenium всегда начинается с перехода по нужной нам ссылке (url), откуда будем начинать выполнять наши действия, для этого используем дочерний метод навигации – GoToUrl:

$Selenium.Navigate().GoToUrl("https://google.com")

Если в опциях запуска браузера мы не используем аргумент headless (достаточно его закомментировать), что удобно при отладке (мы можем наблюдать все выполняемые нами действия в окне браузера или наоборот), то в браузере мы увидим, как перейдем по указанной нами ссылке, в нашем случае поисковика Google. Далее нужно передать текст для поиска, например так: “calculator online”. Для этого нужно найти элемент, который отвечает за ввод текста.

:/>  BCDBoot сбой при копировании файлов загрузки Windows 10: методы решения

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

1. DevTools – это встроенный набор инструментов Web-разработчиков и тестировщиков. В своем браузере (для удобства в отдельном окне, например, Google Chrome) переходим по нужной нам ссылки и запускаем данный инструмент используя клавишу F12 (или правой кнопкой мыши на странице – Просмотреть код), где нам нужна первая вкладка – «Элементы» (еще иногда может быть полезна вкладки Сеть, что бы отслеживать образующиеся запросы во время взаимодействия с приложением). Универсальным способом будет использовать комбинацию Ctrl+Shift+C, далее используя курсор мыши наводим на нужный нам элемент, в нашем случае, поисковой строки:

Выбранный элемент поисковой строки Google.
Выбранный элемент поисковой строки Google.

Что нас тут может интересовать:

  • textarea – это элемент управления (он же Tag), который предоставляет пользователю возможность вводить несколько строк текста.

  • jsname=”yZiJbe” – это атрибут, который указывает на используемый JavaScript-код для данного элемента. В нашем случае может использоваться для поиска через метод CssSelector.

  • id=”#APjFqb” – это уникальный идентификатор элемента – селектор (selector), который можно скопировать правой кнопкой мыши, и использоваться для обращения к этому элементу из JavaScript, CSS, а также в нашем случае из PowerShell. Альтернативным вариантом является XPath, который также можно использовать для обращения к элементу.

  • class=”gLFyf” — атрибут, который определяет класс элемента, который можно использовать для поиска через ClassName.

  • name=”q” — атрибут, определяющий имя элемента, которое будет отправлено на сервер при отправке формы, его можно использовать для обращения к элементу по имени.

  • maxlength=”2048″ — атрибут, указывающий на максимальное количество символов, которые пользователь может ввести в данном поле.

  • rows=”1″ — атрибут, который определяет количество строк текстового поля ввода.

  • aria-label=”Найти” — лейбл или этикетка, который соответствует свойству объекта ComputedAccessibleLabel и может быть полезен при фильтрации элементов, используя метод FindElements.

  • role=”combobox” — роль элемента, соответствует свойству объекта ComputedAccessibleRole.

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

  • Проще всего обращаться к таких элементам по его Id:

$Selenium.FindElements([OpenQA.Selenium.By]::Id('APjFqb'))
  • Если скопировали XPath:

$Selenium.FindElements([OpenQA.Selenium.By]::XPath('//*[@id="APjFqb"]'))
  • Или по имени элемента:

$Selenium.FindElements([OpenQA.Selenium.By]::Name('q'))
  • В формате XPath, аналогичный запрос будет выглядеть следующим образом (используя соответствующий метод):

$Selenium.FindElements([OpenQA.Selenium.By]::XPath('//*[@name="q"]'))
  • Используя имя класса:

$Selenium.FindElements([OpenQA.Selenium.By]::ClassName('gLFyf'))
  • Используя jsname для CSS селектора:

$Selenium.FindElements([OpenQA.Selenium.By]::CssSelector('[jsname="yZiJbe"]'))

2. Второй вариант, если вы не хотите пользоваться браузером для поиска элементом (мне иногда так было удобнее), а также если у элемента нет уникальных данных (когда в DevTools мы можем увидеть только название тэга, такое будет в примере с ChatGPT ниже) для обращения к нему или они могут меняться, в таком случае можно использовать тот же метод FindElements через PowerShell и искать по имени тэга (TagName):

Пример поиска элементов по id и TagТ.
Пример поиска элементов по id и TagName.

При таком поиске мы можем получить несколько элементов (практически всегда их несколько), т.к. имя тэга не уникально и понадобится дополнительная фильтрация, тут нам могут помочь упомянутые выше свойства объектов ComputedAccessibleLabel и ComputedAccessibleRole, а также Text. Такой подход может занимать продолжительное время при большом количестве элементов. В нашем случае все просто, мы уже знаем нужный нам элемент, его роль combobox:

$Selenium.FindElements([OpenQA.Selenium.By]::TagName('textarea')) | Where-Object ComputedAccessibleRole -eq combobox

Еще мы знаем, что значение Lable не является пустым, и даже если бы мы этого не знали, элемента всего два, не сложно догадаться, который из них нам нужен (или просто предварительно проверить оба).

Есть множество популярных тегов, которые используются для работы с CSS и DOM (Document Object Model), и в нашем случае могут быть полезны для поиска элементов. Такие тэги и их описание не сложно найти в интернете, перечислю некоторые из них, которые использовал в данной статье:

  • textarea – используется для создания многострочного поля ввода текста;

  • input – используется для добавления различных элементов ввода, таких как текстовые поля, флажки, радиокнопки и т.п.;

  • div – используется для создания блочного контейнера на веб-странице, который позволяет группировать другие элементы;

  • span – также используется для группировки элементов, но в отличие от div обычно не создает перенос строки и отображается в строке;

  • button – используется для создания кнопок.

Как только мы нашли элемент, помещаем его в переменную, (в нашем случае $Search), чтобы взаимодействовать с ним на прямую. Для передачи текста, используем метод SendKeys:

$Search.SendKeys("calculator online")

Теперь, чтобы не повторять поиск элемента с кнопкой поиска, просто используем привычный многим в браузере метод Enter (мы уже выяснили, что данный элемент не поддерживает несколько строк: rows=1):

$Search.SendKeys([OpenQA.Selenium.Keys]::Enter)

На данном этапе мы выполнили переход на новый url, где получаем содержимое страницы с результатом поиска. В нашем случае, калькулятор уже находится на странице, специально для примера используем второй подход, где вначале находим все элементы с тэгом div, которые отвечают за нажатие, фильтруем вывод по роли (ComputedAccessibleRole) – Button и лейблу (ComputedAccessibleLabel, также можно использовать свойство Text) – 2. После чего производим два нажатия, используя метод Click(). Выглядит это так:

Start-Sleep 1
$div = $Selenium.FindElements([OpenQA.Selenium.By]::TagName("div"))
$2 = $div | Where-Object {($_.ComputedAccessibleRole -like "button") -and ($_.ComputedAccessibleLabel -like "2")}
$2.Click()
$2.Click()

Мы нашли цифру 2, и нажали на нее два раза. Далее находим элемент, отвечающий за сложение. У данного элемента свойство Lable имеет содержимое «сложение», которое отличается от Text, где содержимым является «+», после чего нажимаем на искомый элемент:

$plus = $div | Where-Object {($_.ComputedAccessibleRole -eq "button") -and ($_.Text -eq "+")}
$plus.Click()

К остальным элементам обратимся по jsname (которые мы предварительно находим через DevTools). Теперь, нам остается забрать полученный результат, чтобы вывести его в консоль. Для этого достаточно обратиться к нужному элементу и получить значение его свойства Text.

$3 = $Selenium.FindElement([OpenQA.Selenium.By]::CssSelector('[jsname="KN1kY"]'))
$3.Click()
$3.Click()
$sum = $Selenium.FindElement([OpenQA.Selenium.By]::CssSelector('[jsname="Pt8tGc"]'))
$sum.Click()
$result = $Selenium.FindElement([OpenQA.Selenium.By]::CssSelector('[jsname="VssY5c"]')).Text
Write-Host "Result: $result" -ForegroundColor Green

Обратите внимание, как быстро происходит обращение к элементам на прямую. При фильтрации имеющихся 1092 элементов (в примере) с тэгом div, для поиска одного элемента уходит порядка 3-4 секунд, что ощутимо дольше и нецелесообразно для использования в конечном скрипте:

Сравнение скорости при поиске элемента по тэгу и селектору. Получаем наш результат сложения.
Сравнение скорости при поиске элемента по тэгу и селектору. Получаем наш результат сложения.

Пример целиком опубликован на GitHub.

Для второго случая продемонстрирую пример с ChatGPT. Это альтернатива, т.к. для подобных сервисов (обобщаю в примере с другими модулями) присутствует API, но иногда невозможно получить такой токен бесплатно, а ввиду ограничений в нашей стране даже приобрести напрямую за деньги, что послужило для меня отправной точкой создания подобных модулей. Цель простая, общаться с ИИ используя только консоль PowerShell. Еще, это может оказаться полезным, если вы хотите внедрить такое решение в свой скрипт для генерации ответов (например, произвести парсинг динамических данных) или даже части кода (зависит от задач и потребностей, тенденция растет с каждым днем). И тут действительно все очень просто, нам нужен любой бесплатный сервис, который желательно не требует авторизации, после чего найти элемент, который отвечает за ввод текста и передать в него параметр $Text с нашим запросом (он и будет использоваться параметром в модуле для задаваемых нами вопросов), после чего дождаться ответа и получить результат. Вот пример для вышеупомянутого шаблона:

$Selenium.Navigate().GoToUrl("$url")
$Search = $Selenium.FindElements([OpenQA.Selenium.By]::TagName('textarea')) | Where-Object ComputedAccessibleRole -eq textbox
$Search.SendKeys("$Text")
$Search.SendKeys([OpenQA.Selenium.Keys]::Enter)
while ($true) { if ($($Selenium.FindElements([OpenQA.Selenium.By]::TagName('button')).Text) -eq "Clear") { $Result = $Selenium.FindElements([OpenQA.Selenium.By]::TagName('span')) return $($Result[-2].Text) break }
}

Важным моментом является получение конечного результата ответа на наш вопрос, т.к. ChatGPT не отвечает сразу, а постепенно генерирует текст на экране. Нам всегда надо за что-то зацепиться, для проверки, что ответ был получен полностью, только после этого прочитать его. В примере я использую содержимое свойства кнопки – Text, когда оно имеет значение Clear, можно производить новый запрос, соответственно ответ на предыдущий запрос уже был получен. Но это всегда индивидуально, в нашем случае можно было считать кол-во элементов c тэгом div, его значение вырастает на 7 после каждого полученного ответа. Пример работы с таким модулем выглядит так:

:/>  Выходной файл Windows cmd и как создать или сгенерировать текстовый файл в командной строке Windows
Общаемся с ChatGPT в консоли PowerShell.
Общаемся с ChatGPT в консоли PowerShell.

Если вы уже установили зависимости, то для установки модуля Get-GPT из репозитория GitHub можете воспользоваться следующей командой:

Invoke-RestMethod https://raw.githubusercontent.com/Lifailon/Selenium-Modules/rsa/Modules/Get-GPT/Get-GPT.psm1 | Out-File -FilePath "$(New-Item -Path "$($($Env:PSModulePath -split ";")[0])\Get-GPT" -ItemType Directory -Force)\Get-GPT.psm1" -Force

По тому же принципу можно написать модуль, который позволят производить запросы для перевода текста. К слову, я уже писал автоматизированный модуль для перевода текста в консоли с использованием бесплатного публичного API для Google Translate и эмулятора DeepLX, для меня это самый удобный способ, который я использую ежедневно. Планирую написать модуль для bash и отдельную статью, т.к. на мой взгляд такой простое решение может оказаться полезно и другим.

Приведу еще один пример, у меня была задача получать значения измерения скорости интернета от провайдера, используя публичные сервисы, такие как LibreSpeed, OpenSpeedTest и Ookla SpeedTest. Во всех случаях достаточно перейти на нужный url сервиса, нажать кнопку измерения, по завершению получить результат – метрики измерений, и заполнить ими объект System.Collections.Generic.List.

Измерение скорости интернета в консоли PowerShell.
Измерение скорости интернета в консоли PowerShell.

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

$Url = "https://www.speedtest.net/"
$Selenium.Navigate().GoToUrl($Url)
Start-Sleep 1
$UrlTemp = $Selenium.Url
$span = $Selenium.FindElements([OpenQA.Selenium.By]::TagName("span"))
$Button = $span | Where-Object Text -Match "GO"
$Button.Click()
while ($True) { if ($Selenium.Url -ne $UrlTemp) { $UrlResult = $Selenium.Url break }
}
$Cont = Invoke-RestMethod $UrlResult

В примере с SpeedTest, если из полученного url нам нужно получить данные всей страницы, то можем просто прочитать его содержимое, используя Invoke-RestMethod (аналог клиента Curl в Windows). Нужные нам значения можно получить по средствам парсинга HTML-страницы, в некоторых случаях можно попытаться вытащить кусок JSON, который PowerShell сразу преобразует в объект и его достаточно только отфильтровать или пересобрать, что очень сильно упрощает процесс:

$Data = ($Cont -split "window.OOKLA.")[3] -replace "(.+ = )|(;)" | ConvertFrom-Json
return $Data.result

Используя модуль Get-SpeedTest, можно прямо в консоли получать результаты измерений, которые в свою очередь возможно настроить на отправку в базу данных временных рядов, например, InfluxDB и отображать в виде графиков в Grafana:

Перевод метрик в МБ или ГБ можно средствами самой Grafana, в примере этого не было сделано.
Перевод метрик в МБ или ГБ можно средствами самой Grafana, в примере этого не было сделано.

Работу (Ookla-SpeedTest-API) с отправкой метрик измерений скорости интернета я делал еще в Internet Explorer c использованием COM (Component Object Model) интерфейса. Это устаревший вариант, который можно использовать как альтернативу Selenium. Принцип работы у него точно такой же, при этом никаких зависимостей не требуется и работает по сей день. Тем не менее, если станет интересно, я оставлял заметки на тему работы с IE, а также с InfluxDB. Планирую (как только подготовлю тестовый стенд), написать отдельную статью по работе с InfluxDB, используя PowerShell или Bash и настройки графиков в Grafana.

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

Preview of New Entra PowerShell Module Built on the Microsoft Graph

On June 27, 2024, Microsoft announced the public preview of the Microsoft Entra PowerShell module. The new module is intended to ease the migration of customers who still have scripts running cmdlets from the AzureAD and AzureAD Preview modules that Microsoft deprecated on March 30, 2024. Microsoft says that these modules will function until March 30, 2025, and will then be retired, so obviously time is running out.

Then again, time has been running out for these modules since Microsoft first announced their intention to move away from the Azure Graph to embrace the Microsoft Graph as the foundation for Azure AD (Entra ID) PowerShell in June 2020. I guess it takes some organizations a long time to migrate.

Microsoft says that the new module is “high quality.” Let’s hope that this is so because the Entra PowerShell module is built on top of the Microsoft Graph PowerShell SDK, which has had a sorry history of quality failures recently.

Представляем официальный модуль power shell oktas

Why Two Modules?

When I first heard about the Entra PowerShell module a few months ago, my initial reaction was that the existence of the module would confuse customers. We have spent the last few years convincing people about the wisdom of embracing the Microsoft Graph. Even if there have been a few bumps along the way, we’ve encouraged people to rewrite scripts and replace old AzureAD and MSOL cmdlets with Microsoft Graph PowerShell SDK cmdlets.

Even though aliasing allows commands using AzureAD cmdlets to run, the Entra module is not a silver bullet for migration. Not all commands run without intervention and because Graph authentication and permissions underpin the module, there’s a bunch of work to do to make sure that scripts run without a hitch.

Installing the Entra PowerShell Module

The Entra PowerShell module depends on many of the modules distributed with the Microsoft Graph PowerShell SDK, including the Microsoft.Graph.Authentication module which handles connections. Before attempting to install the Entra module, start a new PowerShell session to remove the possibility of conflicts between the Graph modules distributed with the Entra module. Then install the V1.0 and beta modules from the PowerShell gallery:

Install-Module Microsoft.Graph.Entra -AllowPrerelease -Repository PSGallery -Force
Install-Module Microsoft.Graph.Entra.Beta -AllowPrerelease -Repository PSGallery -Force

The Microsoft Graph PowerShell SDK split into V1.0 (production) and beta modules for its V2.0 release, and that’s why the Entra module uses the same structure. You only need to install the beta module if you intend to use beta cmdlets. The problem is that it’s difficult to predict when that need might arise, so it’s safer to install the two.

:/>  Подлый трюк. Microsoft переименовала шпионский сервис DiagTrack и снова запустила его у всех пользователей / Хабр

If the PowerShell installer complains that it can’t install a module because some cmdlets already exist on the system, add the AllowClobber parameter (which is what I had to do).

Connecting with the Entra PowerShell Module

To start an Entra PowerShell session, use the Connect-Entra cmdlet in the same way as the Connect-MgGraph cmdlet functions for the Microsoft Graph PowerShell SDK. As it turns out, Connect-Entra is an alias for Connect-MgGraph and exists purely to give the appearance that the new module has its own connect cmdlet:

Import-Module Microsoft.Graph.Entra
Connect-Entra -NoWelcome -Scopes User.Read.All

Immediately, the dependency on Graph APIs and permissions is obvious. In the FAQ for the announcement, Microsoft says that the Entra PowerShell module is not a “pre-consented app” or “preauthorized.” When someone runs the Connect-AzureAD cmdlet to create a session with the AzureAD module, the permissions associated with any roles held by their account are available. The situation is very different with the Graph.

Представляем официальный модуль power shell oktas

Running AzureAD Cmdlets

I don’t have any scripts based on the AzureAD module that are now in use. Everything has been converted to the Microsoft Graph PowerShell SDK. This meant that I had to look back at some old articles to find test cases to see how well the Entra PowerShell module copied with old code. Here are two examples:

Find all member accounts in a tenant:

Enable-EntraAzureADAlias
$Users = Get-AzureADUser -All $True -Filter "Usertype eq 'Member'"
Get-EntraUser: A positional parameter cannot be found that accepts argument 'True'.

Microsoft claims “over 98% compatibility” for the Entra module with AzureAD cmdlets. I guess I was unlucky to find a problem with the first cmdlet I tried! According to a Microsoft contact, the development team made a deliberate decision to correct what they see as bad patterns by making the All parameter a switch parameter instead of a boolean. This stance is perfectly justifiable, but it will cause some scripts to fail. In any case, the fix is easy: remove the $True.

$Users = Get-AzureADUser -All -Filter "Usertype eq 'Member'"
$Users = Get-AzureADUser -All -Filter "Usertype eq 'Member'"

Given its intended purpose, it’s unsurprising that the cmdlets contained in the Entra module match those found in the AzureAD and AzureADPrevIew modules. To see the list of the available commands run the Get-Command cmdlet. The list reveals the available aliases too.

Get-Command -Module Microsoft.Graph.Entra
$Manager = Get-AzureADUserManager -ObjectId $User.ObjectId
Invoke-MgGraphRequest: GET https://graph.microsoft.com/v1.0/users/7bfd3f83-be63-4a5a-bbf8-c821e2836920/manager?$select=* HTTP/2.0 404 Not Found Cache-Control: no-cache Vary: Accept-Encoding
Strict-Transport-Security: max-age=31536000 request-id: 6622507b-c060-42f2-938e-a0225e756a70 client-request-id: 95d52d30-1faa-4891-82b2-783bfdf1e6bc x-ms-ags-diagnostic:
{"ServerInfo":{"DataCenter":"North Europe","Slice":"E","Ring":"4","ScaleUnit":"000","RoleInstance":"DU6PEPF00010AAA"}} x-ms-resource-unit: 1 Date: Fri, 28 Jun 2024 12:53:43 GMT
Content-Type: application/json Content-Encoding: gzip {"error":{"code":"Request_ResourceNotFound","message":"Resource 'manager' does not exist or one of its queried reference-property objects are not
present.","innerError":{"date":"2024-06-28T12:53:43","request-id":"6622507b-c060-42f2-938e-a0225e756a70","client-request-id":"95d52d30-1faa-4891-82b2-783bfdf1e6bc"}}}

I could go on and hammer home the point that translating all but the simplest use of AzureAD cmdlets into Graph API requests is difficult. You can expect problems to happen and be required to intervene to fix problems. Then I ask myself if problems are expected, why not simply embrace the full Microsoft Graph PowerShell SDK and update scripts to use it? As code is updated, it can be checked and improved, maybe even to improve performance by taking advantage of how the Graph retrieves data.

It’s not horribly difficult to migrate a script from the AzureAD module to the Microsoft Graph PowerShell SDK, especially if you invest in GitHub Copilot and use it with Visual Studio Code.

Same Permissions Used as for the Microsoft Graph PowerShell SDK

The Microsoft Graph Command Line Tools app requests consent for the Entra PowerShell module
Figure 1: The Microsoft Graph Command Line Tools app requests consent for the Entra PowerShell module

Astute observers will notice that the Microsoft Graph Command Line Tools app is now verified. This is a recent change announced in message center post MC804763 (24 June 2024). Previously, consent requests showed an unverified app, and who wants to give consent for permissions to an app that might come from an unverified source? Quite why it took so long for Microsoft to put the app through their own app compliance program is a mystery.

Weighing Up the Entra PowerShell Module

The downside of migrating scripts using the Microsoft Graph PowerShell SDK is that the SDK has its own set of foibles that can catch the unwary developer. Pipelining is a prime example because the Graph SDK cmdlets don’t support this fundamental PowerShell concept. But the Entra PowerShell cmdlets do, meaning that something like this is possible:

Get-AzureADUserManager -ObjectId cad05ccf-a359-4ac7-89e0-1e33bf37579e | Get-AzureADUser

Microsoft also points out that the new module comes with “human-readable parameters,” which is an interesting way of saying that Graph API requests and SDK cmdlets sometimes enjoy a syntax only penetrable after deep study.

When it becomes generally available, the Entra PowerShell module will serve a highly specific role to help organizations that still have not migrated PowerShell scripts using cmdlets from the old AzureAD and AzureADPreview modules to a modern base. Microsoft says that the module is part of their “ongoing commitment and increased investment” in the Microsoft Graph PowerShell SDK. If so, I wonder why they did all the work to create a new module instead of fixing the problems in the SDK? Having one solid and dependable module seems like a better strategy than splitting development effort across the two.

I won’t use the Entra PowerShell module because I’ve invested the best part of four years to become acquainted with the Graph APIs. Microsoft can be applauded for introducing a tool to help a subset of customers, but I have a nagging feeling that the Entra PowerShell module will be a source of confusion. My advice remains that customers should embrace the Graph and use the Microsoft Graph PowerShell SDK. But read the Entra module documentation and make your own mind up!