In the past, I’ve written some blog posts that show how you can setup automation across tenants leveraging GDAP. GDAP effectively broken a lot of MSP and vendor automation because it requires a specific configuration. In this article, I am going to share a script I have created so that you can update permissions for your automations over time across all of your GDAP relationships. (applies to both MSPs and vendors)
Problem Statement
As an MSP or vendor that integrates with Microsoft, you want to set up an app registration for automations with the principles of least privilege. Inevitably, you may need to increase scope of your API permissions on the app registration over time. If you do not have the Global Administrator role assigned to the group with your service principal, your API calls will inevitably fail as you add permissions to your app registration. The only way to overcome this is to go back and add the role you need to call the specific API to every single GDAP relationship in partner center. Real Life example:
- You have an existing app registration you have consented to across all of your customers.
- Your customers have a GDAP relationship with the access assignments set to a handful of roles.
- You add the Device.ReadWrite.All permission to your app registration
- You try to make API calls with this newly added permissions and find out you can’t
- In investigating, you see that the roles part of the access assignment do not include any that would allow you to write to devices.
- You have to add Intune Administrator role to all GDAP access assignments across customers
This final statement is what I wanted to solve for through scripting. We have 1000+ customers and no one has the time to manually go to each relationship and add the permission to the access assignment we need.
Automation Steps
1. Get a Refresh Token
Prerequisites: Follow my previous post on setting up an App Registration: How to leverage Microsoft APIs for Automation – (tminus365.com)
2. Define your variables
The script I wrote requires you to define some variables that will be used:
GroupID => This is the security group that has your service principal you used to set up the integration. You can find this ID by going to the properties of the Group in Entra ID
RoleID => This is the ID of the role you want to include of the access assignment. These values can be found from the following support article: Microsoft Entra built-in roles | Microsoft Learn
3. Ensure your App Registration has the correct GDAP scope
The API calls we will make to get GDAP relationships, access assignments and update them will require the following Delegated admin permission:
DelegatedAdminRelationship.ReadWrite.All |
GDAP APIs leveraged:
- Get GDAP relationships: List delegatedAdminRelationships – Microsoft Graph v1.0 | Microsoft Learn
- Get Access Assignments: List accessAssignments – Microsoft Graph v1.0 | Microsoft Learn
- Update Access Assignments:Update delegatedAdminAccessAssignment – Microsoft Graph v1.0 | Microsoft Learn
4. Run the Automation Script
The following script will prompt you for secrets to acquire an access token and then ask for the variables we defined earlier. Here is a summary of the logic:
- Get an Access token for graph
- Get all active GDAP relationships
- Define a Variable for the security Group ID that your service principal is located in
- Define a Variable for the role ID you want to add: Microsoft Entra built-in roles | Microsoft Learn
- Define a blank variable called RelationshipID
- Define an variable called GroupAssignments which is an empty array
- Loop through all GDAP relationships IDs
- Set the variable of Relationship ID to the ID you are passing into the API
- For each ID, loop through each object
- See if the acccesContainer.accessContainerId = the Security Group where your service principal is located
- If this is true, store the ID and the Relationship ID as an Object added to an GroupAssigments array as a new object appended to the array
- Loop through the GroupAssigments array and pass them into the update API call from Graph in the request:
- Update delegatedAdminAccessAssignment – Microsoft Graph v1.0 | Microsoft Learn
- In the body, we will pass in the role ID variable we need to add.
- This should be a try/catch block in which we can display a write-host of success or fail and the failure error message.
This will tell you if the tenant succeeds or fails. If it does fail, its most likely because the role isnt part of the existing GDAP relationship
5. Revoke and Re-add CPV consent
When we add a new API permission to our app registration and perform the process of adding a new role to our GDAP access assignment, we need to revoke and er-add consent to the application as the customer tenant is only pre-approved with the original list of permissions provide.
5a. Get an access token for partner center
# Define variables $appId = 'your Azure AD application client ID' $appSecret = 'your Azure AD application client secret' $tenantId = 'your Azure AD tenant ID' $scope = 'https://api.partnercenter.microsoft.com/.default' $redirectUri = 'your redirect URI' # Construct authorization endpoint URL $authEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/authorize?client_id=$appId&response_type=code&redirect_uri=$redirectUri&scope=$scope" # Navigate to authorization endpoint and obtain authorization code Start-Process $authEndpoint $code = Read-Host "Enter authorization code" $body = "grant_type=authorization_code&client_id=$appId&client_secret=$appSecret&code=$code&redirect_uri=$redirectUri&scope=$scope" $headers = @{ 'Content-Type' = 'application/x-www-form-urlencoded' } $tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/token" $response = Invoke-RestMethod -Method POST -Uri $tokenEndpoint -Body $body -Headers $headers $AccessToken = $response.Access_Token
5b. Use the access token to revoke consent across you customers.
You will want to loop through all of your customers. (use the same GDAP relationship API to get a list of tenant IDs) and run the following API to revoke consent from the App Registration using the existing App ID (ClientID).
https://learn.microsoft.com/en-us/partner-center/developer/control-panel-vendor-apis#remove-consent
5c. Re-add consent with new scope.
You will modify the body in the following with the list of roles you want to consent on behalf of your customers.
# Define variables $appId = 'your Azure AD application client ID' $appSecret = 'your Azure AD application client secret' $tenantId = 'your Azure AD tenant ID' $scope = 'https://api.partnercenter.microsoft.com/.default' $redirectUri = 'your redirect URI' # Construct authorization endpoint URL $authEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/authorize?client_id=$appId&response_type=code&redirect_uri=$redirectUri&scope=$scope" # Navigate to authorization endpoint and obtain authorization code Start-Process $authEndpoint $code = Read-Host "Enter authorization code" $body = "grant_type=authorization_code&client_id=$appId&client_secret=$appSecret&code=$code&redirect_uri=$redirectUri&scope=$scope" $headers = @{ 'Content-Type' = 'application/x-www-form-urlencoded' } $response = Invoke-RestMethod -Method POST -Uri $tokenEndpoint -Body $body -Headers $headers $AccessToken = $response.AccessToken $CustomerTenantId = 'Your Customer TenantID' # Get list of customers $uri = "https://api.partnercenter.microsoft.com/v1/customers" $headers = @{ Authorization = "Bearer $($AccessToken)" 'Accept' = 'application/json' } $Customers = Invoke-RestMethod -Uri $uri -Headers $headers # Loop through each customer and run the existing script foreach ($Customer in $Customers.value) { $CustomerTenantId = $Customer.id # Consent to required applications $uri = "https://api.partnercenter.microsoft.com/v1/customers/$CustomerTenantId/applicationconsents" $body = @{ applicationGrants = @( @{ enterpriseApplicationId = "00000003-0000-0000-c000-000000000000" scope = "Directory.Read.All,Directory.AccessAsUser.All" }, @{ enterpriseApplicationId = "00000002-0000-0ff1-ce00-000000000000" scope = "Exchange.Manage" } ) applicationId = $AppId displayName = $AppDisplayName } | ConvertTo-Json Invoke-RestMethod -Uri $uri -Headers $headers -Method POST -Body $body -ContentType 'application/json' }
Conclusion
I hope this article was helpful in showcasing how to automate updating access assignments across GDAP relationships. While you could just have the Global Admin role assigned to all access assignments with your automation, it is kind of defeating the purpose of the additional security GDAP should be providing. Happy scripting!