Monthly COE ROI Summary Generator
Monthly flow aggregates automation metrics from Dataverse: total flow runs, estimated hours saved (based on avg manual time per process), error rates, active maker count, and new apps deployed. Compiles an executive-friendly ROI summary into Excel and emails the leadership team with key KPIs and trend arrows.
Provided as-is, without warranty of any kind. Review and test each pattern in a non-production environment before deploying it to live automations. See our Terms.
Overview
FlowLibs - Monthly COE ROI Summary Generator is a scheduled cloud flow that runs on the first business day of each month and compiles a leadership-ready ROI report for the Power Platform Center of Excellence (COE). It reads the COE's analytics tables in Dataverse (flow run history, app inventory, maker registry, and a per-process avg-manual-time lookup), aggregates the prior month into a handful of high-signal KPIs — total flow runs, estimated hours saved, error rate, active makers, and new apps deployed — and writes a populated row into a templated Excel ROI tracker stored in SharePoint. The current-month vs prior-month deltas are baked into the email body as trend arrows so executives can read the story without opening the workbook.
The flow is built around the "Dataverse aggregate -> Excel template -> Executive email" reporting pattern: every metric source is an environment variable, every threshold (error rate red/yellow/green) is driven by a config row in Dataverse, and the recipient list is a single env var so the same solution promotes cleanly through Dev / Test / Prod environments.
Use Case
Most COE teams build their monthly ROI deck by hand: someone exports run history from the COE Starter Kit, pivots it in Excel, looks up avg-manual-time per process from a spreadsheet, formats trend arrows, and emails the result to the leadership team. This flow removes that recurring 4-6 hour effort entirely. The COE owns the inputs (the avg-manual-time table and the active-maker tagging in Dataverse); the flow owns the math, the formatting, and the delivery. It is a good fit for organizations that have already deployed the Microsoft COE Starter Kit (or an equivalent Dataverse-based usage tracker) and want a defensible monthly ROI artifact in front of executives without anyone having to remember to build it.
The flow is ideal for teams that:
- Power Platform COE leads who need a recurring, defensible monthly ROI artifact for executive sponsors.
- IT admins running the COE Starter Kit who want the run-history and maker-inventory tables to drive an executive summary automatically.
- Finance / business-ops partners who need an hours-saved estimate they can plug into a yearly automation business case.
- Organizations standing up a fresh COE that want an out-of-the-box ROI cadence on day one, before custom Power BI is built.
Flow Architecture
Recurrence (1st business day of month)
RecurrenceFires on a monthly cadence at 06:00 local time on the 1st of each month. A downstream condition skips weekends so the report always lands on a business day.
Initialize report window variables
Initialize variableComputes varReportMonthStart (first day of prior month, 00:00 UTC) and varReportMonthEnd (first day of current month, 00:00 UTC) using addToTime / startOfMonth so all downstream Dataverse list-rows filters share one consistent window.
Initialize KPI accumulators
Initialize variableInitializes integer / float variables varTotalRuns, varSuccessRuns, varFailedRuns, varHoursSaved, varActiveMakers, varNewApps, and varErrorRate to 0 so the Apply to each blocks can increment them safely.
Dataverse - List flow runs for prior month
ListRowsCalls Microsoft Dataverse ListRows against the COE flow-run-history table (configurable via flowlibs_FlowRunHistoryTable) with a $filter on createdon ge varReportMonthStart and createdon lt varReportMonthEnd. Pagination is on with threshold flowlibs_MaxRunsPerMonth so the action can fan over hundreds of thousands of run rows.
Dataverse - List avg-manual-time lookup
ListRowsReads the per-process avg-manual-time lookup table (flowlibs_AvgManualTimeTable) once into a select-shaped array of { flowName, avgMinutesPerRun } so the Apply to each loop can resolve hours-saved without re-querying Dataverse for every run.
Apply to each flow run
Apply to eachFor each item in the flow-run-history result: increment varTotalRuns by 1, branch on the run's status column to increment varSuccessRuns or varFailedRuns, and (on success) add avgMinutesPerRun from the lookup array divided by 60 into varHoursSaved using a Filter array + first() pattern. Concurrency on Apply to each is set to 20 to keep the run inside the 30-minute action timeout for large COEs.
Environment Variables
| Schema name | Type | Default | Description |
|---|---|---|---|
| flowlibs_FlowRunHistoryTable | String | flowlibs_flowrunhistories | Logical collection name of the Dataverse table that holds one row per flow run (e.g. from the COE Starter Kit run-history capture). Used as the source of total runs, success / failure splits, and hours-saved. |
| flowlibs_AvgManualTimeTable | String | flowlibs_avgmanualtimes | Logical collection name of the Dataverse table that maps each flow / process to its estimated avg manual minutes per run. Populated by the COE team; the flow only reads. |
| flowlibs_MakerRegistryTable | String | flowlibs_makers | Logical collection name of the COE maker registry. The flow counts distinct rows with a lastactivityon date in the report month to compute Active Makers. |
| flowlibs_AppInventoryTable | String | flowlibs_apps | Logical collection name of the COE app inventory table. The flow counts rows whose createdon falls inside the report month to compute New Apps Deployed. |
| flowlibs_RoiSnapshotTable | String | flowlibs_roisnapshots | Logical collection name of the table where each monthly KPI bundle is written. Read at the start of the next run to compute trend arrows. |
| flowlibs_RoiTrackerWorkbookPath | String | /sites/<configure>/Shared Documents/COE/PowerPlatform_ROI_Tracker.xlsx | Server-relative path to the Excel workbook that holds the ROI tracker table. The site collection should be your-tenant.sharepoint.com. |
| flowlibs_RoiTrackerTable |
Connectors & Connections
| Connector | API name | Actions used |
|---|---|---|
| Microsoft Dataverse | shared_commondataserviceforapps | ListRows (Reads flow-run-history, avg-manual-time, maker registry, app inventory, and prior-month ROI snapshot) AddNewRow (Writes the monthly ROI snapshot row) |
| Excel Online (Business) | shared_excelonlinebusiness | AddRowV2 (Appends the monthly KPI row to the ROI tracker workbook table) GetFileContentV2 (Fetches the updated workbook so it can be attached to the executive email) |
| Office 365 Outlook | shared_office365 | SendEmailV2 (Sends the monthly ROI summary to the leadership distribution list and the threshold-breach alerts to the admin mailbox) |
Note — All connections are referenced as solution connection references; the flow is portable between environments as long as a connection is mapped at import time.
Customization Guide
Almost every realistic variant of this flow can be implemented by changing environment variable values. A few cases require small edits inside the flow definition — those are called out explicitly below.
- Add a hard-dollar savings KPI
- Add a flowlibs_BlendedHourlyRate environment variable (e.g. 75) and a Compose action that multiplies varHoursSaved by it. Bind the dollar figure into the email body and the Excel row so executives see hours and dollars side by side.
- Switch the report cadence to weekly
- Edit the Recurrence trigger to Weekly on Mondays at 06:00 and change the date window initialization to addDays(utcNow(), -7) -> utcNow(). The ROI snapshot table grows faster - add a retention policy that deletes rows older than 18 months.
- Send a Teams adaptive card instead of an email
- Replace the Outlook 365 SendEmailV2 step with a Microsoft Teams 'Post adaptive card in a chat or channel' action. Render the same KPIs in an adaptive card template and post into the leadership channel; keep the alert email branch as a safety net.
- Drive Power BI instead of Excel
- Drop the Excel AddRow / GetFileContent steps and let the ROI snapshot Dataverse table be the system of record. Build a Power BI report on flowlibs_roisnapshots and embed it; the flow keeps writing one row per month, Power BI handles the visuals.
- Per-business-unit breakouts
- Add a flowlibs_BusinessUnitColumn env var pointing at the column in flow-run-history that tags each run with a BU. Wrap the aggregation steps in an Apply to each over the distinct BU values and emit one Excel row + one email section per BU - useful in larger orgs where leadership wants per-team ROI.
- Tighten the avg-manual-time model
- Today every successful run earns its flow's avgMinutesPerRun. For long-running approval flows you may want to credit only the human-completed runs or apply a confidence factor per process. Add a flowlibs_avgmanualtime row column like 'confidenceFactor' (0.0 - 1.0) and multiply it in the hours-saved increment.
Key Expressions
The flow is intentionally light on Power Fx / WDL gymnastics — the heaviest expressions are the branch-name concatenation and the approval outcome check. They are listed below in the order they appear in the flow.
EXPR.01Report month start (UTC)
Used to initialize varReportMonthStart - first day of the prior month at 00:00 UTC.
EXPR.02Report month end (UTC)
Used to initialize varReportMonthEnd - first day of the current month at 00:00 UTC. Combined with the start above it forms a half-open interval [start, end) for all Dataverse filters.
EXPR.03Dataverse $filter for runs in report month
Built into the ListRows Filter rows parameter so only prior-month runs are returned.
EXPR.04Hours-saved increment per successful run
Inside Apply to each: resolves the run's flowName against the cached avg-manual-time array via Filter array + first(), divides minutes by 60, and accumulates into varHoursSaved.
EXPR.05Error rate, zero-run safe
Computes percent error rate without dividing by zero in months where no runs occurred.
EXPR.06Trend arrow glyph
Pattern used per KPI to choose the trend arrow. For error rate the comparison is inverted so 'down' counts as the positive direction.
EXPR.07Threshold-breach condition
Used in the If condition - fires the alert email if error rate is at or above the configured red line, or hours-saved falls below its floor.
Comments
Sign in to join the conversation.
Sign inNo comments yet. Be the first to share your experience with this flow.