Conversation
Co-authored-by: MSBrett <24294904+MSBrett@users.noreply.github.com>
Co-authored-by: MSBrett <24294904+MSBrett@users.noreply.github.com>
flanakin
left a comment
There was a problem hiding this comment.
🤖 [AI][Claude] PR Review
Summary: This documentation-only PR adds helpful mitigation guidance for Azure Resource Graph payload size limit errors. The expanded error documentation is a significant improvement. A couple of items to address before merging.
⚠️ Should fix (1)
- Changelog entries are placed under "Implementing FinOps guide v14" but these are Power BI report documentation changes. They should be in their own section (e.g., a new
### [Power BI reports](power-bi/reports.md) v14section or a standalone### Documentation improvementssection). Entries also missing issue link#1768.
💡 Suggestions (1)
- "small- and medium-sized" uses a suspended hyphen that's grammatically correct but unusual in Microsoft docs — consider "small and medium-sized" for readability.
The v15 Power BI reports changelog entry and ms.date bump were edited after staging during merge conflict resolution and got left out of the merge commit. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 30 out of 43 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
src/power-bi/kql/Shared.Dataset/definition/expressions.tmdl:158
tostring(split(id, '/')[2])only yields a real subscription ID for IDs that start with/subscriptions/{guid}/.... For tenant- or management-group-scoped policy definitions, it produces incorrect values (for example,/providers/Microsoft.Authorization/...becomesMicrosoft.Authorization). Use an extract that returns null when the ID is not subscription-scoped.
subscriptionId = tostring(split(id, '/')[2]),
src/power-bi/storage/Shared.Dataset/definition/expressions.tmdl:565
tostring(split(id, '/')[2])only yields a real subscription ID for IDs that start with/subscriptions/{guid}/.... For tenant- or management-group-scoped policy definitions, it produces incorrect values (for example,/providers/Microsoft.Authorization/...becomesMicrosoft.Authorization). Use an extract that returns null when the ID is not subscription-scoped.
subscriptionId = tostring(split(id, '/')[2]),
src/power-bi/kql/Shared.Dataset/definition/tables/PolicyAssignments.tmdl:372
tostring(split(id, '/')[2])yields incorrect values for non-subscription-scoped IDs (tenant or management group scope). Use an extract that only returns a subscription ID when the resource ID contains/subscriptions/{guid}/...so you don't populate bogus IDs likeMicrosoft.Authorization.
| extend subscriptionId = tostring(split(id, '/')[2])
src/power-bi/kql/Shared.Dataset/definition/tables/PolicyStates.tmdl:347
tostring(split(id, '/')[2])yields incorrect values for non-subscription-scoped IDs (tenant or management group scope). Use an extract that only returns a subscription ID when the resource ID contains/subscriptions/{guid}/...so you don't populate bogus IDs likeMicrosoft.PolicyInsights.
| extend subscriptionId = tostring(split(id, '/')[2])
Confirmed as a real bug in code this PR introduced (this One residual limitation this fix does not solve, disclosed for transparency: |
…urces Copilot's automated review flagged that tostring(split(id, '/')[2]) produces misleading values (e.g. Microsoft.Authorization, Microsoft.PolicyInsights) instead of null/empty for tenant- or management-group-scoped policy definitions, assignments, and states. This subscriptionId derivation was added by this PR to support ARG subscription batching. Guard the split so non-subscription-scoped IDs now yield an empty value instead of a bogus one, in both kql and storage copies of expressions.tmdl and in PolicyAssignments.tmdl / PolicyStates.tmdl (storage copies are symlinks to the kql originals). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
MSBrett
left a comment
There was a problem hiding this comment.
- High — subscription batching drops built-in and management-group Azure Policy data
PolicyDefinitions and PolicyAssignments derive subscriptionId from split(id, '/')[2], then call ftk_QueryARG, which appends where subscriptionId in ('<subscription GUIDs>'). Built-in definition IDs use /providers/Microsoft.Authorization/policyDefinitions/<id>, so the derived value is Microsoft.Authorization; management-group IDs similarly derive Microsoft.Management. Neither can match a subscription GUID, so those rows are removed. Keep tenant/management-group records outside subscription batching and union them with batched subscription-scope records, or apply the filter only to subscription-scoped IDs. Add regression coverage for built-in, management-group, and subscription IDs in both dataset copies.
- Medium — the reported NSG payload case remains insufficiently bounded and unproven
The issue specifically identifies the NSG query and its mv-expand properties.securityRules as exceeding 16 MB, but NetworkSecurityGroups.tmdl retains that expansion and uses ftk_QueryARG(query, 20). The documented default therefore creates 2,000-subscription batches, and the documented integer minimum of 1 still produces 20 subscriptions. No measurement or validation demonstrates that this stays below ARG’s 16 MB limit. Remove or reduce the multiplier, or expose a true per-table batch setting, and validate the selected bound against a large NSG-rule tenant.
- Medium — required validation evidence for the core M change is absent
There is no Power BI test, .tmdl/M CI validation, or reported manual refresh for the new batching behavior. The PR only reports an M-spec trace, while the repository’s build-and-test guidance says Power BI reports require manual build/verification and that manual verification is always expected. Record a Power BI Desktop refresh against a representative large tenant, including NSG expansion and policy scopes, and add a deterministic boundary check for batching and ID scoping if automated Power BI validation remains unavailable.
… size - ftk_QueryARG: add optional includeUnscoped param that unions in one unbatched Azure Resource Graph query for rows whose derived subscriptionId is blank (tenant- and management-group-scoped policy resources), since the per-batch "subscriptionId in (...)" filter can never match them and they were being silently dropped from every batch. - PolicyDefinitions (kql + storage), PolicyAssignments, PolicyStates: opt into includeUnscoped so built-in policy definitions and management-group-scoped assignments/states are no longer missing from the Governance report. - NetworkSecurityGroups: remove the 20x batch multiplier (2000 subs/batch). It was the largest multiplier of any table despite this table's mv-expand of security rules being the highest per-row payload expansion in the dataset and the original motivating case for ARG batching (changelog #1540). Falls back to the 100-subs/batch default. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Thanks for the detailed review, @MSBrett. Addressed in 1. High: subscription batching drops built-in and management-group policy data Confirmed and fixed. I did not add a union of every possible non-subscription scope pattern beyond "blank subscriptionId" - the fix relies on the existing 2. Medium: NSG payload case insufficiently bounded Agreed the I could not validate this bound against a live large tenant. This sandboxed environment has no Azure Resource Graph access, no Power BI Desktop, and (confirmed by checking 3. Medium: validation evidence for the core M change is absent This is a fair gap I can't close from here for the same reason as #2 - there's no automated Power BI/M test harness in this repo ( |
flanakin
left a comment
There was a problem hiding this comment.
🤖 [AI][Claude] PR Review
Summary: The batching approach is sound and the hardening from earlier review rounds (batch-size validation, try...otherwise on the subscription list, restored RI filter) is solid. Three items need attention before merge — most importantly registering the two new functions in the Power BI build script, and the direction of the batchMultiplier argument. A few items are confirmation requests rather than defects.
🚫 Blockers (3)
Build-PowerBI.ps1needsftk_QueryARGandftk_ARGBatchSizeadded to the Governance and WorkloadOptimizationExpressionslists so they survive the build filter.batchMultiplierscales batches up rather than down, so the heaviest tables issue the largest requests.- Confirm the storage
.tmdl→ symlink conversion is intentional and works on Windows + the build scripts.
⚠️ Should fix (3)
AdvisorRecommendationschangedresultTruncatedfromtruetofalse.- In-query
subscriptionIdfiltering trims results but doesn't reduce Resource Graph's scan scope. - Confirm the
AdvisorReservationRecommendationscolumn/typing additions are intended to ship in this PR.
💡 Suggestions (2)
- Reference the default batch size instead of duplicating the
100literal. - Apply the same
try...otherwiseguard to theUnscopedSourcequery.
…entries, batch size inversion, and Advisor sort/type regressions - Revert 13 storage/*.tmdl symlinks to real duplicated files; git checks out symlinks as literal path-text on Windows without core.symlinks=true, which corrupted local builds and Power BI Desktop sessions for most Windows contributors. - Add ftk_QueryARG and ftk_ARGBatchSize to the Governance and WorkloadOptimization Expressions allowlists in Build-PowerBI.ps1; both were being stripped from the shipped .pbit even though ~10 tables call ftk_QueryARG, which would break refresh in the built report. - Reduce batchMultiplier on VirtualMachines (3 mv-expand blocks, widest row) from 5 to none, and on AdvisorRecommendations, AdvisorReservationRecommendations, and SqlDatabases from 5 to 2, so batch size roughly tracks per-row payload weight instead of being inverted (heaviest tables previously got the largest batches). - Restore the lookbackPeriod Int64.Type transform in AdvisorReservationRecommendations that this PR had dropped. - Re-sort AdvisorRecommendations/AdvisorReservationRecommendations by SortOrder after Table.Combine, since combining per-batch results only preserves sort order within each batch, not globally. - Wrap ftk_QueryARG's UnscopedSource query in try...otherwise so a transient ARG failure on the optional unscoped-rows query degrades to batched-only results instead of failing the whole table. - Extract defaultBatchSize as a named constant and add a comment explaining the in-query subscription filter vs request-level scope tradeoff. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
RolandKrummenacher
left a comment
There was a problem hiding this comment.
Overview
The batching approach is sound and ftk_QueryARG is correctly written M. My concerns are about failure modes, request volume, and scope rather than the core idea.
I've left inline comments on the specific lines. Summary of what I think blocks merge:
- A failed subscription enumeration silently produces an empty report rather than an error — strictly worse than the error it replaces.
- ~6x more ARG requests with no retry, in exactly the large tenants this targets. Trades a deterministic 16 MB error for a nondeterministic 429.
- Resource-group-scoped readers lose data, because they can't see the subscription containers the batch list is built from.
And a scope request: this is at least three PRs. Beyond batching it rewrites AdvisorReservationRecommendations' schema (+16 columns, lookbackPeriod string→int64, coalesce(…,0) on savings amounts, a new recommendedActions fallback), switches properties['category'] to properties.category, converges the entire storage dataset onto the kql dataset's content, and churns several hundred lineageTag GUIDs. Those may all be wanted, but they're unreviewable next to the batching change and will make bisecting a future refresh regression painful. I'd like the AdvisorReservationRecommendations schema work split out.
Note that every finding on src/power-bi/kql/.../expressions.tmdl applies identically to the duplicated copy in src/power-bi/storage/.../expressions.tmdl — I only commented once per issue to keep this readable.
Verified as fine
So these don't come up again: changelog placement is correct (v15 is still 15.0.0-dev.0; the "Released June 2026" header is a placeholder). The errors.md anchor resolves. Every batched query either uses extend-only against resources/advisorresources/policyResources — which carry subscriptionId natively — or explicitly projects it, so the appended filter always resolves; I checked all twelve. The broken symlinks flagged earlier are gone. ManagementGroups correctly stays unbatched. {0..-1} is a valid empty range in M, so the zero-subscription path doesn't throw.
Testing
@MSBrett's note that none of this ran against a live tenant is the crux. Pester passes but never touches these files, and findings 1–3 are all runtime-only failure modes that no static check would catch. A manual refresh against a genuinely multi-batch tenant (>100 subscriptions, ideally >500) is the real gate here — specifically watching for 429s during concurrent partition refresh, and confirming the tables aren't silently empty.
- Raise an explicit error instead of silently returning empty results when the subscription-list query fails or returns an unusable shape - Add retry with backoff and jitter around all AzureResourceGraph.Query calls in ftk_QueryARG (kql and storage datasets) - Document the resourcecontainers-vs-resources RBAC scope mismatch in code comments and in governance.md / workload-optimization.md - Document why ftk_DemoFilter() intentionally does not apply to the unscoped (tenant/management-group) query path - Remove unreachable Value.Is checks that try...otherwise already narrows away - Replace placeholder-looking lineageTag GUIDs with real random GUIDs - Remove the stale per-batch order by SortOrder in AdvisorReservationRecommendations (discarded by Table.Combine anyway); fix the matching stale comment in AdvisorRecommendations - Document that AdvisorRecommendations and AdvisorReservationRecommendations now return complete per-batch results (resultTruncated=false via ftk_QueryARG) instead of silently truncating, as an explicit decision - Clarify in errors.md that the effective ARG batch size is ftk_ARGBatchSize() times a per-table multiplier, not a flat 100 - Revert an unrelated See also / Related content restructure in workload-optimization.md Follow-ups tracked in #2224: hoisting the subscription-list query into a shared/cached expression, and normalizing Table.Combine output against each table's authoritative column list. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…-report-loading # Conflicts: # docs-mslearn/toolkit/changelog.md
Problem
Users with large Azure environments are encountering payload size limit errors when attempting to load the Governance and Workload Optimization Power BI reports. The error message appears as:
This occurs because both reports use Azure Resource Graph queries with
[resultTruncated = false]to retrieve comprehensive resource details. For organizations with millions of resources, the response payload exceeds Azure Resource Graph's 16 MB limit, preventing the reports from loading.Root Cause
Azure Resource Graph enforces a 16 MB (16,777,216 bytes) response payload limit per query. The Governance and Workload Optimization reports are designed to show detailed resource-level information and are optimized for small to medium-sized environments. When used in large enterprise environments with millions of resources, the unfiltered queries exceed this limit.
Solution
This PR includes both a functional fix and documentation. The functional fix paginates Azure Resource Graph queries by subscription so a single query is split into multiple smaller batches that each stay under the 16 MB limit, then combines the results. The documentation gives users manual mitigation options (filtering by subscription/tags, trimming columns) for cases where the default batching still isn't enough, or where they want more control.
Functional changes
ftk_QueryARGandftk_ARGBatchSize(inShared.Dataset/definition/expressions.tmdl, both thekqlandstoragedatasets) — a shared M function that queries Resource Graph for the tenant's subscriptions, splits them into batches (default 100 subscriptions per batch, user-configurable viaftk_ARGBatchSize), runs the caller's query once per batch with asubscriptionId in (...)filter, and combines all batch results.ftk_QueryARGacross the Resource Graph-backed tables used by the Governance and Workload Optimization reports (Advisor recommendations, disks, network interfaces/security groups, policy assignments/states, public IPs, resources, SQL databases, virtual machines).ftk_QueryARGagainst invalidftk_ARGBatchSize()values (null, non-numeric, non-integer, or less than 1 — falls back to the documented default of 100) and against the subscription-list Resource Graph query failing or returning an unexpected shape (falls back to an empty batch list instead of throwing).Documentation changes
1. Enhanced Error Documentation (
help/errors.md)Expanded the existing "Response payload size exceeded" error section with detailed, step-by-step mitigation strategies for cases where the built-in batching isn't sufficient:
Option 1: Filter by Subscription
Option 2: Filter by Tags
Option 3: Remove Unnecessary Columns
Option 4: Disable Problematic Queries
Each option includes step-by-step instructions, KQL code examples, and links to official Microsoft documentation.
2. Updated Report Documentation
Added "Known limitations" sections to both affected reports (
power-bi/governance.md,power-bi/workload-optimization.md) explaining the 16 MB Azure Resource Graph payload limit and linking to the error reference guide.3. Changelog Entry
Added an entry to the
v15Power BI reports section ofchangelog.md(the next unreleased version).Impact
References
Testing
devand resolved conflicts, restoring a review-flagged regression (a dropped reserved-instance-specific filter inAdvisorReservationRecommendations) in the processKnown limitations / follow-ups
ftk_ARGBatchSize) is user-configurable but has no upper cap; an extremely large custom value could still hit the 16 MB limit in very large tenants.Fixes: #1768
Original prompt
Fixes #1768
💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.