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:
We often still have legacy apps around. First advice is always; get rid of them 🙂
But sometimes, that’s not the option the customer wants to pay for, so alternatives need to be researched. Commercial tools like IAMCloud Drive Mapper work really well in exposing Teams/Sharepoint as driveletters on endpoints (for a price).
But free solutions are limited to automapping using GPO’s or letting users sync each team they need access to manually. This often causes issues on multiple fronts that I’m sure you’ve already experienced if you’re reading this.
So here’s an alternative to try! M365AutoLink is a PowerShell script you can execute on the user’s device. It’ll try to use SSO and then:
check all sites the user has access to (including Teams)
filter sites you do and don’t want
create a folder in their onedrive if it doesn’t exist yet (you can decide how to name it)
add all these sites as shortcuts in this folder
remove any shortcuts the user no longer has access to
And since Onedrive syncs these links, any legacy apps on the user’s device can now also directly access Teams/Sharepoint, without syncing down the entire library or using drive mappings!
Can’t delete a sharepoint site that used to have a group attached to it, but the group has long since been deleted?
Apparently, the sharepoint admin center is unable to handle this scenario, and just throws a “could not be deleted” error. Using fiddler the only extra info I could get was an underlying 500 Internal Server Error Microsoft.Office.Server.Directory.DirectoryObjectNotFoundException was thrown
Obviously that directory object not found means the office 365 group. Fora seem to point to MS support for manual fixes, but since Microsoft Support has rarely been a pleasant experience for me I went over the PS module for SPO and found a MUCH faster fix, hope it also works for you!
start PowerShell 5 (don’t use a newer version until the module supports it)
install-module Microsoft.Online.SharePoint.PowerShell -force -scope currentuser (unless you already have the module installed)
A customer wanted to know how many hours on average their devices were being used, without modifying their configuration.
This to help target replacements, upgrades, #desks per location, #shared devices per location etc.
At first I figured it’d be a simple task, just getting the right event ID’s from the local eventlog, but no, that required additional auditing to be enabled and how would the data flow to PowerBI?
The next attempted data source was much better; Defender Advanced Hunting. Especially the DeviceLogonEvents table, which contains cached and interactive logons to all monitored devices.
Using logon events, we can get an idea of a device’s relative use in the fleet. It won’t be exact as measuring the time between pure lock/unlock/signin/signout events.
This was fine for the customer’s purpose and checking their data the number of daily logon events (7-9) was actually much higher than the expected 2-3 per device per day (at least on Windows 11, our target OS).
Getting that data into PowerBI turned out to be even easier 🙂
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"
}
}