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.

https://gitlab.com/Lieben/assortedFunctions/-/blob/master/get-licenseReportByDomain.ps1

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 myupn@lieben.nu

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

Invoke-RestMethod -Method POST -body ($patchBody | convertto-json) -Uri "https://graph.microsoft.com/beta/oauth2PermissionGrants" -Headers @{"Authorization"="Bearer $token"} -ContentType "application/json"

Any future tokens you grab for graph.microsoft.com 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: https://gitlab.com/Lieben/assortedFunctions/-/blob/master/get-SPOPermissions.ps1

Usage

  • 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.