Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs-mslearn/toolkit/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ _Released April 2026_
- Azure Hybrid Benefit doesn't apply to Dev/Test resources as Windows licenses are already covered by Visual Studio subscriptions.
- Fixed Azure Hybrid Benefit reports to include Windows VMs from all publishers, not just Microsoft-published images ([#1793](https://github.com/microsoft/finops-toolkit/issues/1793)).

### [Optimization engine](optimization-engine/overview.md) v14

- **Changed**
- Upgraded the Azure EA/MCA Pricesheet download API version used by the pricesheet export runbook (previously used version is being deprecated in June 1, 2026).

### [Open data](open-data.md) v14

**[Commitment discount eligibility](open-data.md#commitment-discount-eligibility)**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,24 @@ param(

$ErrorActionPreference = "Stop"

function Authenticate-AzureWithOption {
function Authenticate-AzureWithOption
{
param (
[string] $authOption = "ManagedIdentity",
[string] $cloudEnv = "AzureCloud",
[string] $clientID
)

switch ($authOption) {
"UserAssignedManagedIdentity" {
switch ($authOption)
{
"UserAssignedManagedIdentity"
{
Connect-AzAccount -Identity -EnvironmentName $cloudEnv -AccountId $clientID
break
}
Default { #ManagedIdentity
Default
{
#ManagedIdentity
Connect-AzAccount -Identity -EnvironmentName $cloudEnv
break
}
Expand Down Expand Up @@ -149,7 +154,8 @@ if ([string]::IsNullOrEmpty($BillingAccountID))
{
throw "Billing Account ID undefined. Use either the AzureOptimization_BillingAccountID variable or the BillingAccountID parameter"
}
else {
else
{
if ($BillingAccountID -match $mcaBillingAccountIdRegex)
{
if ([string]::IsNullOrEmpty($BillingProfileID))
Expand Down Expand Up @@ -186,7 +192,8 @@ if (-not([string]::IsNullOrEmpty($meterRegions)))
$meterRegionFilters = $meterRegions.Split(',')
}

function Generate-Pricesheet {
function Generate-Pricesheet
{
param (
[string] $InputCSVPath,
[string] $OutputCSVPath,
Expand All @@ -195,35 +202,36 @@ function Generate-Pricesheet {

# header normalization between MCA and EA
$headerConversion = @{
'Meter ID' = "MeterID";
meterId = "MeterID";
'Meter name' = "MeterName";
meterName = "MeterName";
'Meter category' = "MeterCategory";
meterCategory = "MeterCategory";
'Meter sub-category' = "MeterSubCategory";
meterSubCategory = "MeterSubCategory";
'Meter region' = "MeterRegion";
meterRegion = "MeterRegion";
'Unit of measure' = "UnitOfMeasure";
unitOfMeasure = "UnitOfMeasure";
'Part number' = "PartNumber";
'Unit price' = "UnitPrice";
unitPrice = "UnitPrice";
'Currency code' = "CurrencyCode";
currency = "CurrencyCode";
'Included quantity' = "IncludedQuantity";
includedQuantity = "IncludedQuantity";
'Offer Id' = "OfferId";
Term = "Term";
'Price type' = "PriceType";
priceType = "PriceType"
'Meter ID' = "MeterID"
meterId = "MeterID"
'Meter name' = "MeterName"
meterName = "MeterName"
'Meter category' = "MeterCategory"
meterCategory = "MeterCategory"
'Meter sub-category' = "MeterSubCategory"
meterSubCategory = "MeterSubCategory"
'Meter region' = "MeterRegion"
meterRegion = "MeterRegion"
'Unit of measure' = "UnitOfMeasure"
unitOfMeasure = "UnitOfMeasure"
'Part number' = "PartNumber"
'Unit price' = "UnitPrice"
unitPrice = "UnitPrice"
'Currency code' = "CurrencyCode"
currency = "CurrencyCode"
'Included quantity' = "IncludedQuantity"
includedQuantity = "IncludedQuantity"
'Offer Id' = "OfferId"
Term = "Term"
'Price type' = "PriceType"
priceType = "PriceType"
}

$r = [IO.File]::OpenText($InputCSVPath)
$w = [System.IO.StreamWriter]::new($OutputCSVPath)
$lineCounter = 0
while ($r.Peek() -ge 0) {
while ($r.Peek() -ge 0)
{
$line = $r.ReadLine()
$lineCounter++
if ($lineCounter -eq $HeaderLine)
Expand Down Expand Up @@ -286,7 +294,7 @@ function Generate-Pricesheet {
$w.Close()

$csvBlobName = [System.IO.Path]::GetFileName($OutputCSVPath)
$csvProperties = @{"ContentType" = "text/csv"};
$csvProperties = @{"ContentType" = "text/csv" }
Set-AzStorageBlobContent -File $OutputCSVPath -Container $storageAccountSinkContainer -Properties $csvProperties -Blob $csvBlobName -Context $saCtx -Force

$now = (Get-Date).ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'")
Expand All @@ -305,21 +313,21 @@ function Generate-Pricesheet {

Write-Output "Starting pricesheet export process for $billingPeriod billing period for Billing Account $BillingAccountID..."

$MaxTries = 30 # The typical Retry-After is set to 20 seconds. We'll give 10 minutes overall to download the pricesheet report
$MaxTries = 50 # The typical Retry-After is set to 20 seconds. We'll give ~15 minutes overall to download the pricesheet report

if ($BillingAccountID -match $mcaBillingAccountIdRegex)
{
$PriceSheetApiPath = "/providers/Microsoft.Billing/billingAccounts/$BillingAccountID/billingProfiles/$BillingProfileID/providers/Microsoft.CostManagement/pricesheets/default/download?api-version=2023-03-01&format=csv"
$PriceSheetApiPath = "/providers/Microsoft.Billing/billingAccounts/$BillingAccountID/billingProfiles/$BillingProfileID/providers/Microsoft.CostManagement/pricesheets/default/download?api-version=2023-11-01&format=csv"
$result = Invoke-AzRestMethod -Path $PriceSheetApiPath -Method POST
}
else
{
$PriceSheetApiPath = "/providers/Microsoft.Billing/billingAccounts/$BillingAccountID/billingPeriods/$billingPeriod/providers/Microsoft.Consumption/pricesheets/download?api-version=2022-06-01&ln=en"
$PriceSheetApiPath = "/providers/Microsoft.Billing/billingAccounts/$BillingAccountID/billingPeriods/$billingPeriod/providers/Microsoft.Consumption/pricesheets/download?api-version=2023-11-01&ln=en"
$result = Invoke-AzRestMethod -Path $PriceSheetApiPath -Method GET
}

$requestResultPath = $result.Headers.Location.PathAndQuery
if ($result.StatusCode -in (200,202))
if ($result.StatusCode -in (200, 202))
{
$tries = 0
$requestSuccess = $false
Expand Down