Microsoft Graph API Get Group Events - azure

I'm trying to use the MS Graph API (which I'm new at) to write a Powershell script to copy the events from a private group calendar into a public group calendar.
So far I've managed to get the private group using this call:
$api = "https://graph.microsoft.com/v1.0/groups"
$groups = $null
try { $groups = Invoke-RestMethod -Headers #{Authorization = "Bearer $($token.access_token)" } -Uri $api -Method "GET" -ContentType "application/json" }
catch { Write-host -Foreground Red $_}
$calendar_group = $groups.value | ? -Property mailNickname -eq $pvt_group
However, when I try and move this forth and get the events for that group, I get bounced on a 403 error.
The calls I'm trying are either:
$api = "https://graph.microsoft.com/v1.0/groups/$group_ID/calendar/events"
Write-Host $api -Fore Green
$events = $null
try { $events = Invoke-RestMethod -Headers #{Authorization = "Bearer $($token.access_token)" } -Uri $api -Method "GET" -ContentType "application/json" }
catch { Write-host -Foreground Red $_}
Or:
$api = "https://graph.microsoft.com/v1.0/groups/$group_ID/events"
Write-Host $api -Fore Green
$events = $null
try { $events = Invoke-RestMethod -Headers #{Authorization = "Bearer $($token.access_token)" } -Uri $api -Method "GET" -ContentType "application/json" }
catch { Write-host -Foreground Red $_}
Both fail on the same error.
NB: the $group_ID variable is correctly valued by the first call.
I've the app registered on Azure with the following permimssions:
Calendars.Read Delegated
Calendars.Read.Shared Delegated
Calendars.ReadWrite Delegated
Calendars.ReadWrite.Shared Delegated
Directory.AccessAsUser.All Delegated
Directory.Read.All Delegated
Directory.Read.All Application
Directory.ReadWrite.All Delegated
Directory.ReadWrite.All Application
Group.Read.All Delegated
Group.Read.All Application
Group.ReadWrite.All Delegated
Group.ReadWrite.All Application
GroupMember.Read.All Delegated
GroupMember.Read.All Application
User.Read Delegated
Does anybody know what I'm doing wrong?
Many thanks in advance.

The access token that you use, is an application access token (the permissions are defined under the Roles section). If a user access token is used, you will see the permissions under de scp section.
In the Microsoft docs (here) it is stated that calling the /calendar/events unfortunately is not supported with an application access token.
If you assign your account to the Groups administrator role, you will implicitly be assigned the Group.Read.All permission. Then use the access token from your account to call the endpoint without any issues (because you've already granted the needed permissions for delegation in the App Registration).

Related

Unable to create Azure B2C User Flow via powershell with b2cIdentityUserFlow

I am trying to create a user flow with PowerShell, but I receiveThe remote server returned an error: (403) Forbidden.. I was reading the documentation from Microsoft but with no success.
Connect-AzAccount -Tenant "myorg.onmicrosoft.com"
$managementAccessToken = Get-AzAccessToken -TenantId "$tenantId" -ResourceTypeName MSGraph
$DefinitionFilePath = "C:\azdeploy\flows\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($managementAccessToken.Token)";
} `
-Body $signinFlowContent
JSON Content(Default From Microsoft Docs):
{
"id": "Customer",
"userFlowType": "signUpOrSignIn",
"userFlowTypeVersion": 3
}
Connect-AzAccount is made with a user who is Global Administrator, also tried with Lifecycle Workflows Administrator permissions. I don't know what to do, trying the old API but it is deprecated. I need to create a few User Flows with а few Application Claims. How can I achieve this?
Thanks!
I tried to reproduce the same in my environment and got below results:
I created one json file with same parameters as you like below:
I have one user named Sritest having Global Administrator role like below:
When I ran the same code as you by signing in with above user, I got same error as below:
Connect-AzAccount -Tenant "myorg.onmicrosoft.com"
$managementAccessToken = Get-AzAccessToken -TenantId "$tenantId" -ResourceTypeName MSGraph
$DefinitionFilePath = "C:\test\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($managementAccessToken.Token)";
} `
-Body $signinFlowContent
Response:
You need to have IdentityUserFlow.ReadWrite.All permission to create userflow.
To resolve the error, I registered one Azure AD application and added that API permission like below:
Make sure to grant admin consent after adding API permissions in application. Now, I created one client secret and added all these details in getting access token by modifying PowerShell code.
When I ran below modified code, userflow created successfully as below:
Connect-AzureAD -TenantId "c6d99123-0cf9-4b64-bde3-xxxxxxxxx"
$graphtokenBody = #{
grant_type = "client_credentials"
scope = "https://graph.microsoft.com/.default"
client_id = "appID"
client_secret = "secret"
}
$graphToken = Invoke-RestMethod -Uri "https://login.microsoftonline.com/c6d99123-0cf9-4b64-bde3-xxxxxxxxx/oauth2/v2.0/token" -Method POST -Body $graphtokenBody
$token = $graphToken.access_token
$DefinitionFilePath = "C:\test\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($token)";
} `
-Body $signinFlowContent
Response:
To confirm that, I checked the same in Portal where B2C_1_Customer userflow is present like below:

Using token from Connect-AZAccount on Intune Graph calls

So, after years consuming valuable information from StackOverflow this is my first question!
I used part of a code I found when researching as I start to move away from App Secrets in Graph API and prepare to use Managed Identities.
It successfully authenticate and generate the token when running locally(haven't tested anything on Azure Automation yet as this would be the PoC) but it will not work for everything in Graph, it works when querying AD Users and Groups but not on anything else, it seems it is not getting all the scopes the accounts has access to although the account is global admin.
This part will check if you are using Managed Identity or prompt for login
#Requires -Modules #{ ModuleName="Az.Accounts"; ModuleVersion="2.7.0" } , #{ ModuleName="Az.Resources"; ModuleVersion="5.1.0" }
Param(
[Switch]$nonInteractive
)
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Web")
$res = [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
if(!$Global:AZConnection){
try{
if($nonInteractive){
Write-Output "Logging in with MI"
$Null = Connect-AzAccount -Identity -ErrorAction Stop
Write-Output "Logged in as MI"
}else{
Write-Output "Logging in to Azure AD"
$Global:AZConnection = Connect-AzAccount -Force -ErrorAction Stop
Write-Output "Logged in to Azure AD with $($Global:AZConnection.Context.Account.id)"
}
}catch{
Throw $_
}
}
After this it will get the context and authenticate against graph.microsoft.com & graph.windows.net to get both tokens.
It will also prepare the headers to be used on WebRequests
$context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
$graph = ([Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Always, $null, "https://graph.microsoft.com"))
$graphToken = $graph.AccessToken
$AAD = ([Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, "https://graph.windows.net"))
$aadToken = $AAD.AccessToken
$HeadersGraph = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $graphToken"
}
$HeadersAAD = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $aadToken"
}
Trying to use the token to query user or group information will work
$UserData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/Users" -Method GET -Headers $HeadersGraph
$GroupData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/Users" -Method GET -Headers $HeadersGraph
But if I try to query any Intune or SharePoint URI it will give me:
Invoke-RestMethod : The remote server returned an error: (403) Forbidden.
$deviceData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices" -Method GET -Headers $HeadersGraph
$SharepointData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/sites" -Method GET -Headers $HeadersGraph
I can use these Tokens to authenticate on other modules but I was trying to avoid it and keep everything on Graph web requests
#This also works
$Intune = Connect-MgGraph -AccessToken $graphToken
$AzureAD = Connect-AzureAD -AadAccessToken $aadToken -AccountId $context.Account.Id
Does anyone know of any resource principal that I can authenticate that would give me a valid token to make calls to SharePoint and Intune?
I assume that you're aware that this is a permission issue and not a token issue.
I have no solution for your problem. I just want to let you know, that nowadays there is an easier way to get Graph Access tokens (since you are already using the Az.Accounts module).
https://learn.microsoft.com/en-us/powershell/module/az.accounts/get-azaccesstoken

I would like to give admin consent to API permissions through powershell script

I have a code in Az module of powershell to create appID, app secret and assign API permission. How do I grant admin consent to all the API permissions that I assigned to the AzApp?
...
$context = Get-AzContext
$ResourceAppIdURI = "https://graph.windows.net/"
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $ResourceAppIdURI).AccessToken
$headers = #{ }
$headers.Add("Content-Type", "application/json")
$headers.Add("Accept", "application/json")
$headers.Add("Authorization", "Bearer $($token)")
$objectID = $myApp.ObjectId
$url = "https://graph.windows.net/$tenant/applications/{0}?api-version=1.6" -f $objectID
Write-Host "URL: " $url
$postData = "{`"requiredResourceAccess`":[
{`"resourceAppId`":`"00000003-0000-0000-c000-000000000000`",
`"resourceAccess`":[
{`"id`":`"e1fe6dd8-ba31-4d61-89e7-88639da4683d`",`"type`":`"Scope`"},
{`"id`":`"7ab1d382-f21e-4acd-a863-ba3e13f7da61`",`"type`":`"Role`"},
{`"id`":`"5b567255-7703-4780-807c-7be8301ae99b`",`"type`":`"Role`"},
{`"id`":`"e2a3a72e-5f79-4c64-b1b1-878b674786c9`",`"type`":`"Role`"},
{`"id`":`"df021288-bdef-4463-88db-98f22de89214`",`"type`":`"Role`"}
]
}]
}";
Invoke-RestMethod -Uri $url -Method "PATCH" -Headers $headers -Body $postData
Write-Host "App created..."
Write-Host "AppID: " $myApp.ApplicationId
Write-Host "App Secret: " $secret
Write-Host "TenantID: " $tenant.Id
There is no API exposed by Microsoft to grant admin consent for Azure AD application / service principal. You can vote this post on User Voice.
There is a workaround:
Call Microsoft Graph API Create a delegated permission grant and Grant an appRoleAssignment to a service principal in Powershell.
A sample for your reference:
$context = Get-AzContext
$ResourceAppIdURI = "https://graph.windows.net/"
$ResourceGraphURI = "https://graph.microsoft.com/"
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $ResourceAppIdURI).AccessToken
$graphToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $ResourceGraphURI).AccessToken
$clientID = "d154cc56-f1a2-4906-9f26-bfb4756f9c20"
$resourceID = "08a1faff-51c1-4cbb-81c4-1bc11286da76"
$scopes = "Sites.Read.All User.Read User.Read.All User.ReadBasic.All"
$body = #{
clientId = $clientID
consentType = "AllPrincipals"
principalId = $null
resourceId = $resourceID
scope = $scopes
startTime = "2019-10-19T10:37:00Z"
expiryTime = "2020-10-19T10:37:00Z"
}
$apiUrl = "https://graph.microsoft.com/beta/oauth2PermissionGrants"
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization = "Bearer $($graphToken)" } -Method POST -Body $($body | convertto-json) -ContentType "application/json"
$principalId = "d154cc56-f1a2-4906-9f26-bfb4756f9c20"
$body1 = #{
principalId = $principalId
resourceId = $resourceID
appRoleId = "df021288-bdef-4463-88db-98f22de89214"
}
$apiUrl1 = "https://graph.microsoft.com/beta/servicePrincipals/$($principalId)/appRoleAssignedTo"
Invoke-RestMethod -Uri $apiUrl1 -Headers #{Authorization = "Bearer $($graphToken)" } -Method POST -Body $($body1 | convertto-json) -ContentType "application/json"
For the first call https://graph.microsoft.com/beta/oauth2PermissionGrants:
clientID is the object id of the service principal (not Azure AD application), you can find it using Get-AzADServicePrincipal. You can also find it on Azure Portal - Azure Active Directory - Enterprise Applications, search for the name of your Azure AD application.
resouceID is the object id of Microsoft Graph service principal. You can find under Enterprise applications (search for "00000003-0000-0000-c000-000000000000").
scopes are the delegated permissions you want to grant admin consent.
For the second call https://graph.microsoft.com/beta/servicePrincipals/$($principalId)/appRoleAssignedTo:
principalId is the same as clientID mentioned above.
appRoleId is the application permission id.
Actually, the Azure AD PowerShell module provides a cmdlet equivalent for Application Permissions : New-AzureADServiceAppRoleAssignment.
Even if it's poorly documented, the command adds the requested application permissions (and grant admin consent if you have the right to do so) to your AAD Application (through the service principal).
# If it's not the case, declare your AAD Application as a service principal (Enterprise Application)
$aadappsp = New-AzureADServicePrincipal -AppId "AAD_APPLICATION_ID"
# Id of the application permission (role)
$roleId = "2a8d57a5-4090-4a41-bf1c-3c621d2ccad3" # TermStore.Read.All
# Object Id of the concerned Service Principal (could be Graph or SharePoint for example)
# (Not the Application Id like "00000003-0000-0ff1-ce00-000000000000" for SharePoint)
$aadSpObjectId = "c30e8a24-ff90-464e-aed3-7c39a7bdc280"
# Register the application permission
New-AzureADServiceAppRoleAssignment -ObjectId $aadappsp.ObjectId -Id $roleId -PrincipalId $aadappsp.ObjectId -ResourceId $aadSpObjectId
It's using a dedicated endpoint, so don't be surprised if you have this display once the command correctly executed:
(permissions added through PowerShell appear as "Other permissions granted for...")
To avoid that, you have to first add them through interface or with New-AzureADApplication (to register the permissions as "configured") and New-AzureADServicePrincipal (to grant admin consent properly for your organization).
Sadly, there's no cmdlet for granting admin consent on Delegated Permissions, so the answer provided by #Allen Wu still works in this case (just update the URIs to use v1.0 version instead of beta).

How to get the access token to Azure API Management programmatically?

I'm trying to implement Azure Active Directory in my API Management instance using the Protect an API by using OAuth 2.0 with Azure Active Directory and API Management doc. The doc suggests that in order to get the access token I need to use the Developer Portal.
My problem is: An external application is going to communicate with API Management. Is there a way to omit the Developer Portal and get the access token programmatically?
It's a pain but thanks to Jos Lieben I am able to do it with this Powershell function
It's specifically for granting API access on behalf of the Org, but as you can see you can extract the commands to get and use the API token.
Original Author Link: https://www.lieben.nu/liebensraum/2018/04/how-to-grant-oauth2-permissions-to-an-azure-ad-application-using-powershell-unattended-silently/
Function Grant-OAuth2PermissionsToApp{
Param(
[Parameter(Mandatory=$true)]$Username, #global administrator username
[Parameter(Mandatory=$true)]$Password, #global administrator password
[Parameter(Mandatory=$true)]$azureAppId #application ID of the azure application you wish to admin-consent to
)
$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($Username, $secpasswd)
$res = login-azurermaccount -Credential $mycreds
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$refreshToken = #($context.TokenCache.ReadItems() | where {$_.tenantId -eq $tenantId -and $_.ExpiresOn -gt (Get-Date)})[0].RefreshToken
$body = "grant_type=refresh_token&refresh_token=$($refreshToken)&resource=74658136-14ec-4630-ad9b-26e160ff0fc6"
$apiToken = Invoke-RestMethod "https://login.windows.net/$tenantId/oauth2/token" -Method POST -Body $body -ContentType 'application/x-www-form-urlencoded'
$header = #{
'Authorization' = 'Bearer ' + $apiToken.access_token
'X-Requested-With'= 'XMLHttpRequest'
'x-ms-client-request-id'= [guid]::NewGuid()
'x-ms-correlation-id' = [guid]::NewGuid()
}
$script:url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$azureAppId/Consent?onBehalfOfAll=true"
Invoke-RestMethod -Uri $url -Headers $header -Method POST -ErrorAction Stop
}

Programatically Add users to group in AzureAD as group owner

Is there an API for AzureAD which allows group owners to add users to their owned groups?
Using the Graph api's looks to require admin consent (granting admin permissions) and so provides access to more than the user's owned groups. I don't want to grant this.
I also don't want to use delegated access - an admin shouldn't need to be present for a group owner (perhaps a service principal?) to add users to their own group?
I have some interesting findings.
A) If you set a service principal as the owner of a group, and want to manage the group with the service principal, you have to add and grant necessary permission for Azure AD Graph API.
B) If you set a user as the owner of a group, then you can use the public client (1b730954-1685-4b74-9bfd-dac224a7b894) and user credential to acquire token, and then call the AAD graph API as the user to manage the group.
Here, I use PowerShell to make http requests. You can use other program languages.
# Get token for Azure AD Graph
$uri = "https://login.microsoftonline.com/{tenant_name_or_id, for example: hanxia.onmicrosoft.com}/oauth2/token"
$body = #{grant_type='password';resource='https://graph.windows.net';client_id='1b730954-1685-4b74-9bfd-dac224a7b894';username='normaluser#hanxia.onmicrosoft.com';password='a*******7'}
$result = Invoke-RestMethod -Method Post -Uri $uri -Body $body
$accessToken = $result.access_token
# Azure AD Graph. Get group information
$tenantId = "e4c9ab4e-bd27-40d5-8459-230ba2a757fb"
$groupId = "f37d06f2-e26f-45f9-b9b1-da13d0b79ea7"
$apiVersion = "1.6"
$result = Invoke-WebRequest -Method Get `
-Uri ("https://graph.windows.net/" + $tenantId + "/groups/" + $groupId +"?api-version=" + $apiVersion) `
-Headers #{ "Authorization" = "Bearer " + $accessToken }
$result.Content | ConvertFrom-Json | ConvertTo-Json
# Azure AD Graph. Get users in group
$result = Invoke-WebRequest -Method Get `
-Uri ("https://graph.windows.net/" + $tenantId + "/groups/" + $groupId +"/`$links/members" +"?api-version=" + $apiVersion) `
-Headers #{ "Authorization" = "Bearer " + $accessToken }
$result.Content | ConvertFrom-Json | ConvertTo-Json
# Azure AD Graph. Add user to group
$userObject = #{"url" = "https://graph.windows.net/e4c9ab4e-bd27-40d5-8459-230ba2a757fb/directoryObjects/3f43b292-adac-48f9-a623-ee76ca9c7174"} | ConvertTo-Json
$result = Invoke-WebRequest -Method Post `
-Uri ("https://graph.windows.net/" + $tenantId + "/groups/" + $groupId +"/`$links/members" +"?api-version=" + $apiVersion) `
-Headers #{ "Authorization" = "Bearer " + $accessToken; "Content-Type" = "application/json" } `
-Body $userObject
if($result.StatusCode -eq 204){ Write-Host "User added" }
Note:
1b730954-1685-4b74-9bfd-dac224a7b894 is a common application from Microsoft for every tenant.
API Reference: Operations on groups | AAD Graph API reference
The reason that consent is needed is that while the group owner has rights to add users to a group, an app by default does not. The delegated permissions give the app rights to modify groups on behalf of the user, depending on the user's rights as well. It's usually the better approach. App permissions give the app itself permissions to act without a user. Which is often too much, but has its use cases.
You need to grant the delegated permission at least to the app so it can do the modification on behalf of the user.

Resources