Skip to content

Commit 20a8475

Browse files
committed
Merge remote-tracking branch 'origin/dev' into flanakin/1769-mca-reservations
# Conflicts: # docs-mslearn/toolkit/changelog.md
2 parents 6353665 + 298f792 commit 20a8475

27 files changed

Lines changed: 6368 additions & 5470 deletions

‎.github/policies/pulls-03-feedback.yml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ configuration:
3232
reviewState: Commented
3333
- isActivitySender:
3434
issueAuthor: False
35+
- and:
36+
- payloadType: Issue_Comment
37+
- isAction:
38+
action: Created
39+
- not:
40+
commentContains:
41+
pattern: '#needs-review'
42+
- isActivitySender:
43+
issueAuthor: False
44+
- isPullRequest
45+
- isOpen
3546
- not:
3647
targetsBranch:
3748
branch: main

‎.github/workflows/opendata-ci.yml‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
- 'src/open-data/*.json'
99
# Internal operational baselines for the eligibility completeness guard, not
1010
# reference data; Build-OpenData ignores them, so they shouldn't trigger CI.
11-
- '!src/open-data/*.shardcounts.json'
11+
- '!src/open-data/*.familycounts.json'
1212
permissions:
1313
contents: write
1414
pull-requests: write
@@ -45,6 +45,15 @@ jobs:
4545
git commit -a -m "${{ env.CI_COMMIT_MESSAGE }}"
4646
git push
4747
}
48+
# Build-OpenData.ps1 -Test calls Test-PowerShell.ps1, which requires Pester 6 (#2254):
49+
# the suite uses -AllowNullOrEmptyForEach, which Pester 5 rejects. The ubuntu runner
50+
# image ships Pester 5.9.0, so the minimum has to be installed explicitly. This is the
51+
# same command Init-Repo.ps1 runs and the one Test-PowerShell.ps1 names in its error.
52+
# dev.yml pins the same floor through psmodulecache, which this repo only uses on
53+
# Windows runners.
54+
- name: Install Pester
55+
shell: pwsh
56+
run: Install-Module -Name Pester -MinimumVersion 6.0.0 -Scope CurrentUser -Repository PSGallery -Force
4857
- name: Test Open Data
4958
id: test
5059
shell: pwsh

‎.github/workflows/opendata-commitment-eligibility.yml‎

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,62 @@ name: Update Commitment Discount Eligibility
22

33
on:
44
schedule:
5-
- cron: '0 6 * * 1' # Every Monday at 06:00 UTC
5+
- cron: '0 6 * * 1' # Every Monday at 06:00 UTC
66
workflow_dispatch:
7+
inputs:
8+
ref:
9+
# The scheduled run always uses dev. This input exists so a change to the fetch
10+
# script can be exercised end-to-end from its PR branch BEFORE merging, which
11+
# matters for a job that only runs weekly and takes ~44 minutes to fail. Only
12+
# users with write access can dispatch a workflow, so this does not widen who
13+
# can run code in the repo.
14+
#
15+
# A non-dev ref is TEST-ONLY and is enforced as such by the guard step below:
16+
# pushing a data branch built from a feature ref would carry that ref's code
17+
# commits into the data PR, so the combination is rejected rather than merely
18+
# discouraged.
19+
description: 'Branch to check out and run the script from (non-dev requires dry_run)'
20+
required: false
21+
default: 'dev'
22+
type: string
23+
dry_run:
24+
# Defaults to true so the safe path is also the default one. A real data push is
25+
# therefore always an explicit choice, and the scheduled run (where this input is
26+
# absent entirely, not false) is unaffected -- see the DRY_RUN env note below.
27+
description: 'Fetch and detect changes only; do not push a branch'
28+
required: false
29+
default: true
30+
type: boolean
731

832
permissions:
933
contents: write
1034

1135
jobs:
1236
update:
1337
runs-on: ubuntu-latest
14-
timeout-minutes: 60
38+
# Two full traversals per price type (fetch + verification) measured 44 minutes on
39+
# run 31710007476. The headroom absorbs Retry-After backoff on a throttled run.
40+
timeout-minutes: 90
1541
steps:
42+
# Fail before the ~44-minute fetch rather than after it, so an invalid input
43+
# combination costs seconds instead of most of an hour.
44+
- name: Validate dispatch inputs
45+
env:
46+
DRY_RUN: ${{ github.event.inputs.dry_run }}
47+
RUN_REF: ${{ github.event.inputs.ref || 'dev' }}
48+
run: |
49+
if [ "$RUN_REF" != "dev" ] && [ "$DRY_RUN" != "true" ]; then
50+
echo "::error::ref='$RUN_REF' is not 'dev' and dry_run is not true. A push from a" \
51+
"non-dev ref would base the opendata/* branch on that ref, so the data PR" \
52+
"would carry its code commits as well as the data update. Re-run with" \
53+
"dry_run enabled to test a branch, or with ref='dev' to publish data."
54+
exit 1
55+
fi
56+
57+
# github.event.inputs is null for the scheduled run, so this falls back to dev.
1658
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
1759
with:
18-
ref: dev
60+
ref: ${{ github.event.inputs.ref || 'dev' }}
1961

2062
- name: Fetch eligibility data from Azure Retail Prices API
2163
shell: pwsh
@@ -25,29 +67,53 @@ jobs:
2567
# a branch and surface a one-click "create PR" link in the job summary for a
2668
# maintainer to open. Opening the PR triggers Open Data CI and normal review.
2769
- name: Push branch and surface PR link if data changed
70+
env:
71+
# Read as a string and compared as one below. A `type: boolean` input arrives
72+
# here as the literal "true"/"false", and in a GitHub `if:` expression the
73+
# string "false" is truthy -- comparing in bash sidesteps that trap. For the
74+
# SCHEDULED run there is no inputs object at all, so this is empty (not the
75+
# "true" default), and the run therefore takes the normal push path.
76+
DRY_RUN: ${{ github.event.inputs.dry_run }}
77+
RUN_REF: ${{ github.event.inputs.ref || 'dev' }}
2878
run: |
29-
# Check for changes to the CSV or the per-shard baseline (handles both
79+
# Check for changes to the CSV or the per-family baseline (handles both
3080
# modified and newly-created/untracked files). The baseline is checked too
3181
# so a counts-only update (no eligibility change) is still surfaced.
32-
if git diff --quiet --exit-code -- src/open-data/CommitmentDiscountEligibility.csv src/open-data/CommitmentDiscountEligibility.shardcounts.json 2>/dev/null && \
33-
! git ls-files --others --exclude-standard -- src/open-data/ | grep -qE 'CommitmentDiscountEligibility\.(csv|shardcounts\.json)'; then
82+
if git diff --quiet --exit-code -- src/open-data/CommitmentDiscountEligibility.csv src/open-data/CommitmentDiscountEligibility.familycounts.json 2>/dev/null && \
83+
! git ls-files --others --exclude-standard -- src/open-data/ | grep -qE 'CommitmentDiscountEligibility\.(csv|familycounts\.json)'; then
3484
echo "No changes detected"
3585
exit 0
3686
fi
3787
88+
if [ "$DRY_RUN" = "true" ]; then
89+
{
90+
echo "### Dry run: data changed, nothing pushed"
91+
echo ""
92+
echo "Ran the fetch against \`${RUN_REF}\` and detected changes, but \`dry_run\` was set, so no branch was pushed."
93+
echo ""
94+
echo '```'
95+
git --no-pager diff --stat -- src/open-data/CommitmentDiscountEligibility.csv src/open-data/CommitmentDiscountEligibility.familycounts.json || true
96+
# --stat covers tracked files only, so list any newly created ones too.
97+
git ls-files --others --exclude-standard -- src/open-data/ | grep -E 'CommitmentDiscountEligibility\.(csv|familycounts\.json)' | sed 's/^/new file: /' || true
98+
echo '```'
99+
} >> "$GITHUB_STEP_SUMMARY"
100+
echo "Dry run: changes detected, not pushing."
101+
exit 0
102+
fi
103+
38104
BRANCH="opendata/commitment-eligibility-$(date +%Y%m%d)-${{ github.run_number }}"
39105
git config user.name "github-actions[bot]"
40106
git config user.email "github-actions[bot]@users.noreply.github.com"
41107
git checkout -b "$BRANCH"
42-
git add src/open-data/CommitmentDiscountEligibility.csv src/open-data/CommitmentDiscountEligibility.shardcounts.json
108+
git add src/open-data/CommitmentDiscountEligibility.csv src/open-data/CommitmentDiscountEligibility.familycounts.json
43109
git commit -m "chore: Update commitment discount eligibility data"
44110
git push origin "$BRANCH"
45111
46112
PR_URL="${{ github.server_url }}/${{ github.repository }}/compare/dev...${BRANCH}?expand=1"
47113
{
48114
echo "### Commitment discount eligibility data updated"
49115
echo ""
50-
echo "Pushed branch \`${BRANCH}\`. GitHub Actions cannot open PRs in this repo, so open it manually:"
116+
echo "Pushed branch \`${BRANCH}\` (fetched from \`${RUN_REF}\`). GitHub Actions cannot open PRs in this repo, so open it manually:"
51117
echo ""
52118
echo "[**Create pull request →**](${PR_URL})"
53119
echo ""

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ env/
384384
# Auto-generated build artifacts
385385
src/templates/finops-hub-copilot-studio/knowledge/query-catalog.md
386386
.gate/
387+
.copilot-tracking/
387388
todo/
388389
done/
389390
release/scloud-occurrence-report.md

‎docs-mslearn/toolkit/changelog.md‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: FinOps toolkit changelog
33
description: Review the latest features and enhancements in the FinOps toolkit, including updates to FinOps hubs, Power BI reports, and more.
44
author: MSBrett
55
ms.author: brettwil
6-
ms.date: 08/17/2026
6+
ms.date: 08/26/2026
77
ms.topic: reference
88
ms.service: finops
99
ms.subservice: finops-toolkit
@@ -30,9 +30,11 @@ The following section lists features and enhancements that are currently in deve
3030
- **Added**
3131
- Added VNet and private network modes, including opt-in NAT Gateway support for private mode; NAT Gateway incurs additional cost when enabled ([#2163](https://github.com/microsoft/finops-toolkit/pull/2163)).
3232
- **Changed**
33+
- Clarified that the FinOps toolkit exclusively manages the FinOps hub virtual network and documented customer-managed private endpoints as the preferred private-access topology, with virtual network peering as a secondary option ([#2156](https://github.com/microsoft/finops-toolkit/issues/2156)).
3334
- Replaced redundant `tolower()` comparisons in hub KQL with case-insensitive operators (`has`, `=~`, `!~`) so the engine can use the term index instead of scanning every row ([#2213](https://github.com/microsoft/finops-toolkit/issues/2213)).
3435
- Replaced whole-term `contains` matches with `has` across hub KQL and the query catalog (resource ID paths, licensing phrases, SKU description terms) and added a per-row operator-equivalence regression harness with unit test coverage ([#2220](https://github.com/microsoft/finops-toolkit/pull/2220)).
3536
- **Fixed**
37+
- Fixed private-network deployments that Azure Policy blocked when `defaultOutboundAccess` was omitted. Private mode subnets now set it to `false`, while an Azure Files private endpoint supports deployment-script storage and the NAT Gateway provides required container egress ([#2258](https://github.com/microsoft/finops-toolkit/issues/2258), [#2259](https://github.com/microsoft/finops-toolkit/pull/2259)).
3638
- Fixed the `ContractedCost` recompute guard to compare with a null-safe tolerance instead of exact float equality, eliminating millions of no-op rewrites that polluted the `x_SourceValues` audit trail while preserving the null-cost backfill and no longer overwriting an existing cost when the unit price is missing ([#2216](https://github.com/microsoft/finops-toolkit/issues/2216)).
3739
- Fixed the SQL VMs without Azure Hybrid Benefit recommendation query to join on the SQL VM `virtualMachineResourceId` instead of a case-sensitive VM name match that skipped VMs with uppercase names and dropped duplicate names, and made all Azure Resource Graph join kinds explicit so no query relies on the `innerunique` default ([#2225](https://github.com/microsoft/finops-toolkit/pull/2225)).
3840
- Switched dimension enrichment in the v1_0/v1_2 ingestion transforms (`PricingUnits`, `Regions`, `ResourceTypes`, `Services`) from `join` to the broadcast-optimized `lookup` operator and deduplicated the `Services` mapping per resource type to prevent cost row fan-out ([#2225](https://github.com/microsoft/finops-toolkit/pull/2225)).
@@ -59,6 +61,11 @@ The following section lists features and enhancements that are currently in deve
5961
- **Changed**
6062
- Switched the reservations and benefits workbooks from the retired `ccmstorageprod` isfratioblob.csv to the FinOps toolkit [Instance size flexibility](open-data.md#instance-size-flexibility) open data file ([#2090](https://github.com/microsoft/finops-toolkit/issues/2090)).
6163
64+
### [PowerShell module](powershell/powershell-commands.md)
65+
66+
- **Fixed**
67+
- Fixed [Start-FinOpsCostExport](powershell/cost/start-finopscostexport.md) exporting the wrong period for anyone running in a positive UTC offset. `-StartDate` and `-EndDate` are now treated as UTC calendar dates instead of being time zone converted, so the days you request are the days that get exported. Previously, local midnight converted to the previous UTC day, which moved the period back a day and made `-Backfill` run one extra month ([#2255](https://github.com/microsoft/finops-toolkit/issues/2255)).
68+
6269
### [Open data](open-data.md) updates
6370
6471
**[Instance size flexibility](open-data.md#instance-size-flexibility)**
@@ -70,6 +77,7 @@ The following section lists features and enhancements that are currently in deve
7077
7178
- **Fixed**
7279
- Fixed the commitment discount eligibility dataset refresh so it is reproducible and complete; retired meters now age out and previously missed meters are included ([#2164](https://github.com/microsoft/finops-toolkit/pull/2164)).
80+
- Fixed the weekly commitment discount eligibility refresh timing out before it could publish, which left the dataset unchanged since it first shipped in v14. The refresh now walks each price type directly instead of sharding by service family, and verifies completeness by comparing two independent traversals before writing ([#2251](https://github.com/microsoft/finops-toolkit/pull/2251)).
7381
7482
-->
7583

‎docs-mslearn/toolkit/hubs/deploy.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: How to create and update FinOps hubs
33
description: This tutorial helps you create a new or update an existing FinOps hubs instance in Azure or Microsoft Fabric.
44
author: flanakin
55
ms.author: micflan
6-
ms.date: 08/13/2026
6+
ms.date: 08/19/2026
77
ms.topic: tutorial
88
ms.service: finops
99
ms.subservice: finops-toolkit
@@ -84,9 +84,9 @@ Public routing is most common and easiest to use. Resources are reachable from t
8484
Do you prefer public or private network routing?
8585

8686
- Public routing is most common, easiest to use, and makes resources reachable from the open internet.
87-
- Private routing is most secure, comes with added cost, and makes resources only reachable from peered networks.
87+
- Private routing is most secure, comes with added cost, and makes resources reachable through private connectivity.
8888

89-
Public routing doesn't require configuration. If you opt for private routing, work with your network admin to configure peering and routing so the FinOps hubs isolated network is reachable from your network. Before you decide, learn more about the extra configuration steps required in [Configure private networking](private-networking.md).
89+
Public routing doesn't require configuration. If you opt for private routing, work with your network admin to create private endpoints and DNS in your own virtual network (preferred) or configure virtual network peering and routing (secondary). Before you decide, learn more about the ownership boundaries and configuration steps in [Configure private networking](private-networking.md).
9090

9191
<br>
9292

237 KB
Loading
170 KB
Loading
232 KB
Loading
119 KB
Loading

0 commit comments

Comments
 (0)