Поиск по сайту:

Использование Microsoft Graph API с PowerShell


API Microsoft Graph предоставляет вам возможность взаимодействовать с постоянно развивающимися службами Azure через единую конечную точку: https://graph.microsoft.com. Мы собираемся подключиться к Graph с помощью Powershell, OAuth 2.0 и REST.

Регистрация вашего приложения

Чтобы взаимодействовать с Graph, нам нужно зарегистрировать наше приложение. Перейдите по адресу https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade, чтобы начать. Войдите с учетной записью, связанной с вашим арендатором. Добавьте новое приложение и введите следующие данные:

  • Имя > просто дайте ему имя
  • Тип приложения > Веб-приложение/-API
  • Подписка на Url > Не имеет значения, поэтому мы идем по адресу: http://localhost:8000

Затем выберите только что созданное приложение и перейдите к необходимым разрешениям, здесь мы собираемся добавить разрешение Graph API. Вам нужно будет выбрать разные разрешения в зависимости от того, к чему вы хотите получить доступ. Вы сможете поменять это позже, поэтому сейчас мы нажимаем 'Добавить' вверху, выбираем Microsoft Graph и на шаге 2 просто выбираем доступ на чтение и запись к профилю пользователя.

Последний шаг - создать ключ, перейти в раздел "Ключи", ввести описание ключа и установить срок действия "Никогда не истекает". Когда вы нажмете на "Сохранить", значение (ClientSecret) будет отображаться только один раз, поэтому запишите его, когда оно появится.

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

Аутентификация и авторизация

Чтобы получить доступ к Graph API, нам необходимо получить код доступа. Если вы новичок в работе с REST и Graph API, то есть несколько вещей, которые нужно учитывать:

  1. Чтобы получить токен доступа, вам нужен код авторизации. Код действителен только 1 час, но пока вашRefresh Token действителен, вам нужно обновлять его только каждые 90 дней.
  2. Токен доступа, который используется в каждом запросе, действителен только в течение 1 часа. Вы можете обновить его с помощью токена обновления.
  3. Токен обновления действителен в течение 14 дней.

Таким образом, первый шаг - получить код аутентификации. Мы используем для этого следующий скрипт. Запустите скрипты с:

GraphAPIGetAuthCode.ps1 -ClientId <clientId> -ClientSecret <clientSecret> -RedirectUrl <redirectUrl>

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

[CmdletBinding()]
PARAM(
	[parameter(ValueFromPipeline=$true,
				ValueFromPipelineByPropertyName=$true,
				Mandatory=$true)]
	[string]$ClientId,

	[parameter(ValueFromPipeline=$true,
				ValueFromPipelineByPropertyName=$true,
				Mandatory=$true)]
	[string]$ClientSecret,

	[parameter(ValueFromPipeline=$true,
				ValueFromPipelineByPropertyName=$true,
				Mandatory=$true)]
	[string]$RedirectUrl
)
BEGIN
{
	$ResourceUrl = "https://graph.microsoft.com"
}
PROCESS
{
	Function Get-AuthCode {
		Add-Type -AssemblyName System.Windows.Forms

		$form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width=440;Height=640}
		$web  = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{Width=420;Height=600;Url=($url -f ($Scope -join "%20")) }

		$DocComp  = {
			$Global:uri = $web.Url.AbsoluteUri        
			if ($Global:uri -match "error=[^&]*|code=[^&]*") {$form.Close() }
		}
		$web.ScriptErrorsSuppressed = $true
		$web.Add_DocumentCompleted($DocComp)
		$form.Controls.Add($web)
		$form.Add_Shown({$form.Activate()})
		$form.ShowDialog() | Out-Null

		$queryOutput = [System.Web.HttpUtility]::ParseQueryString($web.Url.Query)
		$output = @{}
		foreach($key in $queryOutput.Keys){
			$output["$key"] = $queryOutput[$key]
		}

		$output
	}

	# UrlEncode the ClientID and ClientSecret and URL's for special characters 
	$clientIDEncoded = [System.Web.HttpUtility]::UrlEncode($ClientId)
	$clientSecretEncoded = [System.Web.HttpUtility]::UrlEncode($ClientSecret)
	$redirectUrlEncoded =  [System.Web.HttpUtility]::UrlEncode($RedirectUrl)
	$resourceUrlEncoded = [System.Web.HttpUtility]::UrlEncode($ResourceUrl)
	$scopeEncoded = [System.Web.HttpUtility]::UrlEncode("https://outlook.office.com/user.readwrite.all")

	# Get AuthCode
	$url = "https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&redirect_uri=$redirectUrlEncoded&client_id=$clientID&resource=$resourceUrlEncoded&prompt=admin_consent&scope=$scopeEncoded"

	Get-AuthCode
	
	# Extract Access token from the returned URI
	$regex = '(?<=code=)(.*)(?=&)'
	$authCode = ($uri | Select-string -pattern $regex).Matches[0].Value

	# Store AuthCode
	Set-Content "$PSScriptRoot\AuthCode.txt" $authCode
}

Благодарности Дэррену Робинсону: https://gist.github.com/darrenjrobinson/b74211f98c507c4acb3cdd81ce205b4f#file-ps2graphapi-ps1

Получите токен доступа и обновления

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

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

Скрипт проверяет время последнего изменения файла accesstoken.txt. Если оно старше 1 часа, он запрашивает новый токен доступа с помощью токена обновления. Если это не удается, он запрашивает новый код авторизации и начинает с этого момента снова.

Я использую файл config.json для хранения моего ClientId, ClientSecret и RedirectUrl, таким образом вы можете легко их изменить.

Config.json

{
  "AppId": {
    "ClientId": "fa9d9d34-7a2a-****-****-************",
    "ClientSecret": "J/WDSKLjE+XC9G0+ASDHUen31/***********=",
    "RedirectUrl": "https://localhost:8000",
    "ResourceUrl": "https://graph.microsoft.com"
  }
}

ConnectTo-Graph

#----------------------------------------------------------[Declarations]----------------------------------------------------------

#Get the config file
$config = Get-Content $PSScriptRoot"\config.json" -Raw | ConvertFrom-Json

Add System Web Assembly to encode ClientSecret
Add-Type -AssemblyName System.Web

Encode ClientSecret
$clientSecretEncoded = [System.Web.HttpUtility]::UrlEncode($config.AppId.ClientSecret) 

Get the accessToken
If ((Test-Path -Path $PSScriptRoot"\accessToken.txt") -ne $false) {
	$accessToken = Get-Content $PSScriptRoot"\accessToken.txt"
}

#---------------------------------------------------------[Initialisations]--------------------------------------------------------

Check if the AccessToken is not older then 1 hour
If (($accessToken -eq $null) -or ((get-date) - (get-item $PSScriptRoot"\accessToken.txt").LastWriteTime).TotalHours -gt 1) {

	# Get the refreshToken
	$refreshToken = Get-Content $PSScriptRoot"\refreshToken.txt"

	$clientId = $config.AppId.ClientId
	$clientSecret = $config.AppId.clientSecret
	$redirectUrl = $config.AppId.RedirectUrl
	$resourceUrl = $config.AppId.ResourceUrl
	
	Try {
		$refreshBody = "grant_type=refresh_token&redirect_uri=$redirectUrl&client_id=$clientId&client_secret=$clientSecretEncoded&refresh_token=$refreshToken&resource=$resourceUrl"

		$Authorization = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token `
			-Method Post -ContentType "application/x-www-form-urlencoded" `
			-Body $refreshBody `
			-UseBasicParsing
	}
	Catch {
		$webResponse = $_.Exception.Response
	}

	If ($webResponse -ne $null) {
		# Get Authorization code
		GraphAPIGetAuthCode.ps1 -ClientId $clientId -ClientSecret $clientSecret -RedirectUrl $redirectUrl   

		$authCode = get-content $PSScriptRoot"\authCode.txt"
		$body = "grant_type=authorization_code&redirect_uri=$redirectUrl&client_id=$clientId&client_secret=$clientSecretEncoded&code=$authCode&resource=$resourceUrl"

		$Authorization = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token `
			-Method Post -ContentType "application/x-www-form-urlencoded" `
			-Body $body `
			-UseBasicParsing
	}

	# Store refreshToken
	Set-Content $PSScriptRoot"\refreshToken.txt" $Authorization.refresh_token

	# Store accessToken
	$accessToken = $Authorization.access_token
	Set-Content $PSScriptRoot"\accessToken.txt" $accessToken
} 

Получение актуальной даты с помощью Invoke-RestMethod из Graph

После того как все сделано, мы можем взаимодействовать с графом следующим образом:

Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} `
                  -Uri  https://graph.microsoft.com/v1.0/me `
                  -Method Get

Вы можете скачать скрипты здесь: https://github.com/ruudmens/SysAdminScripts/tree/master/Graph