How to Attribute Microsoft Fabric CU Consumption to Power BI Users

One question I hear regularly is: Who is actually consuming the Capacity Units (CUs) on a Microsoft Fabric capacity?

The Fabric Capacity Metrics app is a great starting point for understanding capacity utilization. However, when we inspect the consumption, it’s focusing more on Workspace and Item level rather then on user level. The drillthrough option shows the CU consumption by user, but only for a specific 30-second Timepoint. This makes it difficult to connect CU consumption to the user for example for the whole day, week, or even month.

The good news is that Fabric provides the information required to solve this. Capacity Operation Events contain the metered CU consumption, while Workspace Monitoring (both Public Preview as of writing this blog post) provides semantic-model telemetry, including user information. The OperationId connects both sources.

In this blog post, I will walk you through the overall approach. The complete setup, production-ready KQL query, validation checklist, and screenshots are available in my GitHub repository.

This solution is intended for capacity optimization, workload troubleshooting, and proactive best-practice discussions. It should not be used as an employee performance score.

Why Is User Attribution Difficult?

Capacity Operation Events tell us exactly how much capacity an operation consumed. For example, they include:

  • Capacity, workspace, and item details
  • Operation name and timing
  • CapacityUnitMs
  • OperationId
  • Raw identity information

The challenge is that the identity recorded for a Power BI semantic-model operation can be:

identityType = FabricService
identityValue = Fabric Service

This identity is valid for the metered Fabric operation, but it does not necessarily identify the person who opened a report or interacted with a visual.

Workspace Monitoring provides the other half of the picture. Its SemanticModelLogs table includes the semantic-model request ID together with fields such as User and ExecutingUser.

The central principle is therefore straightforward: Capacity Operation Events.operationId = SemanticModelLogs.OperationId

An exact match lets us enrich the metered operation with semantic-model and user context without estimating attribution from timestamps.

Step 1: Create a Monitoring Workspace

Start by creating a dedicated Fabric workspace for the monitoring solution. This workspace can contain the Eventstream, Eventhouse, KQL queries, semantic model, and Power BI report used to analyze the results.

Use your organization’s naming, access, retention, and sensitivity conventions. The collected telemetry can contain user identifiers and query text, so access should follow the principle of least privilege.

Step 2: Configure Capacity Operation Events

In the Fabric Real-Time hub, create an Eventstream for the following event type:

Microsoft.Fabric.CapacityOperationEvents.Operation

Select the Fabric capacity you want to monitor as the event scope. You need to be a capacity administrator to configure this source.

It is important to use the operation event rather than only the capacity summary event. Summary events are useful for capacity-level health and reconciliation, but they do not provide the operation-level detail required for deterministic user attribution.

Step 3: Store the Events in an Eventhouse

Add an Eventhouse destination to the Eventstream and use direct ingestion. Create a raw table, for example rawData, and retain the original CloudEvent envelope.

Once the Eventstream has been published, verify that events arrive:

rawData
| take 10

The CloudEvent data property contains the Capacity Operation Event payload. For this Power BI scenario, we retain semantic-model items represented as Dataset:

rawData
| extend Envelope = todynamic(Data)
| where tostring(Envelope.type) == "Microsoft.Fabric.CapacityOperationEvents.Operation"
| extend Payload = todynamic(Envelope.data)
| where tostring(Payload.itemKind) == "Dataset"

Step 4: Enable Workspace Monitoring

Workspace Monitoring must be enabled in every workspace containing semantic models that you want to include. Enabling it only in the central monitoring workspace does not collect telemetry from other workspaces.

Open the settings of each source workspace, select Monitoring, and add an Eventhouse. Fabric creates a managed, read-only monitoring Eventhouse and KQL database.

After generating some report activity, confirm that the SemanticModelLogs table receives records:

SemanticModelLogs
| take 10

Relevant columns include OperationIdUserExecutingUser, workspace and item information, operation details, duration, and CPU time.

Note that CPU time and duration are not Fabric CU. The metered CU value must continue to come from the Capacity Operation Event.

Step 5: Correlate Both Sources

Normalize the operation IDs from both sources and join them by exact equality. A simplified version looks like this:

CapacityEvents
| join kind=leftouter SemanticOperationMap on NormalizedOperationId
| extend
CUSeconds = CapacityUnitMs / 1000.0,
IsDeterministicallyAttributed = isnotempty(RawSemanticOperationId)

I recommend starting from the capacity events and using a leftouter join. This ensures that metered CU does not disappear simply because matching semantic-model telemetry is unavailable.

Do not join the raw tables without checking their grain. One semantic-model request can produce multiple log records. If both sides contain multiple rows for the same OperationId, the join can multiply the CU consumption.

The production query in the GitHub repository deduplicates both sources, preserves the raw identities, and keeps unmatched operations visible. In a production environment, I recommend working with Policies to to normalize the rawData table.

Step 6: Validate the Attribution

Before using the result for reporting, validate the approach with controlled tests. For example:

  • Open one report as a known user.
  • Change one or more slicers.
  • Repeat the test with two simultaneous users.
  • Test cached report interactions.
  • Run a semantic-model refresh.

For each test, compare the operationId from the capacity event with the OperationId from SemanticModelLogs. Also reconcile the total metered CU before and after the join.

Only an exact normalized identifier match should be treated as deterministic attribution. Missing matches must remain classified as service, system, service principal, or unattributed according to the available evidence.

Step 7: Build the Power BI Report

Once the correlated data has been validated, it can be exposed through a Power BI semantic model and report. A useful analysis path is:

User -> Workspace -> Semantic model -> Operation -> CU consumption

The report can answer questions such as:

  • Which users generated the most CU consumption during a selected period?
  • Which workspaces and semantic models were involved?
  • Which operations caused the consumption?
  • How much consumption could be attributed deterministically?
  • How much remains service, system, or unattributed activity?

Keep the unmatched consumption in the model. Removing it would understate the total CU usage and give an incomplete view of the capacity.

Can This Work for Other Fabric Items?

This implementation focuses specifically on Power BI semantic models. However, the same general pattern can be extended to other Fabric item types when suitable Workspace Monitoring telemetry and a deterministic correlation key are available.

To adapt it, change the itemKind filter in the Capacity Operation Events query and use the corresponding table from the Workspace Monitoring Eventhouse instead of SemanticModelLogs. You must validate the identifier, table grain, operation coverage, and join cardinality for that workload before treating the result as attributed.

Final Thoughts

Capacity Operation Events provide the exact metered CU, but their raw identity does not always represent the person who initiated a Power BI request. Workspace Monitoring adds the semantic-model user context, and OperationId gives us a deterministic way to connect both sources.

This produces a much stronger result than timestamp matching, report-view counts, or proportional allocation. Just as importantly, it keeps service and unattributed consumption visible instead of inventing a user attribution where none can be proven.

Have you already tried correlating Capacity Operation Events with Workspace Monitoring in your Fabric environment? I would love to hear about your results and the workloads you are monitoring in the comments.

If you’re interested in the files used in this blog check out my GitHub repo https://github.com/PBI-Guy/blog

Please let me know if this post was helpful and give me some feedback. Also feel free to contact me if you have any questions.

Leave a comment