Permissions to access to MS Graph API via PowerShell - powershell

Question
I am trying to write a PowerShell script to get report data via the MS Graph API /reports/credentialUserRegistrationDetails.
When I use Graph Explorer it works just fine, as long as I enable Reports.Read.All on the Modify permissions (Preview) tab.
But, when I try to do it with my script, I just get the error "Calling principal does not have required MSGraph permissions Reports.Read.All"
In all my searches, I can only find how to assign permissions to apps.
Is there some way to make it so I can do it from my script?
My Script
$azContext = Get-AzContext
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
$azContext.Account,
$azContext.Environment,
$azContext.Tenant.Id,
$null,
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never,
$null,
"https://graph.microsoft.com"
)
$params = #{
Method = "GET"
Uri = "https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails"
Headers = #{
Authorization = "Bearer $($token.AccessToken)"
"Content-Type" = "application/json"
}
}
Invoke-RestMethod #params
Response
{
"error": {
"code":"Authentication_MSGraphPermissionMissing",
"message":"Calling principal does not have required MSGraph permissions Reports.Read.All",
"innerError": {
"date":"2021-10-19T01:18:36",
"request-id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client-request-id":"6b8cc3a3-b93b-44bb-b1d4-190c618aa52a"
}
}
}

When I use Graph Explorer it works just fine, as long as I enable
Reports.Read.All on the Modify permissions (Preview) tab.
Its because Microsoft Graph Explorer is a Enterprise Application of Microsoft which is present on every Azure AD tenant just you need to sign in and use it by providing the required permissions.
But when you are writing running your Powershell script it uses Microsoft Azure Powershell . You can verify it by checking the access_token received in JWT Token.So, you need to provide the Reports.Read.All API Permission to the same app in your tenant with appid : 1950a258-227b-4e31-a9cf-717495945fc2 in Enterprise Application >> Permissions and grant the admin consent .After providing the required permissions only it will work.
Another Way will be to create a App registration ,Create a client secret for it and then provide the Reports.Read.All API permission and use the below script:
$TenantName = "tenantname.onmicrosoft.com"
$clientID = "d344e3xxx-xxx-xxxx-xxxx-9c861d363244" # app registration clientId
$clientSecret = "fNc7Q~UNHBgv_xxxxxxxxxxxxxxxxxxxxxx-PD"
$Scope = "https://graph.microsoft.com/.default"
$Body = #{
Grant_Type = "client_credentials"
Scope = $Scope
client_Id = $clientID
Client_Secret = $clientSecret
}
$authUri = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"
$TokenResponse = Invoke-RestMethod -Uri $authUri -Method POST -Body $Body
$Headers = #{
"Authorization" = "Bearer $($TokenResponse.access_token)"
"Content-type" = "application/json"
}
$apiUri = "https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails"
$response = Invoke-RestMethod -Headers $Headers -Uri $apiUri -Method GET
$response.value
Output:
Note: In Some Tenants Microsoft Azure PowerShell might not be visible from portal , so in that case please use the above solution it will be easier.

For Authorization code flow, try something like this -
#region Auth1
#With User Interaction for Delegated Permission
Add-Type -AssemblyName System.Web
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
}
Get-AuthCode
#Extract Access token from the returned URI
$regex = '(?<=code=)(.*)(?=&)'
$authCode = ($uri | Select-string -pattern $regex).Matches[0].Value
Write-output "Received an authCode, $authCode"
$tokenBody = #{
Grant_Type = "authorization_code"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
redirect_uri = $redirectUri
code = $authCode
ressource = $resource
}
$tokenResponse = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenBody -ErrorAction STOP
#endregion Auth1
For delegated permissions use something like below -
$tokenBody = #{
Grant_Type = "password"
Scope = "user.read%20openid%20profile%20offline_access"
Client_Id = $clientId
username = $User
password = $pw
resource = $resource
}
$tokenResponse = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenBody -ErrorAction STOP
#endregion Auth2
For Application permissions (using client credential flow) use something like this
$tokenBody = #{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$Tenantid/oauth2/v2.0/token" -Method POST -Body $tokenBody
#endregion Auth3
Despite what Method you have chosen, the tokenRepsonse Variable is holding our Key to Query against the Microsoft GRAPH API.
We want a list of all Teams in our Tenant, so this require propriate Application Permission. So for example- our Powershell to get a Full List of all Teams look like this -
$headers = #{
"Authorization" = "Bearer $($tokenResponse.access_token)"
"Content-type" = "application/json"
}
$URL = "https://graph.microsoft.com/beta/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')"
$AllTeams = (Invoke-RestMethod -Headers $headers -Uri $URL -Method GET).value
Thanks.

I finally gave up on using the REST APIs and started using Microsoft.Graph PowerShell Modules. I find the documentation pretty sparse, but at least it works for what I need. :)
Import-Module "Microsoft.Graph.Identity.Signins"
Import-Module "Microsoft.Graph.Users"
Import-Module "Microsoft.Graph.Groups"
Connect-MgGraph -TenantId $TenantId -Scopes "Directory.Read.All", "UserAuthenticationMethod.Read.All" -ForceRefresh
Select-MgProfile -Name "beta"
$report = Get-MgReportCredentialUserRegistrationDetail

Related

How to retrieve a specific SharePoint online site using graph api and PowerShell

Am currently creating a PowerShell script to retrieve a specific SharePoint online site using Microsoft graph api. The goal is, once i retrieve the site, then i can grab the siteid. My script fails on the api call. Have tried different search and filter combination, but it's not working. All of my api calls just retrieves all the sites. I got all required permissions assigned to the app registration.
Below is the entire script.
$siteUrl = "https://bernardcomms.sharepoint.com/sites/Project1"
$tenantId = ""
$clientId = ""
$clientSecret = ""
# Get an access token for the Microsoft Graph API
$tokenAuthUri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$tokenRequestBody = #{
grant_type = "client_credentials"
client_id = $clientId
client_secret = $clientSecret
scope = "https://graph.microsoft.com/.default"
}
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenAuthUri -Body $tokenRequestBody
$accessToken = $tokenResponse.access_token
# Get the ID of the SharePoint site
$requesturi = "https://graph.microsoft.com/v1.0/sites?$search=$siteUrl"
$siteIdResponse = Invoke-RestMethod -Method Get -Uri $requesturi -Headers #{Authorization=("bearer {0}" -f $accessToken)}
$siteId = $siteIdResponse.value.id
$siteId
Have tried to use below calls but it just returns all the sites.
$requesturi = "https://graph.microsoft.com/v1.0/sites?$search=weburl eq '$siteUrl'"
$requesturi = "https://graph.microsoft.com/v1.0/sites?$filter=weburl eq '$siteUrl'"
How can I retrieve a site that has a matching specific siteurl.
permissions assigned.
I agree with #Cpt.Whale, you need to change your graph query to https://graph.microsoft.com/v1.0/sites/bernardcomms.sharepoint.com:/sites/Project1
I tried to reproduce the same in my environment and got below results:
I registered one Azure AD application and granted same API permissions like below:
I have one SharePoint site named sritestsite like below:
When I ran below PowerShell script by making few changes, I got siteID successfully like below:
$hostName = "mytenant.sharepoint.com"
$siteName = "sitename"
$tenantId = <tenantID>
$clientId = <appID>
$clientSecret = <secret>
# Get an access token for the Microsoft Graph API
$tokenAuthUri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$tokenRequestBody = #{
grant_type = "client_credentials"
client_id = $clientId
client_secret = $clientSecret
scope = "https://graph.microsoft.com/.default"
}
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenAuthUri -Body $tokenRequestBody
$accessToken = $tokenResponse.access_token
# Get the ID of the SharePoint site
$requesturi = "https://graph.microsoft.com/v1.0/sites/$hostName`:/sites/$siteName"
$siteIdResponse = Invoke-RestMethod -Method Get -Uri $requesturi -Headers #{Authorization=("bearer {0}" -f $accessToken)}
$siteId = $siteIdResponse.id
$siteId
Response:
To get full response, you can run $siteIdResponse like below:

Cannot use OAuth 2.0 ROPC (Resource Owner Password Credentials flow) even using #tenant.onmicrosoft.com account

I thought I followed the doc below carefully but I still cannot use ROPC even with #tenant.onmicrosoft.com account.
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc
Can someone please enlighten me?
You probably need to URL encode the parameters of your request body.
Try the below script:
Add-Type -AssemblyName System.Web
$client_id = [System.Web.HttpUtility]::UrlEncode("<client_id>")
$client_secret = [System.Web.HttpUtility]::UrlEncode("<client_secret>")
$tenant = [System.Web.HttpUtility]::UrlEncode("<tenant_id>")
$user = [System.Web.HttpUtility]::UrlEncode("<user>")
$password = [System.Web.HttpUtility]::UrlEncode("<user_password>")
$scopes = [System.Web.HttpUtility]::UrlEncode("<scopes>")
$AuthUri = "https://login.microsoftonline.com/$tenant/oauth2/v2.0/token"
$AuthBody = "grant_type=password&client_id=$client_id&client_secret=$client_secret&username=$user&scope=$scopes&password=$password"
$Authentication =
Invoke-RestMethod -Method Post `
-ContentType application/x-www-form-urlencoded `
-Uri $AuthUri `
-Body $AuthBody
$Authentication

Unlike postman, getting slow powershell script performance when used to interact with Microsoft Graph API

We are trying to use a PS script to start using Graph API:
$clientId = "XXXXXXXXXXXXXXXXXXXXXXXXXXX"
$clientSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
$TenantName = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
$resource = "https://graph.microsoft.com/"
$URL = "https://graph.microsoft.com/v1.0/groups"
$tokenBody = #{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $tokenBody
$result = Invoke-RestMethod -Headers #{Authorization = "Bearer $($tokenResponse.access_token)"} -Uri $URL -Method Get -Verbose
($result | select-object Value).Value | Select-Object id
I am not sure if it is the script or something else but the success rate for this script is 20%. Most of the time the return is either delayed or timed out.
We are getting consistent results when we use postman to interact with the graph API. How come powershell is giving us a hard time? Is there something that we need to change on our script?
Thanks

The remote server returned an error : (401) Unauthorized - PowerShell - Microsoft Graph API

I am trying to get the list of all groups where the resourceProvisioningOptions = Team, here is the url which gets all the data through API call:
$clientID = xxxx
$tenantName = xxxx
$ClientSecret = xxxx
$resource = "https://graph.microsoft.com/"
$ReqTokenBody = #{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
client_Id = $clientID
Client_Secret = $clientSecret
}
$authheader = #{
'Authorization' = "Bearer $($Tokenresponse.access_token)"
'Content-Type'='application\json'
}
$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody
$test = "'Team'"
$apiUrl = 'https://graph.microsoft.com/beta/groups?$filter=resourceProvisioningOptions/Any(x:x eq {0})' -f $test
$Data = Invoke-RestMethod -Uri $apiUrl -Headers $authheader -Body $ReqTokenBody -Method Get
$Groups = ($Data | select-object Value).Value | Select-Object displayName, id, description, mail | Out-File .\texxtfile.txt
However, I am getting a 401 error when I try to run the script even though I have all the permissions required to make the API call.
You may have picked application permissions in your AAD application. There is an additioanl step. You will need to admin consent your application in the app registration portal to use Group.Read.All to run this. If you have not done this in the UI this will fail.

Microsoft Graph API - app in powershell - forbidden response

I am attempting to make a Microsoft Graph API call using powershell 5.1. I have Registred the app. I use the AppID and secret to obtain an Authorization Bearer token. I then construct my API request. The security/events endpoint only returns a Forbidden response.
API Permissions Granted
My script to make the call looks like this:
# Define AppId, secret and scope, your tenant name and endpoint URL
$AppId = [myAPPID]
$AppSecret = [myAPPSecret]
$Scope = "https://graph.microsoft.com/.default"
$TenantName = "[mytenant].onmicrosoft.com"
$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"
# Add System.Web for urlencode
Add-Type -AssemblyName System.Web
$Body = #{
client_id = $AppId
client_secret = $AppSecret
scope = $Scope
grant_type = 'client_credentials'
}
# Splat the parameters for Invoke-Restmethod for cleaner code
$PostSplat = #{
ContentType = 'application/x-www-form-urlencoded'
Method = 'POST'
# Create string by joining bodylist with '&'
Body = $Body
Uri = $Url
}
$Request = Invoke-RestMethod #PostSplat
$Header = #{
Authorization = "$($Request.token_type) $($Request.access_token)"
}
$Uri = "https://graph.microsoft.com/v1.0/security/events"
$SecurityAlertsRequest = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"
Just looking to see If I have missed something obvious. Is there any other reason this would response with Forbidden for this configuration?
You have granted delegated permissions to the app.
Those only apply when there is a user signed in to your app and you acquire an access token to act on their behalf.
You need to grant application permissions to your app in Azure AD.