Category Archives: AzureAD

Inviting an external user to a PowerApp programmatically

Another week, another use case for Managed Identities in Automation Accounts!

The scenario today concerns a PowerApp and connected resources that should be shared with external identities, automatically of course. For each user this requires a guest account in the host / resource tenant, and a license. The license can be applied in the home tenant of the guest, or in your tenant.

Key points:

  1. Runbook that invites a user and adds the resulting guest account to a security group
  2. Security group gives access to the PowerApp and underlying (SpO) resources, and uses Group Based Licensing to license the guest for PowerApps and Sharepoint Online
  3. Logic App that is triggered by the PowerApp (trigger on create item in a sharepoint list), and starts the runbook
  4. When the invited user (guest) redeems the invitation, they are directed to a Sharepoint page first so Sharepoint syncs their profile. Otherwise, the PowerApp will not have access to any lists in Sharepoint Online as Guests are not synced to SpO until they access SpO directly.

I may demo the PowerApp, Logic App and Sharepoint lists at some point, but the main thing I wanted to share today is the Azure Runbook that creates the Guest invitation and adds the Guest to a security group using the Managed Identity of the Automation account, instead of service accounts or other pre-2021 solutions:

Expanding Microsoft First Party Application Permissions in AzureAD

Connect-AzAccount and my own silent token function use the Microsoft built in client ID of “1950a258-227b-4e31-a9cf-717495945fc2”.

The resulting token has some openID scopes and most backend calls use RBAC, but I wanted to experiment by adding OAuth2 permissions and app roles to it so I can use the context/cached refresh token to also call other Microsoft API’s.

I discovered that this can be done by adding the client to your AzureAD as an SPN (Enterprise Application):

$spn = New-AzureADServicePrincipal -AppId "1950a258-227b-4e31-a9cf-717495945fc2" -DisplayName "Microsoft Azure PowerShell"

Since this is Microsoft owned app, you’ll actually see that show up in AzureAD:

Next, we can add the AuditLog.Read.All permission to the local instance of this app ($spn), this is a Graph permission, so we first need to get the resourceId of graph in our tenant:

$GraphServicePrincipal = Get-AzureADServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"

Then we prepare our post body for the Graph API to add the AuditLog.Read.All permission to Microsoft Azure PowerShell:

        $patchBody = @{
            "clientId"= $spn.ObjectId
            "consentType"= "AllPrincipals"
            "principalId"= $Null
            "resourceId"= $GraphServicePrincipal.ObjectId
            "scope"= "AuditLog.Read.All"
            "expiryTime" = "2022-05-05T09:00:00Z"

Then we’ll grab a token for the Graph API:

$token =  get-azResourceTokenSilentlyWithoutModuleDependencies -userUPN

And call Graph to add the permission to our local instance of Microsoft Azure PowerShell:

Invoke-RestMethod -Method POST -body ($patchBody | convertto-json) -Uri "" -Headers @{"Authorization"="Bearer $token"} -ContentType "application/json"

Any future tokens you grab for using 1950a258-227b-4e31-a9cf-717495945fc2 as clientId will now contain the AuditLog.Read.All scope as well;

You should also be able to add approles, but since (hopefully) only Microsoft has the client credentials, they won’t do much.

Inspired by a question PrzemyslawKlys asked me 🙂

Scheduled migration / group add script

Usually, I want to roll out new features gradually to my users.

I used to do this by creating a security group, assigning new policies/software/patches etc to it, and then telling the IT staff to add users to it in groups and check with them if things went well.

But why? Why is that manual step needed?

Exactly, it is not!

So attached script can be scheduled in an Azure Automation Account (with Managed Identity enabled!) to add an X amount of users from Group A to Group B. This script can be scheduled to e.g. run daily until all users have been moved. Multiple scheduled would also work in case you’re deploying multiple features.

Handling exceptions

When certain users need to be excluded, simply create an exclusion security group and exclude that from your policies. Exclusions take precedence in most systems.

Grouping users

You can group deployment by supplying the groupByProperty, e.g. by Country

The code

Required modules

  • Az.Accounts

Graph Permissions

You’ll have to give the Managed Identity of your automation account sufficient graph permissions. This cannot be done through the GUI, so I’ve added a snippet you can use here:


You’ll probably want to communicate to your users in advance, it’d be fairly easy to generate a report in advance or to add email notifications to the script but that will require additional graph permissions.

Deploying a service principal to (CSP) child tenants

Cloud Solution Providers, or sometimes other types of Managed Service Providers often have to manage a large number of tenants. Ideally, they do their ‘Infrastructure As Code’.

Using various API’s to manage tenants is best done using a Service Principal instead of a user (MFA, lifecycle, etc).

Recently, I was tasked to provide a deployment method of a Service Principal (multi-tenant) to all child tenants of an MSP, including programmatically granting various Graph API permissions. The Graph endpoint for this (oauth2PermissionGrants) is still in Beta, but the other methods I wrote about in the past are not as reliable so we’re using the Beta endpoint.

The linked example script creates an SPN and grants AuditLog.Read.All. If you’re an MSP/CSP, you’ll probably want to capture the tenant ID’s you’re installing into, so you can easily administer these tenants centrally using your main multi-tenant SPN.

Moving forwards, you won’t need an admin user / service account in the tenants you manage anymore, at least for the API’s that support SPN’s.

Note: to completely remove module dependencies / login, check my independent token function.

Sensitive group protection

It is best practise in IT to secure access to resources with Groups.

Membership of a security group means access to whatever resources are secured by that group. Sometimes these groups are self-managed by an owner, sometimes centrally.

In all cases, fairly low privileged users, that are not global admins, can add users to these groups including themselves. Imagine that you have a group called ‘Global Admins’, and your helpdesk user assigns himself to that group. You’d like to know right?

With Privileged Access Groups in Azure AD (Preview) you can protect groups like these actively, but, this requires a P2 license and still lacks some customization features.

An alternative method is to use a simple alerting rule in MCAS (Microsoft Cloud App Security), where you set an alert when ‘someone’ joins a specific group, or if you want to do more than alerting you could also run an automation playbook.

Here’s how to protect a specific Azure AD or Office 365 group with MCAS:

  1. look up its GUID in AzureAD
  2. Create an Activity Policy in the MCAS console
  3. Specify the group GUID as ‘Activity object ID’ in the policy and the correct action type: