When a user gets deleted in M365, their OneDrive hangs around for a retention window, and sometimes somebody needs to get into it — legal hold, a manager who suddenly remembers the departed employee "had a spreadsheet," the usual. The SharePoint admin center has a page for finding those deleted OneDrives. It works fine. It is also the only place that information exists.
No Graph endpoint. No SPO cmdlet that covers every case. The admin center UI calls an internal API you're not supposed to touch, and Microsoft's position, as far as I can tell, is that this page is the feature. Every request meant an admin manually clicking through User Profiles, searching a name, and copying a URL out of a modal. We got enough of these requests that the clicking became a line item.
If the browser page is the only interface, then the browser page is the API — it just doesn't know it yet. The core of the system is a PowerShell HTTP listener that drives headless Chrome through Selenium. It signs in through the Microsoft login flow, lands on the User Profiles page, searches for the deleted user, and parses what comes back with regex. Each result gets enriched with profile data from PnP and a deletion timestamp from a SQL database we already maintained.
Scraping a stateful admin page from multiple callers at once is a great way to have a bad afternoon, so a semaphore keeps it to one scraping session at a time, and the driver recovers itself when Chrome inevitably dies. Headless Chrome does not owe you anything.
The listener sits behind a small FastAPI wrapper that handles routing: one endpoint proxies search requests to the Selenium service, another kicks off a PowerShell script that grants a site collection admin on a chosen OneDrive. That wrapper is wired into a Power App through a custom connector and an on-premises data gateway.
The end result: the service desk opens an app, types a name, clicks the right person, and clicks "add admin." No terminal, no admin center, no tribal knowledge about which page under which blade. The scraper does the clicking that we used to do.
No. This should be one Graph call: GET /admin/deletedOneDrives?search= or anything in that shape. The whole contraption — browser automation pretending to be a REST API, glued to a low-code frontend through a data gateway — exists because the data is displayed but not exposed. Until that changes, the robot does the clicking, and honestly the robot is better at it than we were.