Power
Platform Administration
Finding & Reassigning Abandoned Flows
A complete, beginner-friendly guide using PowerShell
March 2025 • Power Automate •
PowerShell • Microsoft Graph
Introduction
If you manage a Microsoft Power
Platform environment — even a small one — you have almost certainly run into
this situation: flows that nobody owns, flows sitting in a broken state for
months, flows owned by employees who have long since left the company. These
are what we call abandoned flows.
This blog post walks you through
exactly what abandoned flows are, why they are a problem, how to find them
using PowerShell, and how to take ownership of them so you can fix or clean
them up. No prior PowerShell experience is required — every step is explained
in plain English.
What Are Abandoned Flows?
A Power Automate flow is a set of
automated steps that runs on a trigger (like a new email, a form submission, or
a schedule). Flows are owned by users in your organisation. An abandoned flow
is any flow that has effectively become ownerless or unmanaged. This typically
happens in one of four ways:
|
Category |
Description |
|
Orphaned |
The original owner's account
was deleted or disabled in Entra ID (Azure AD). The flow still exists but has
no active owner. |
|
Suspended |
Power Platform automatically
suspends a flow when its connections break (e.g. a password change, licence
removal, or connector expiry). |
|
Stopped |
Someone turned the flow off
and it has been sitting idle with no modification for a long time. |
|
Stale |
The flow is technically on,
but has not been modified or checked in 90+ days and may be running on
outdated logic. |
|
💡 Why does this matter? Abandoned flows can quietly fail, consume
API quota, hold licences, or contain sensitive data with no one watching over
them. In an audit or compliance review, they are a red flag. |
Before You Start — Prerequisites
You will need the following before
running any of the scripts in this guide:
•
Power Platform Admin role
or Global Admin in your Microsoft 365 tenant
•
PowerShell 5.1 or
PowerShell 7+ installed on your machine
•
Internet access to reach
the Power Platform admin APIs
Install the required PowerShell
modules by running these commands once:
|
# Power Platform Admin
module Install-Module -Name
Microsoft.PowerApps.Administration.PowerShell -Force -AllowClobber # Microsoft Graph (for
looking up Entra ID users) Install-Module
Microsoft.Graph -Scope CurrentUser -Force |
|
⚠️ You only need to install these modules
once. After that, you can just run the scripts directly. |
Step 1 — Connect to Power Platform
Before the scripts can talk to
your tenant, you must authenticate. Run the following:
|
# Sign in to Power
Platform Admin Add-PowerAppsAccount # Sign in to Microsoft
Graph (for user lookups) Connect-MgGraph -Scopes
'User.Read.All' |
A browser window will open asking
you to sign in with your admin account. Once signed in, you are ready to
proceed.
Step 2 — Find All Abandoned Flows
The script below loops through
every environment in your tenant, collects all flows, and flags them as
abandoned if they are suspended, stopped, or have not been modified in 90 days.
|
# How many days without
modification counts as stale? $staleDays = 90 $staleCutoff =
(Get-Date).AddDays(-$staleDays) # Collect all flows
across all environments $environments =
Get-AdminPowerAppEnvironment $abandonedFlows = @() foreach ($env in
$environments) { Write-Host "Scanning:
$($env.DisplayName)" -ForegroundColor Cyan $flows = Get-AdminFlow -EnvironmentName
$env.EnvironmentName foreach ($flow in $flows) { $state = $flow.Internal.properties.state $lastMod = $flow.LastModifiedTime if ($state -in
@("Suspended","Stopped") -or $lastMod -lt $staleCutoff) { $abandonedFlows +=
[PSCustomObject]@{ DisplayName =
$flow.DisplayName FlowName = $flow.FlowName Environment =
$env.EnvironmentName EnvDisplay = $env.DisplayName State = $state LastMod = $lastMod Owner = $flow.CreatedBy.userPrincipalName } } } } Write-Host "Found
$($abandonedFlows.Count) abandoned flows" -ForegroundColor Yellow $abandonedFlows |
Format-Table DisplayName, EnvDisplay, State, Owner -AutoSize |
At this point, you will see a
table printed in your PowerShell window listing every abandoned flow. Take a
moment to review it before moving on.
Bonus: Find Flows Owned by Deleted Users
If you also want to catch flows
whose owner account no longer exists in Entra ID (for example, ex-employees),
add this extra check:
|
# Get all active user
UPNs from Entra ID $activeUsers =
Get-MgUser -All | Select-Object -ExpandProperty UserPrincipalName # Filter flows where
owner is not in active user list $orphanedFlows =
$abandonedFlows | Where-Object { $_.Owner -notin $activeUsers -and
$_.Owner -ne $null } Write-Host
"Orphaned (owner deleted): $($orphanedFlows.Count)" $orphanedFlows |
Format-Table DisplayName, Owner, EnvDisplay -AutoSize |
Step 3 — Assign Abandoned Flows to Yourself
Now that you have a list, you can
assign yourself as a co-owner (CanEdit) of each abandoned flow. This gives you
full control to fix, update, or delete them.
|
📝 Note: Power Platform does not support
changing the original creator of a flow. What you can do is add yourself as a
co-owner with CanEdit permission, which gives you the same level of control
as the original owner. |
First, get your own Object ID.
This is a unique identifier for your account in Entra ID. The simplest way is:
|
# Option A: Get your
Object ID from the Power Platform session (no extra module needed) $myObjectId =
(Get-PowerAppsAccount).UserId Write-Host "Your
Object ID: $myObjectId" # Option B: Look it up
via Microsoft Graph $myUPN = "your.email@company.com" $myObjectId =
(Get-MgUser -UserId $myUPN).Id |
Now run the reassignment loop.
Each flow is processed in its own background job with a 30-second timeout to
prevent the script from hanging on a single slow API call:
|
$success = @() $failed = @() $i = 0 foreach ($flow in
$abandonedFlows) { $i++ Write-Host
"[$i/$($abandonedFlows.Count)] $($flow.DisplayName)..." -NoNewline try { $job = Start-Job -ScriptBlock { param($envName, $flowName, $oid) Add-PowerAppsAccount Set-AdminFlowOwnerRole ` -EnvironmentName $envName ` -FlowName $flowName ` -RoleName CanEdit ` -PrincipalType User ` -PrincipalObjectId $oid } -ArgumentList $flow.Environment,
$flow.FlowName, $myObjectId $done = Wait-Job $job -Timeout 30 if ($done) { Receive-Job $job Write-Host " OK"
-ForegroundColor Green $success += $flow } else { Stop-Job $job Write-Host " TIMEOUT"
-ForegroundColor Yellow $failed += $flow } Remove-Job $job -Force } catch { Write-Host " ERROR: $_"
-ForegroundColor Red $failed += $flow } # Pause every 10 flows to avoid API
throttling if ($i % 10 -eq 0) { Start-Sleep -Seconds
5 } } Write-Host "" Write-Host "===
Done ===" -ForegroundColor Cyan Write-Host
"Assigned : $($success.Count)" -ForegroundColor Green Write-Host
"Failed :
$($failed.Count)"
-ForegroundColor Red |
Step 4 — Export a Report
It is good practice to save a
record of what was changed. Export the results to CSV files so you have an
audit trail:
|
$date = Get-Date -Format
"yyyyMMdd" # All abandoned flows
found $abandonedFlows |
Export-Csv "AbandonedFlows_$date.csv" -NoTypeInformation # Flows successfully
reassigned $success | Export-Csv
"Reassigned_$date.csv"
-NoTypeInformation # Flows that failed or
timed out $failed | Export-Csv
"Failed_$date.csv"
-NoTypeInformation Write-Host "Reports
saved to current directory." |
Troubleshooting Common Issues
|
Category |
Description |
|
Script hangs for 10+ min |
The cmdlet is stuck on a
single API call. Use the Start-Job / Wait-Job pattern (Step 3) with a
30-second timeout to prevent this. |
|
Get-AzureADUser not found |
The AzureAD module is
deprecated. Use Get-MgUser from the Microsoft.Graph module instead. |
|
Access denied errors |
Your account needs Power
Platform Admin or Global Admin. Check your role in the Microsoft 365 Admin
Center. |
|
Set-AdminFlowOwnerRole no
output |
This is normal — the cmdlet
returns nothing on success. Check $success count after the loop. |
|
Throttling / 429 errors |
Add Start-Sleep -Seconds 5
every 10 flows (already included in Step 3 script). |
Best Practices — Keep Your Environment Clean
Now that you know how to find and
claim abandoned flows, here are some habits to prevent the problem from
building up again:
•
Run this script monthly as
part of your regular admin routine
•
Set up a DLP (Data Loss
Prevention) policy to limit which connectors flows can use
•
Before offboarding a user,
reassign their flows to a service account or a team owner
•
Use solutions to package
flows, which makes ownership and lifecycle management easier
•
Enable environment-level
auditing so you get alerts when flows enter a suspended state
Quick Reference Summary
|
Category |
Description |
|
Step 1 |
Install modules:
Microsoft.PowerApps.Administration.PowerShell and Microsoft.Graph |
|
Step 2 |
Connect: Add-PowerAppsAccount
and Connect-MgGraph |
|
Step 3 |
Find abandoned flows: loop
environments, filter by state and last modified date |
|
Step 4 |
Get your Object ID:
(Get-PowerAppsAccount).UserId |
|
Step 5 |
Reassign with timeout:
Set-AdminFlowOwnerRole inside Start-Job / Wait-Job |
|
Step 6 |
Export results to CSV for
audit trail |
Conclusion
Abandoned flows are a silent
problem in most Power Platform tenants — they go unnoticed until something
breaks or an auditor asks questions. With the scripts and steps in this guide,
you can find every abandoned flow in your environment, understand why it was
abandoned, and take ownership of it in minutes.
The entire process uses built-in
Microsoft modules, requires no third-party tools, and can be scheduled to run
automatically. Start with a single test flow, confirm it works, then scale up
to your full environment.
Happy automating!
-----------------------------------------------------------------------------------------------------------
# Install module if needed
Install-Module -Name
Microsoft.PowerApps.Administration.PowerShell
# Connect
Add-PowerAppsAccount
# Get the flow (you need
Environment Name + Flow ID)
Get-AdminFlow -EnvironmentName
"Default-<tenantid>" | Where-Object { $_.DisplayName -like
"*FlowName*" }
# Assign
Set-AdminFlowOwnerRole `
-EnvironmentName $flow.Environment `
-FlowName $flow.FlowName `
-RoleName CanEdit `
-PrincipalType User `
-PrincipalObjectId $newOwnerOID
-----------------------------------------------------------------------------------------------------------