https://lieben.nu/tools/SPNRoleMgr
Helps easily set/modify/remove permissions on Managed Identies, a feature totally lacking from the entra portal 🙂
https://lieben.nu/tools/SPNRoleMgr
Helps easily set/modify/remove permissions on Managed Identies, a feature totally lacking from the entra portal 🙂
So there’s this problem with lots of Microsoft API’s not allowing service principals to call them. I’ve written about this a few times in the past 🙂
These api’s want a user. And a user has to do MFA, right?
Not with this!

When I read Nathan McNulty’s LinkedIn post this morning I got a bit hyped and just HAD to get it working. He has a way to use a stored passkey to log in silently to all admin portals/hidden api’s etc.
The missing part I wanted to solve, is to actually generate that passkey for a given global admin in the tenant.
Took a bit of messing around with how to generate the keys using a virtual authenticator, but it works! Here it is:
https://github.com/jflieben/assortedFunctionsV2/blob/main/New-FidoKey.ps1
So basically:
I should also give an honorary mention to Fabian Bader for the work he did to get us here!

disclaimer: don’t store this stuff where anyone can find it!
disclaimer2: you’ll have to set your fido policy to allow not force attestion or key restrictions
For M365Permissions I wanted to categorize service principals in an actually useful way.
This is what I came up with so far
function get-servicePrincipalType{
Param(
[Parameter(Mandatory=$true)][object]$spn
)
#managed identities are simple :)
if($spn.servicePrincipalType -eq "ManagedIdentity"){
return "ManagedIdentity"
}
#other SPN's can be hosted by us, by Microsoft or by a third party
#Although 9188040d-6c67-4c5b-b112-36a304b66dad is also officially Msft, it contains consumer apps not built or vetted by Microsoft thus we treat it as third party
if($spn.appOwnerOrganizationId -in ("f8cdef31-a31e-4b4a-93e4-5f571e91255a","72f988bf-86f1-41af-91ab-2d7cd011db47","7579c9b7-9fa5-4860-b7ac-742d42053c54")){
return "MicrosoftApplication"
}elseif($spn.appOwnerOrganizationId -eq <YOURTENANTID>){
#this is either a homebrew app or an AI agentic app
if($spn.tags -and ($spn.tags -contains "AgenticApp" -or $spn.tags -contains "AIAgentBuilder")){
return "AiAgent"
}else{
return "InHouseApplication"
}
}else{
return "ThirdPartyApplication"
}
}

There are probably many scenario’s where you’d like to identify which Entra groups contain ‘all users’, ‘all guests’ or a combination (all members+all guests).
In my case, I want to use this in M365Permissions, but also needed it for a Maester test to be more precise. It had to be language and implementation agnostic.
M365Permissions uses this mainly for reports that look at oversharing (e.g. when securing a tenant or implementing copilot). But this could also be useful for red/blue teams or any other tenant analysis tooling.
In M365Permissions, I initially looked at the dynamic rule itself, but this is unreliable. Dynamic rules can contain many additional components and can be ordered or written in many ways or the group may have been created without a dynamic rule through e.g. automation.
So I decided to use another approach!
Just get all tenant users from Graph (counts per type).
Then for a given group, look if it matches one of those counts and return a type. Of course, casting members to users to avoid counting devices and looking up membership recursively 🙂
Function: https://github.com/jflieben/assortedFunctionsV2/blob/main/Get-EntraDynamicGroupType.ps1
If you want to use the M365Permissions module in unattended (or headless) mode, e.g. from a runbook or on a server as scheduled task, you’ll need to create an app registration in Entra with sufficient permissions to scan your tenant.

Install-Module -Name M365Permissions -ForceImport-Module -Name M365PermissionsSet-ScanPermissions -switchToSPNAuth -appName "M365Permissions (AppOnly)"Connect-M365 -ServicePrincipalget-allM365Permissions)


If you also want to include PowerBI in your scans, you’ll have to authorize the service principal.
If you want to run from an automation account, Azure function etc, for now you’ll have to retrieve the .pfx file dynamically and install it before the module loads because the module looks in the local certificate store for a certificate with your tenant ID as subject.
I will consider adding support for Managed Identities in the future to make this simpler, and possibly also add keyvault integration or direct path configuration an option.
When scanning as service principal, you cannot scan:
You’ll see a warning in the logs about this as they’ll automatically be excluded.
to be added in a future version