Skip to content

feat(hubs): add Azure Resource Manager ingestion - #2277

Draft
Brett Wilson (MSBrett) wants to merge 2 commits into
devfrom
features/finops-hubs-arm-app
Draft

feat(hubs): add Azure Resource Manager ingestion#2277
Brett Wilson (MSBrett) wants to merge 2 commits into
devfrom
features/finops-hubs-arm-app

Conversation

@MSBrett

Copy link
Copy Markdown
Contributor

🛠️ Description

This PR added a reusable Azure Resource Manager ingestion path for GET-only APIs and connected its output to the existing FinOps hub ingestion flow.

Managed Azure Resource Manager ingestion

  • Added a shared deployment module for manifest-driven Azure Resource Manager queries.
  • Wired the module into hub deployment, ingestion-query generation, Azure Resource Graph, and the portal deployment experience.
  • Added explicit schemas and transforms so Azure Data Factory and Azure Data Explorer receive stable column types.

Quota and recommendation data

  • Added quota ingestion for App Service, capacity reservations, Azure AI services, compute, Premium SSD v2 disks, SQL, and Storage.
  • Added one-year and three-year Azure savings plan recommendation ingestion.
  • Added normalized Quota() functions and query-catalog entries for current usage, headroom, source-specific quota views, and savings plan recommendation breakdowns.

Documentation and tests

  • Documented the normalized quota schema, supported source types, latest-state query pattern, and inventory caveats.
  • Added regression coverage for manifests, schemas, source filtering, generated identifiers, KQL operators, and deployment wiring.
  • Ignored local .copilot-tracking/ workflow artifacts.

This work provides shared ingestion groundwork for MACC consumption. MACC Lots and MACC Events ingestion are not included in this PR. Quota and capacity support extends beyond the current iteration described in #1721.

Refs #1721

Related to #2120 and #2129

📷 Screenshots

Not applicable.

📋 Checklist

🔬 How did you test this change?

  • 🤏 Lint tests
  • 🤞 PS -WhatIf / az validate
  • 👍 Manually deployed + verified
  • 💪 Unit tests
  • 🙌 Integration tests

The full unit suite passed with 2,453 tests and no failures. The full lint suite passed with 3,439 tests and no failures. App Service ingestion completed through Data Factory and Azure Data Explorer, and all seven quota catalog projections returned the expected schema and key behavior.

📦 Deploy to test?

  • Hubs + ADX (managed)
  • Hubs + Fabric (manual) — URI:
  • Hubs (manual)
  • Hubs (no data)
  • Workbooks
  • Alerts

🙋‍♀️ Do any of the following that apply?

  • 🚨 This is a breaking change.
  • 🤏 The change is less than 20 lines of code.

📑 Did you update docs/changelog.md?

  • ✅ Updated changelog (required for dev PRs)
  • ➡️ Will add log in a future PR (feature branch PRs only)
  • ❎ Log not needed (small/internal change)

A changelog entry is pending before this draft is ready for review.

📖 Did you update documentation?

  • ✅ Public docs in docs (required for dev)
  • ✅ Public docs in docs-mslearn (required for dev)
  • ✅ Internal dev docs in docs-wiki (required for dev)
  • ✅ Internal dev docs in src (required for dev)
  • ➡️ Will add docs in a future PR (feature branch PRs only)
  • ❎ Docs not needed (small/internal change)

Public documentation remains pending before this draft is ready for review.

Brett Wilson (MSBrett) and others added 2 commits August 23, 2026 17:26
Add generic ARM query ingestion with quota and Savings Plan definitions, normalized KQL surfaces, catalog queries, documentation, and regression coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Hubs + ADX (managed) deployment failed. View logs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds reusable Azure Resource Manager ingestion to FinOps hubs, supporting savings-plan recommendations and quota/capacity datasets.

Changes:

  • Adds manifest-driven ARM GET ingestion and deployment wiring.
  • Adds normalized quota and savings-plan schemas and KQL queries.
  • Extends portal configuration, documentation, and regression tests.

Reviewed changes

Copilot reviewed 45 out of 46 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/templates/finops-hub/test/main.test.bicep Adds feature deployment combinations.
.../Recommendations/schemas/recommendations_1.1.json Defines savings-plan translation.
.../Recommendations/queries/Recommendations-Microsoft-SavingsPlan-P3Y.json Adds three-year recommendations.
.../Recommendations/queries/Recommendations-Microsoft-SavingsPlan-P1Y.json Adds one-year recommendations.
.../Recommendations/app.bicep Registers new recommendation assets.
.../Quota/schemas/quota_1.0-usage.json Defines usage translation.
.../Quota/schemas/quota_1.0-sql.json Defines SQL quota translation.
.../Quota/schemas/quota_1.0-disk.json Defines disk translation.
.../Quota/schemas/quota_1.0-capacity-reservation.json Defines capacity translation.
.../Quota/queries/Quota-Microsoft-StorageUsage.json Adds Storage usage ingestion.
.../Quota/queries/Quota-Microsoft-SqlSubscriptionUsage.json Adds SQL usage ingestion.
.../Quota/queries/Quota-Microsoft-PremiumSSDv2Disk.json Adds disk inventory ingestion.
.../Quota/queries/Quota-Microsoft-ComputeUsage.json Adds compute usage ingestion.
.../Quota/queries/Quota-Microsoft-CognitiveServicesUsage.json Adds AI quota ingestion.
.../Quota/queries/Quota-Microsoft-CapacityReservation.json Adds capacity inventory ingestion.
.../Quota/queries/Quota-Microsoft-AppServiceUsage.json Adds App Service usage ingestion.
.../Quota/app.bicep Deploys quota queries and schemas.
.../IngestionQueries/app.bicep Integrates ARM staging and finalization.
.../AzureResourceManager/README.md Documents the ARM engine.
.../AzureResourceManager/metadata.bicep Defines ARM app metadata.
.../AzureResourceManager/app.bicep Implements ARM request pipelines.
.../AzureResourceGraph/app.bicep Extends the shared engine contract.
.../Analytics/scripts/IngestionSetup_v1_2.kql Normalizes savings-plan recommendations.
.../Analytics/scripts/IngestionSetup_v1_0.kql Adds quota transforms and tables.
.../Analytics/scripts/IngestionSetup_RawTables.kql Adds the quota raw table.
.../Analytics/scripts/HubSetup_v1_0.kql Adds the versioned quota function.
.../Analytics/scripts/HubSetup_Latest.kql Adds the latest quota alias.
src/templates/finops-hub/modules/hub.bicep Wires ARM and quota apps.
.../fx/hub-deploymentScript.bicep Forces deployment-script refreshes.
src/templates/finops-hub/main.bicep Exposes quota configuration.
src/templates/finops-hub/createUiDefinition.json Adds portal controls and guidance.
src/scripts/Build-HubIngestionQueries.ps1 Generates manifests for both apps.
src/queries/INDEX.md Catalogs the new queries.
src/queries/finops-hub-database-guide.md Documents the quota contract.
src/queries/catalog/savings-plan-recommendation-breakdown.kql Summarizes savings-plan recommendations.
src/queries/catalog/quota-storage-usage.kql Queries Storage quota state.
src/queries/catalog/quota-sql-subscription-usage.kql Queries SQL quota state.
src/queries/catalog/quota-premium-ssd-v2-disks.kql Queries Premium SSD v2 inventory.
src/queries/catalog/quota-headroom.kql Calculates quota headroom.
src/queries/catalog/quota-current-usage.kql Queries current quota state.
src/queries/catalog/quota-compute-usage.kql Queries compute quota state.
src/queries/catalog/quota-cognitive-services-usage.kql Queries AI quota state.
src/queries/catalog/quota-capacity-reservations.kql Queries capacity reservations.
src/queries/catalog/quota-app-service-usage.kql Queries App Service quota state.
src/powershell/Tests/Unit/HubsIngestionQueries.Tests.ps1 Extends ingestion regression coverage.
.gitignore Ignores Copilot tracking artifacts.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1029 to +1031
relativeUrl: {
value: '@concat(pipeline().parameters.queryScope, replace(pipeline().parameters.query, \'{location}\', pipeline().parameters.queryLocation))'
type: 'Expression'
Comment on lines +398 to +400
condition: {
value: '@and(not(empty(item().scope)), startswith(toLower(item().scope), \'/providers/microsoft.billing/\'))'
type: 'Expression'
Comment on lines +1005 to +1007
paginationRules: {
AbsoluteUrl: '$.nextLink'
}
name: 'queries_AzureResourceManager_CopyQuery'
parent: dataFactory
properties: {
concurrency: 1
Comment on lines +1041 to +1043
blobPath: {
value: '@concat(\'_ftk-query-staging/\', replace(pipeline().parameters.ingestionPath, concat(pipeline().parameters.queryType, \'.parquet\'), \'\'), \'/SubAccountId=\', last(split(pipeline().parameters.queryScope, \'/\')), if(empty(pipeline().parameters.queryLocation), \'\', concat(\'/location=\', pipeline().parameters.queryLocation)), \'/x_SourceName=Azure Resource Manager/x_SourceProvider=Microsoft\', if(contains(string(pipeline().parameters.translator), \'x_SourceType\'), \'\', concat(\'/x_SourceType=\', pipeline().parameters.queryType)), \'/x_SourceVersion=\', pipeline().parameters.queryVersion, \'/\', pipeline().parameters.queryType, \'--\', pipeline().parameters.queryVersion, \'--\', replace(pipeline().parameters.queryScope, \'/\', \'_\'), \'--\', pipeline().parameters.queryLocation, \'.parquet\')'
type: 'Expression'
tmp_QueryLocation = replace_string(tostring(tmp_FileParts[3]), '.parquet', '')
| extend
ProviderName = coalesce(ProviderName, 'Microsoft'),
SubAccountId = tolower(coalesce(SubAccountId, tmp_QueryScope)),
Comment on lines +30 to +37
| summarize arg_max(x_RecommendationDate, *) by BenefitType, ArmSkuName, Term, LookBackPeriod, Scope
| project
BenefitType,
ArmSkuName,
Term,
LookBackPeriod,
Scope,
CurrencyCode,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs: Review 👀 PR that is ready to be reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants