microsoft / microsoft/finops-toolkit

Ability to export Microsoft Azure Consumption Commitment (MACC) data and integrate into the FinOps Hub solution

Open
#1,721 2 comments 5 reactions 1 assignee View on GitHub

@MSBrett is already working on this.

Since Jul 3, 2025.

Tool: FinOps hubs Type: Feature 💎
Dominant language
PowerShell
Stars
603
Forks
248
Avg merge
7d 11h
Merged PRs (30d)
11

Description

Scenario

Export Microsoft Azure Consumption Commitment (MACC) data to FinOps hubs.
Also ingest Savings Plan benefit recommendations.

This data lets users:

  • Track remaining MACC commitment.
  • Analyze MACC decrement events.
  • Compare MACC-eligible spend with FOCUS cost data.
  • Review commitment dates, amounts, balances, and status.
  • Review Savings Plan benefit recommendations.

Goal

Adapt the existing Azure Resource Graph app to create a sister Azure REST app.
Reuse the existing query, pipeline, storage, and manifest pattern.

The REST app stays generic.
This iteration supplies three query definitions:

  1. MACC Lots.
  2. MACC Events.
  3. Savings Plan benefit recommendations.

The app supports GET only.

Session setup

Start a fresh session from dev.
Rename its branch once:

MSBrett/issue-1721-azure-rest-ingestion

Do not reuse features/research-custom-resource-graph.

Non-goals

  • Do not add quota APIs.
  • Do not add capacity APIs.
  • Do not discover subscriptions, billing accounts, or other scopes.
  • Do not add POST, PUT, PATCH, or DELETE.
  • Do not add a provider-specific endpoint registry.
  • Do not put API versions in the REST engine.
  • Do not add a new orchestration framework.

What this deletes or consolidates

  • Remove the old 18-API scope.
  • Remove all quota and capacity work from this iteration.
  • Remove all scope-discovery work.
  • Reuse the existing query dispatcher and ingestion pipeline.
  • Reuse the existing ARM linked service and managed identity.
  • Reuse the existing Parquet, manifest, monitoring, and failure behavior.

Scope selection

settings.json is the only source for scopes.
The existing file already supports multiple scopes.
The app reads every scopes[].scope value.

This app evaluates the Microsoft.Billing subset:

/providers/Microsoft.Billing/

Each query declares its compatible Azure resource types:

Dataset Compatible setting scope types
MACC Lots Microsoft.Billing/billingAccounts
MACC Events Microsoft.Billing/billingAccounts
Savings Plan benefit recommendations Microsoft.Billing/billingAccounts, Microsoft.Billing/billingAccounts/billingProfiles

The app performs these steps:

  1. Load settings.json.
  2. Read every scopes[].scope value.
  3. Validate each scope as an Azure resource ID.
  4. Select the Microsoft.Billing subset.
  5. Derive the Azure resource type.
  6. Run each query for every compatible scope.

The match is not case-sensitive.
The app does not add another scope list.
The app does not infer or discover a scope.

The accepted resource types are query configuration.
The REST engine contains no Microsoft.Billing endpoint rules.

Exact initial API requests

{arm} is the Azure Resource Manager authority for the current cloud.
{scope} is a compatible scope from settings.json.

MACC Lots
GET {arm}{scope}/providers/Microsoft.Consumption/lots?api-version=2026-06-01&$filter=source eq 'ConsumptionCommitment'
  • Compatible scope: billing account.
  • Items: value.
  • Next page: nextLink.
MACC Events
GET {arm}{scope}/providers/Microsoft.Consumption/events?api-version=2026-06-01&$filter=lotSource eq 'ConsumptionCommitment'
  • Compatible scope: billing account.
  • Items: value.
  • Next page: nextLink.
  • Do not send startDate or endDate.
Savings Plan benefit recommendations
GET {arm}{scope}/providers/Microsoft.CostManagement/benefitRecommendations?api-version=2025-03-01&$filter=properties/lookBackPeriod eq 'Last7Days' AND properties/term eq 'P3Y'&$expand=properties/usage,properties/allRecommendationDetails
  • Compatible scopes: billing account and billing profile.
  • Items: value.
  • Next page: nextLink.
  • Lookback period: Last7Days.
  • Term: P3Y.
  • Recommendation scope: Shared, by API default.

The API version is query configuration.
The output schema version is a different value.

Call each validated nextLink with GET.
Do not add or replace parameters in a continuation URL.

Existing pattern to reuse

Reuse these existing components:

  • Microsoft.FinOpsHubs.IngestionQueries for query loading and dispatch.
  • Microsoft.FinOpsHubs.AzureResourceGraph as the sister-engine pattern.
  • The Core ARM REST linked service.
  • The existing Parquet sink and manifest flow.
  • The existing bounded query execution.

Do not design a new system.
Change only the REST engine behavior and required query contract fields.

Implementation steps

  1. Add a sister Azure REST app.
    Copy the Azure Resource Graph app structure.

  2. Add a parameterized REST dataset.
    Accept a validated ARM-relative path.
    Accept the API version as a separate parameter.

  3. Extend the existing query contract.
    Add the path, version, scope source, compatible resource types, query parameters, item selector, and continuation selector.

  4. Extend the existing dispatcher.
    Load scopes from settings.json.
    Create one work item for each compatible scope and query.

  5. Preserve current query behavior.
    Existing Azure Resource Graph queries must run without configuration changes.

  6. Keep bounded parallel work.
    Reuse the existing pipeline pattern.
    Do not add a queue or scheduler.

  7. Validate each request before authentication.
    Accept only GET.
    Accept only an ARM-relative path.

  8. Validate every continuation URL.
    Require the current Azure Resource Manager authority.
    Never send the managed identity token to another authority.

  9. Add three query files and three schema translators.
    Keep all API-specific values in those files.

  10. Reuse the current output flow.
    Keep API failures as failures.

  11. Add focused tests.
    Test scope selection, request validation, paging, translation, and existing-query behavior.

  12. Add the required changelog entry.
    Link the pull request to this issue.

Security rules

  • Treat the setting scope as untrusted.
  • Treat the configured path as untrusted.
  • Attach authentication only after validation.
  • Reject an absolute input URL.
  • Reject a scheme-relative input URL.
  • Reject user information.
  • Reject fragments.
  • Reject backslashes.
  • Reject a malformed Azure resource ID.
  • Validate each continuation URL.
  • Require the current Azure Resource Manager authority.

Live test and prototype gate

Use only the user-approved EA billing account for live Azure work.
Supply its identifier through private test configuration.

Do not publish the EA identifier in:

  • This issue.
  • A pull request.
  • Committed files.
  • Logs.
  • Test evidence.

Before each live run, check the selected scope set.
Stop unless the only selected scope is the user-approved EA.

Do not call, inspect, enumerate, or test an MCA scope.
Do not request MCA permissions.
Do not use a billing profile in a live request.

MCA work requires explicit user approval.
Ask only after the user accepts the EA result.
Until that approval, MCA behavior is unverified.

Local contract tests do not call Azure.

API version status

  • Consumption 2024-08-01 is the latest version in the merged public specification.
  • Live provider metadata advertises Consumption 2026-06-01 as non-preview.
  • Azure REST API Specs PR #43855 contains the 2026-06-01 contract.
  • PR #43855 is not merged.
  • Query configuration can change the version without an engine change.

Acceptance criteria

  • The REST engine contains no Microsoft.Billing endpoint rule.
  • The app evaluates all Microsoft.Billing scopes in settings.json.
  • Each query runs for every compatible setting scope.
  • The app adds no second scope setting.
  • The app makes no scope-discovery request.
  • The app supports only GET.
  • The three query files contain the exact requests shown above.
  • API versions come from query configuration.
  • The app ingests all pages from value and nextLink.
  • A request cannot leave the Azure Resource Manager authority.
  • A failure does not become an empty successful dataset.
  • Existing Azure Resource Graph queries work without configuration changes.
  • No quota or capacity API is present.
  • Live Azure work uses only the privately configured EA.
  • No MCA request occurs without explicit approval.
  • Public artifacts contain no EA billing-account identifier.
  • The pull request contains a changelog entry and links this issue.

Requirements traceability matrix

ID Requirement Plan coverage Required proof
R1 Adapt the existing app for REST. Goal; existing pattern; steps 1 and 10 App deployment and pipeline tests
R2 Keep the REST app generic. Scope selection; steps 2, 3, and 9 Engine contains no provider-specific endpoint
R3 Read all scopes from settings.json. Scope selection; step 4 Tests with multiple setting scopes
R4 Evaluate the Microsoft.Billing subset. Scope selection Tests with compatible and incompatible resource types
R5 Support only the three named APIs. Exact requests; step 9 Query contract lists exactly three requests
R6 Exclude quota and capacity work. Non-goals; acceptance criteria Query contract and repository search
R7 Support GET only. Goal; security rules Method validation tests
R8 Keep API versions in configuration. API requests; steps 2 and 3 Version parameter tests
R9 Treat paths and continuations as untrusted. Steps 7 and 8; security rules Malicious path and continuation tests
R10 Preserve the current ingestion flow. Existing pattern; steps 5, 6, and 10 Azure Resource Graph regression tests
R11 Record the exact initial requests. Exact initial API requests Request contract tests
R12 Use the approved EA for live work. Live test gate Private scope preflight
R13 Do not touch MCA without approval. Live test gate No MCA access in live evidence
R14 Keep the EA identifier private. Live test gate Public-artifact scan
R15 Meet repository delivery rules. Step 12 Changelog entry and issue link

References

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.