[Open Data] Add instance size flexibility dataset from Azure Catalogs API - #2199
Conversation
…API (#2090) Adds a new InstanceSizeFlexibility open data dataset to replace the deprecated ISF CSVs hosted on ccmstorageprod.blob.core.windows.net (AutofitComboMeterData.csv and isfratioblob.csv / aka.ms/isf), which Microsoft is retiring (no updates after 9 May 2026, removed 30 Aug 2026). - Update-InstanceSizeFlexibility.ps1 extracts ISF groups/ratios from the authenticated Azure Reservations Catalogs API (Microsoft.Capacity/catalogs), unioning the four ISF-bearing reserved resource types (VirtualMachines, BlockBlob, RedisCache, DedicatedHost) across a broad region set. - InstanceSizeFlexibility.csv is populated with 2,437 SKUs / 558 groups. - Weekly workflow refreshes the data via OIDC (maintainers must configure the AZURE_CLIENT_ID/TENANT_ID/SUBSCRIPTION_ID secrets; see workflow header). - Raw Microsoft ratios are kept for drop-in parity with the deprecated files (normalization available via -Normalize). Foundation only: Power BI and Optimization Engine consumers will be repointed to this dataset in a follow-up once it ships in a release. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a new open-data dataset for instance size flexibility (ISF) ratios by generating InstanceSizeFlexibility.csv from the authenticated Azure Reservations Catalogs API, replacing reliance on deprecated static CSV blobs.
Changes:
- Added a PowerShell generator script to fetch, normalize (optional), and cache-merge ISF ratios from
Microsoft.Capacity/catalogs. - Added the generated
InstanceSizeFlexibility.csvopen-data artifact and documented it in open-data docs/README. - Added a scheduled GitHub Actions workflow to refresh the dataset weekly and open a PR when it changes.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/scripts/Update-InstanceSizeFlexibility.ps1 | New generator that pages the Catalogs API and emits the ISF CSV (with cache-merge + optional normalization). |
| src/open-data/InstanceSizeFlexibility.csv | New published open-data dataset containing ISF group/SKU/ratio rows. |
| .github/workflows/opendata-instance-size-flexibility.yml | Weekly automation to refresh the dataset and open an update PR. |
| src/open-data/README.md | Documents the new ISF dataset, its source, and manual update instructions. |
| docs/open-data.md | Adds a download tile linking to the CSV in the latest release assets. |
- Retry 429/5xx on the same Catalogs API page inside the inner retry loop instead of after it, so page counts and the retry cap stay correct. - Skip SKUs with placeholder ArmSkuName (e.g. 'arm_sku_name_placeholder') so preview/unreleased SKUs don't leak into the public CSV; removed the one such row that had reached InstanceSizeFlexibility.csv. - Document the canonical 3-column schema and why the deprecated Power BI connector columns aren't reproducible from the Catalogs API. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Addressed the Copilot review (commit pushed):
On the column schema (raised in review discussion): the dataset intentionally uses the canonical three-column Catalogs API schema (
|
The repo already has an OIDC service principal with AZURE_CLIENT_ID/ TENANT_ID/SUBSCRIPTION_ID configured in the aoe-prod environment (used by the AOE deployment workflows). Reuse it via federated credentials instead of requiring new secrets: bind the job to the aoe-prod environment and follow the repo's established azure/login@hf_447_release pattern. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Update on the auth setup — no new secrets/SP needed. The earlier note said maintainers would need to create an OIDC service principal and I've updated the workflow to reuse that identity rather than create a parallel one:
So there's nothing to provision. The only residual is a one-time confirmation that the If you'd prefer the open data workflow not to borrow the AOE-named environment, a maintainer can create a dedicated |
flanakin
left a comment
There was a problem hiding this comment.
🤖 [AI][Claude Code] PR Review
Summary: Clean, well-structured addition that closely mirrors the established Update-CommitmentDiscountEligibility sibling pattern (cache-merge, paging, retry, weekly PR workflow). The earlier Copilot items (in-place 429/5xx retry, placeholder-SKU filtering) are already addressed in-tree, and the generator/CSV logic looks correct. Two non-blocking findings below.
⚠️ Should fix (1)
- No changelog entry. The sibling Commitment discount eligibility dataset shipped with an
### [Open data]changelog section; this is a comparably substantial external-facing dataset (2,436 SKUs, new download tile, weekly workflow) with no entry in the currentdocs-mslearn/toolkit/changelog.md. Add an### [Open data]entry under the next/unreleased version.
💡 Suggestions (1)
-Normalizeis typed[bool](requires-Normalize $true);[switch]would be more idiomatic PowerShell. Optional — the[bool]form does allow scripted toggling.
- Add changelog entry (Unreleased > [Open data]) and a matching "Instance size flexibility" section to the MS Learn open-data doc. - Make -Normalize a [switch] (idiomatic) instead of [bool]; update help and the README manual-update note accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
The
The |
|
Two follow-ups: Open Data CI failure was transient — now green. The failure wasn't in the data or generator: the Thanks @helderpinto for confirming the |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
docs/open-data.md:41
- These hidden spacer elements are anchors with empty href values. Even though they’re hidden, empty links can show up in HTML/link validation and create confusing focus/interaction semantics. Use a non-interactive element (e.g., ) for layout spacing instead.
<a class="btn mb-4 mb-md-0 mr-4" href="" style="visibility:hidden; width:100px"> </a>
<a class="btn mb-4 mb-md-0 mr-4" href="" style="visibility:hidden; width:100px"> </a>
docs-mslearn/toolkit/open-data.md:7
- For docs-mslearn pages, ms.date should be updated to today when the file changes to avoid merge conflicts with the automated date updater.
ms.date: 06/29/2026
docs-mslearn/toolkit/changelog.md:6
- For docs-mslearn pages, ms.date should be updated to today when the file changes to avoid merge conflicts with the automated date updater.
ms.date: 06/29/2026
There was a problem hiding this comment.
Request changes
This cannot be approved while the privileged workflow uses mutable third-party action refs and while its published data is unsafe for the documented join.
| Requirement | Evidence | Status |
|---|---|---|
| Pin privileged third-party actions | Workflow lines 24 and 35 use actions/checkout@v4 and azure/login@hf_447_release, both named refs; the latter resolves to branch commit 264ef4588d3ca547fea5296679ee9f8a4766d4df. The job has contents: write, pull-requests: write, and id-token: write. |
Blocked — comment posted |
| Preserve a safe consumer key | Import-Csv analysis finds 45 duplicate ArmSkuName values; archive_grs_data_stored_1 pb has 26 rows in distinct groups. The README recommends joining on this non-unique field. |
Blocked — comment posted |
| Make weekly output converge on the source | Cache rows are copied into $merged before observed rows overwrite them; no path removes an unseen record. Confirmed by executing the merge logic against a synthetic retired-SKU fixture, not just reading it. |
Blocked — comment posted |
| Test changed behavior | No test references Update-InstanceSizeFlexibility; existing Data selection and lint scope do not exercise src/scripts. Direct Invoke-ScriptAnalyzer run against the script also surfaces a PSUseSingularNouns violation that would fail immediately if lint scope were widened as requested. |
Blocked — comment posted |
| Validate documentation | ./src/scripts/Test-PowerShell.ps1 -Docs: 1,510 passed, 0 failed. |
Passed |
| Validate syntax and diff hygiene | PowerShell parser clean; git diff --check clean. |
Passed |
I reviewed the existing discussion and verified the prior retry, placeholder-filter, changelog, and -Normalize feedback is reflected in the current diff. The four requirements above remain unresolved.
Update: every claim above has since been independently re-verified against the current PR HEAD by direct execution (mocked pagination/retry test, live merge-logic replay, CSV reproduction, PSScriptAnalyzer run, and a live authenticated call to the Azure Reservations Catalogs API to validate ISF group shape). All four blocking findings hold. One earlier internal concern about single-SKU flexibility groups was raised and then retracted after the live API confirmed Microsoft's own catalog legitimately returns singleton groups for isolated/high-memory/specialty SKUs — that was not posted here and required no correction.
…tests - Pin actions/checkout (v4.4.0) and azure/login (hf_447_release) to full commit SHAs in the ISF open-data workflow, which holds contents: write, pull-requests: write, and id-token: write. - Add Pester unit tests for Update-InstanceSizeFlexibility.ps1 covering nextLink pagination, 429/5xx same-page retry, non-retryable 4xx scope skip, placeholder SKU filtering, armSkuName fallback, regional union dedup, cache merge/preserve, and -Normalize rescaling. - Include the new tests in Test-PowerShell.ps1 -Data selection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s API - Exclude BlockBlob from the default reserved resource types: its catalog entries carry meter-style names instead of real ARM SKU names, every ratio is 1, and its region-specific groups duplicated 45 ArmSkuName values (984 rows). ArmSkuName is now globally unique and safe as a standalone join key; the generator fails hard if the API ever returns a duplicate, and a unit test enforces uniqueness on the published CSV. - Remove the cache-merge: each run now publishes exactly what the API returns, so retired records disappear on the next refresh. Any scope that fails after retries aborts the run without touching the output; the weekly workflow then keeps the last published dataset. - Regenerate the CSV (1,352 SKUs). Besides the storage rows, 100 legacy series rows (G/GS, Dv2/Dv3, Ev3, F/FS, LSv2, ...) are removed because the Catalogs API no longer returns them in any region. - Update open-data README and Learn docs accordingly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@MSBrett Thanks for the thorough review — all four blocking items are addressed in ee1dc3b and f7f434c. 1. Pinned privileged actions — 2. Safe join key — Root-cause analysis showed all 45 duplicated 3. Convergence on the source — The cache-merge is removed entirely. Each run publishes exactly what the API returns for a fully successful sweep; any scope that still fails after retries aborts the run without touching the output file, so the workflow fails and the last published dataset stays in place. No path retains an unseen record anymore. The regenerated CSV (1,352 SKUs) also drops 100 legacy-series rows (G/GS, Dv2/Dv3, Ev3, F/FS, LSv2, …) — verified as no longer returned by the Catalogs API in any queried region, i.e. convergence working as intended. Reviewers of the weekly PR see removals in the diff before anything merges. 4. Test coverage — Added #needs-review |
Replace the shared aoe-prod deployment principal (Contributor on a personal MSDN subscription) with the dedicated finops-toolkit-opendata-reader app registration in the FinOps Toolkit tenant. The federated credential trusts only this repo's dev branch and the principal holds only Reader, which is all the Catalogs API needs. The client/tenant/subscription IDs are identifiers, not secrets, so they live inline in the workflow and no GitHub environment or secrets are required. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Follow-up hardening (6fa45eb): the workflow no longer uses the shared It now authenticates as a dedicated
@helderpinto this also resolves the continuity concern you raised about the personal MSDN-benefits subscription — the identity now lives in the shared FinOps Toolkit tenant. The trade-off of branch-scoped (vs. environment-scoped) trust: any workflow on Validation after merge: a one-time manual |
The catalog is subscription-independent; FTK Dev is the intended home for toolkit automation identities. Reader was removed from the previous subscription and Microsoft.Capacity is now registered on FTK Dev. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-flexibility # Conflicts: # docs-mslearn/toolkit/changelog.md
Summary
Adds a new instance size flexibility (ISF) open data dataset, sourced from the authenticated Azure Reservations Catalogs API, to replace the two deprecated static CSVs hosted on
ccmstorageprod.blob.core.windows.net(AutofitComboMeterData.csvandisfratioblob.csv/aka.ms/isf). Microsoft is retiring those files — no updates after 9 May 2026, removed 30 Aug 2026.Addresses #2090. This implements the open-data-generation approach discussed on the issue: the authentication complexity lives only in the generator, so downstream tools consume a public CSV and need no Azure credentials. Consumer repoints are tracked separately in #2200.
What's included (foundation only)
src/scripts/Update-InstanceSizeFlexibility.ps1— extracts ISF groups/ratios fromMicrosoft.Capacity/catalogs. Handles both REST (ReservationsAutofit*) and PS-cmdlet (InstanceSizeFlexibility*) property names, pagination with in-place 429/5xx retry, placeholder-SKU filtering, and a cache-merge that preserves SKUs the API drops. Defaults to the four ISF-bearing reserved resource types (VirtualMachines, BlockBlob, RedisCache, DedicatedHost) unioned across a broad region set.src/open-data/InstanceSizeFlexibility.csv— populated with 2,436 SKUs / 557 groups of live data (3-column schema:InstanceSizeFlexibilityGroup,ArmSkuName,Ratio)..github/workflows/opendata-instance-size-flexibility.yml— weekly refresh; opens a PR on change.Coverage vs the deprecated files
InstanceSizeFlexibility.csvisfratioblob.csv(Optimization Engine)AutofitComboMeterData.csv(Power BI, frozen since 2023)Raw Microsoft ratios are kept as-is for drop-in parity with the deprecated files (normalization is available via
-Normalize).Schema (3 columns, by design)
The file uses the canonical Catalogs API ISF schema —
InstanceSizeFlexibilityGroup, ArmSkuName, Ratio— which is a 1:1 match for the Optimization Engine'sisfratioblob.csv. The Power BIAutofitComboMeterData.csvhad additional columns (ResourceLocation, meter IDs, composite key, normalized SKU), but those are Cost Management connector artifacts not exposed by the Catalogs API, so the Power BI repoint (#2200) will join onArmSkuNameinstead.Authentication
The Catalogs API is authenticated (
Microsoft.Capacity/catalogs/read). The workflow reuses the existing OIDC service principal from theaoe-prodenvironment (the same identity the AOE deployment workflows use), following the repo'sazure/login@hf_447_releasepattern — no new secrets to provision. The principal's subscription needs theMicrosoft.Capacityresource provider registered (done). A one-timeworkflow_dispatchafter merge confirms the principal can read the catalog.Test plan
armSkuNamefallback all confirmed)Modified: 0across all regions)workflow_dispatchafter merge to confirm CI auth path🤖 Generated with Claude Code