Microsoft Graph API gives you the ability to interact with the continually evolving Azure services through a single endpoint: https://graph.microsoft.com. We are going to connect to Graph with Powershell, OAuth 2.0 and REST.
Registering your App
To interact with Graph we need to register our app. Go to https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade to get started. Login with your account that is associated with your tenant. Add a new App and enter the following details:
- Name > just give it a name
- Application Type > Web-app/-API
- Sign on Url > Doesn’t matter so we go for: http://localhost:8000
Then select you newly created App and goto Required permissions, here we are going to add the Graph API permission. You will need to select different permission depending on what you want to access. You can change this later, so for now we click Add on the top, select Microsoft Graph and in step 2 we just select Read and write access to user profile.Â
The last step is to create a key, go to Keys and enter a Key Description and set the duration to Never Expires. When you click on Save the value (ClientSecret) will only be displayed once, so write it done when it’s displayed.
Become an ethical hacker that can hack computer systems like black hat hackers and secure them like security experts.
Authentication and Authorization
To access the Graph API we need to get an Access Code. If you are new to working with Rest and Graph API then there are a few things to keep in mind:
- To get an Access Token you need an Authorization Code. The code is only 1 hour valid, but as long as your refresh token is valid, you only need to renew this every 90 days.
- The Access Token, which is used in every request is only valid for 1 hour. You can renew it with the Refresh Token.
- The Refresh Token is valid for 14 days.
So the first step is to get the Authentication code. We use the script below for this. Run the scripts with :
GraphAPIGetAuthCode.ps1 -ClientId <clientId> -ClientSecret <clientSecret> -RedirectUrl <redirectUrl>
It will store the Authentication code in a text file at the script root location. This way we can easily access it later.
[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 }
Thanks to Darren Robinson:Â https://gist.github.com/darrenjrobinson/b74211f98c507c4acb3cdd81ce205b4f#file-ps2graphapi-ps1
Get the Access and Refresh Token
With the Authentication code we can get the all important Access token and the much needed refresh token. Because the Access token is only 1 hour valid, we need to check it’s age and request a new one with the refresh token if it’s expired.
The script below  stores the access and refresh token in text files in the script root location. You can always change it to store it just in variables, but keep in mind, when you close the shell it’s all gone and you need to start again with the Authorization code.
The scripts checks the last modified time of the accesstoken.txt, if it’s older than 1 hour, it will request a new access token with the refresh token. If that fails, it will request a new Authorization code and start from their again.
I use a config.json file to store my ClientId, ClientSecret and RedirectUrl, this way you can easily change it.
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 }
Getting actual date with Invoke-RestMethod from Graph
After all is done we can interact with Graph as followed:
Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} ` -Uri https://graph.microsoft.com/v1.0/me ` -Method Get
You can download the scripts here:Â https://github.com/ruudmens/SysAdminScripts/tree/master/Graph
Hi,
Did you try to run the script with this query?: GET https://graph.microsoft.com/v1.0/reports/getSharePointActivityFileCounts(period='D7‘)
I got an error about the period D7.
https://docs.microsoft.com/en-us/graph/api/reportroot-getsharepointactivityfilecounts?view=graph-rest-1.0
Basically, I am looking to get the info of viewers of a file in SharePoint Online.
https://compliance.microsoft.com/auditlogsearch and https://protection.office.com/unifiedauditlog don’t show the users who have viewed a file, only the number of visits.
Do you have an idea how to accomplish this?
Thanks,
Can you update this article the register app url changes to ‘https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade’ ??
Thanks for letting me know!
Hi
I use localhost:8000 as my app adress in azure,
Got error AADSTS90102: ‘redirect_uri’ value must be a valid absolute Uri.
Any ideas?
Try this comment on SO: https://stackoverflow.com/questions/46660603/aadsts90102-redirect-uri-value-must-be-a-valid-absolute-uri
I got the same error – It was unable to find the type [System.Web.HttpUtility]. I had to add the type – Add-Type -AssemblyName System.Web. No more above error.
Good blog you have here.. It’s difficult to find high-quality writing like yours nowadays.
I seriously appreciate people like you! Take care!!