I have a PowerShell script that uploads data to a SharePoint site, however, the script needs more than an hour to upload all the data. Is there anyway to increase the tokens life span. Below is the call I use to get the Bearer token
$TenantId = "T123"
$ClientId = "C123"
$Secret = "S123"
$uri = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token "
$body = #{
grant_type = "client_credentials"
client_id = $ClientId
client_secret = $Secret
scope = "https://graph.microsoft.com/.default"
}
#call to get access token
$resp = Invoke-RestMethod -Method Post -Uri $uri -Body $body -ContentType "application/x-www-form-urlencoded"
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
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
I just want to create a folder one my OneDrive for Buiness Account through the Graph API. After many hours I got stuck with an error which I really can't comprehend. It says that the property 'Attributes' does not exist on type 'oneDrive.folder' and I should not use this property. The problem is that I don't use this property. After alot of research I think it has something to do with the pinned metadata or something like that. But in gerneral I really dont know what to do futher.
I used the Graph Explorer and another website to create this script.
The Error:
-1, Microsoft.SharePoint.Client.InvalidClientQueryException
The property 'Attributes' does not exist on type 'oneDrive.folder'.
Make sure to only use property names that are defined by the type.
And this is my code:
$clientId = "XXXXXXXXXXXXXX"
$tenantId = "XXXXXX.onmicrosoft.com"
$clientSecret = 'XXXXXXXXXXXX'
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$body = #{
client_id = $clientId
scope = "https://graph.microsoft.com/.default"
client_secret = $clientSecret
grant_type = "client_credentials"
}
$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
$token = ($tokenRequest.Content | ConvertFrom-Json).access_token
Write-Host $token
$uri = "https://graph.microsoft.com/v1.0/<ID XXXXXXX>/drive/root/children"
$method = 'POST'
$head= #{Authorization = "Bearer $token"}
$postPara= #{
name= "NewFolder"
folder= {}
} | ConvertTo-Json
$antwort = Invoke-RestMethod -Headers $head -Uri $uri -Method $method -Body $postPara -ContentType "application/json"
Write-Host $antwort
It really should work and I'm sitting on this sample task for over 10 Hours ._.
The issue with you code is
$postPara= #{
name= "NewFolder"
folder = {}
} | ConvertTo-Json
If you just output $postPara you will see the issue is because your missing the # in front of value in folder you will actually get the details from the underlying script populated in there. so try
$postPara= #{
name= "NewFolder"
folder = #{}
} | ConvertTo-Json
$postPara
Which should fix it.A good diag tool is also to use fiddler to look at the request being sent to server.
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.