Category Archives: Powershell

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:

License reports by a Managed Identity

Capitalizing on the huge advantages that managed identities in Azure offer, here’s another use case similar to the scheduled migration script that also uses it’s managed identity (and graph permissions) to autonomously run as an Azure Runbook without any credentials stored.

The script will log in to Graph, retrieve all unique license types and how they are assigned to users, and will then email an HTML report (table) the the specified recipient, order by email domain.

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 ­čÖé

Sharepoint permission auditing

When auditing a Sharepoint environment, an important component is permissions;

  • invited users
  • sharing links
  • inherited permissions
  • unique permissions
  • broken inheritance
  • sites, webs
  • lists, libraries

I’ve heavily modified Salaudeen┬áRajack’s work to share a more fully featured and faster PowerShell auditing script that will dump all unique permissions (up to item level, recursively) for all sharepoint sites (including O365 group sites). For files, folders, sites, libraries, etc etc.

It retrieves membership of groups so the resulting CSV file contains all permissions, with exception of the “Everyone” group, which is listed as a group instead.

You can find the script here:


  • the script uses device based logon, just follow the prompts.
  • don’t forget to first set permissions on all sites for your admin account, see script header for an example
  • requires the PnP module
  • you can exclude specific sites or users from the report if needed, configure siteIgnoreList or principalIgnoreList for that
  • Runtime on an environment with over 1000 sites and millions of objects was about 6 hours. If you environment is too large, contact me and I can perhaps introduce e.g. multi-threading.

Redirecting anything to Onedrive for Business

A while ago I wrote a script that can mount Teams Libraries and then redirect any local folder to them.

In many situations, this solution is a little overkill though, so I’ve also created a second version which simply allows you to redirect any local folder (including variable paths!) to any location in a user’s Onedrive folder.

The configuration is set through the registry, an example file is included.

It can be used as a onetimer or as logonscript, and it can also be used to migrate existing content or create hard links for specific local appdata folders.