Public/Get-SubscriptionCost.ps1
function Get-SubscriptionCost { <# .SYNOPSIS Retrieves the Azure costs for one or more billing months for one or more subscriptions. .DESCRIPTION Invokes the Get-AzConsumptionUsageDetail cmdlet against one or more subscriptions to return billing data for a specified number of months. If you are interested to see how costs have changed since the previous mont use the -ComparePrevious switch to return additional properties that contain the cost data for the previous month and properties that calculate the cost difference. .PARAMETER SubscriptionName The name or name/s of the Subscriptions to query. If not specified all subscriptions available in the current context will be used. .PARAMETER BillingMonth The billing month to query for cost data, specified as a [datetime] object. You can specify just month/year, e.g 10/2023. If not specified uses the current date. .PARAMETER PreviousMonths The number of previous billing months to query. Default: 0. .PARAMETER SparkLineSize The row height of sparklines to generate (requires PSparkines module). Default: 1. .PARAMETER ComparePrevious Switch: Include values for the previous billing month and adds additional properties that compare the current month to the previous. .PARAMETER Raw Switch: Include the raw cost consumption data as a property on the returned object. .EXAMPLE Get-SubscriptionCost Description ----------- Returns costs for the current billing month for all subscriptions in the current Azure context. .EXAMPLE Get-SubscriptionCost -SubscriptionName 'MySubscriptionA' Description ----------- Returns costs for the current billing month for the specified subscription name. .EXAMPLE Get-SubscriptionCost -SubscriptionName 'MySubscriptionA','MySubscriptionB' Description ----------- Returns costs for the current billing month for the specified subscription names. .EXAMPLE Get-SubscriptionCost -BillingMonth 01/2024 -PreviousMonths 3 Description ----------- Returns costs from October 2023 to January 2024 for all subscriptions in the current Azure context. .EXAMPLE Get-SubscriptionCost -BillingMonth 01/2024 -PreviousMonths 3 -ComparePrevious Description ----------- Returns costs from October 2023 to January 2024 for all subscriptions in the current Azure context and includes properties for comparing each month with the one prior. #> [CmdletBinding()] param( [string[]] $SubscriptionName, [datetime] $BillingMonth = (Get-Date), [ValidateRange(0, 12)] [int] $PreviousMonths = 0, [ValidateRange(1, 10)] [int] $SparkLineSize = 1, [switch] $ComparePrevious, [switch] $Raw ) # Store the users current AZ context before we start $PreAzContext = Get-AzContext try { if (-not $SubscriptionName) { $SubscriptionName = (Get-AzSubscription).Name } foreach ($Name in $SubscriptionName) { $Consumption = $null $PrevConsumption = $null Set-AzContext -Subscription $Name -ErrorAction Stop | Out-Null for ($BillingMonthCount = 0; $BillingMonthCount -le $PreviousMonths; $BillingMonthCount++) { $BillingDate = (Get-Date $BillingMonth).AddMonths(-$BillingMonthCount) $BillingPeriod = $BillingDate.ToString('yyyyMM') $PrevBillingDate = (Get-Date $BillingMonth).AddMonths( - (1 + $BillingMonthCount)) $PrevBillingPeriod = $PrevBillingDate.ToString('yyyyMM') try { $Consumption = if ($PrevConsumption) { $PrevConsumption } else { Write-Progress -Activity "Getting data for billing period $BillingPeriod" -Status $Name Get-AzConsumptionUsageDetail -BillingPeriodName $BillingPeriod -ErrorAction Stop } $Currency = ($Consumption | Select-Object -First 1).Currency $Cost = ($Consumption | Measure-Object -Property PretaxCost -Sum).Sum $DailyCost = Get-DailyCost -Consumption $Consumption $DailyCostCalc = $DailyCost.Cost | Measure-Object -Maximum -Minimum -Average -Sum $CostPerService = Get-ServiceCost -Consumption $Consumption $Budgets = Get-AzConsumptionBudget $ActiveBudgets = foreach ($Budget in $Budgets) { if ($BillingDate -ge $Budget.TimePeriod.StartDate -and $Budget.TimePeriod.EndDate -ge $BillingDate) { [pscustomobject]@{ BudgetAmount = $Budget.Amount BudgetTimeGrain = $Budget.TimeGrain } } } if (Test-PSparklinesModule) { $CostSparkLine = Get-Sparkline $DailyCost.Cost -NumLines $SparkLineSize | Write-Sparkline } $CostObject = [ordered]@{ PSTypeName = 'Subscription.Cost' Name = $Name BillingPeriod = $BillingPeriod Currency = $Currency Cost = [math]::Round($Cost, 2) DailyCost_SparkLine = ($CostSparkLine -join "`n") DailyCost_Min = [math]::Round(($DailyCostCalc).Minimum, 2) DailyCost_Max = [math]::Round(($DailyCostCalc).Maximum, 2) DailyCost_Avg = [math]::Round(($DailyCostCalc).Average, 2) MostExpensive_Date = ($DailyCost | Sort-Object Cost -Descending | Select-Object -First 1).Date LeastExpensive_Date = ($DailyCost | Sort-Object Cost | Select-Object -First 1).Date DailyCost = $DailyCost CostPerService = $CostPerService MostExpensiveService = ($CostPerService | Sort-Object Cost -Descending | Select-Object -First 1).Service MostExpensiveService_Cost = [math]::Round(($CostPerService | Sort-Object Cost -Descending | Select-Object -First 1).Cost, 2) LeastExpensiveService = ($CostPerService | Sort-Object Cost | Select-Object -First 1).Service LeastExpensiveService_Cost = [math]::Round(($CostPerService | Sort-Object Cost | Select-Object -First 1).Cost, 2) ActiveBudgets = $ActiveBudgets } if ($ComparePrevious) { Write-Progress -Activity "Getting data for previous billing period $PrevBillingPeriod" -Status $Name $PrevConsumption = Get-AzConsumptionUsageDetail -BillingPeriodName $PrevBillingPeriod -ErrorAction Stop $PrevCost = ($PrevConsumption | Measure-Object -Property PretaxCost -Sum).Sum $PrevDailyCost = Get-DailyCost -Consumption $PrevConsumption $PrevDailyCostCalc = $PrevDailyCost.Cost | Measure-Object -Maximum -Minimum -Average -Sum $PrevCostPerService = Get-ServiceCost -Consumption $PrevConsumption $CostChange = $Cost - $PrevCost $ChangePct = $CostChange / $PrevCost $DailyCostChange = Get-DailyCostChange -DailyCost $DailyCost -PrevDailyCost $PrevDailyCost if (Test-PSparklinesModule) { $PrevCostSparkLine = Get-Sparkline $PrevDailyCost.Cost -NumLines $SparkLineSize | Write-Sparkline } $ComparePreviousCostObject = [ordered]@{ PrevBillingPeriod = $PrevBillingPeriod PrevCost = [math]::Round($PrevCost, 2) PrevDailyCost_SparkLine = ($PrevCostSparkLine -join "`n") PrevDailyCost_Min = [math]::Round(($PrevDailyCostCalc).Minimum, 2) PrevDailyCost_Max = [math]::Round(($PrevDailyCostCalc).Maximum, 2) PrevDailyCost_Avg = [math]::Round(($PrevDailyCostCalc).Average, 2) PrevMostExpensiveDate = ($DailyCost | Sort-Object Cost -Descending | Select-Object -First 1).Date PrevLeastExpensiveDate = ($DailyCost | Sort-Object Cost | Select-Object -First 1).Date PrevDailyCost = $PrevDailyCost PrevCostPerService = $PrevCostPerService PrevMostExpensiveService = ($PrevCostPerService | Sort-Object Cost -Descending | Select-Object -First 1).Service PrevMostExpensiveService_Cost = [math]::Round(($PrevCostPerService | Sort-Object Cost -Descending | Select-Object -First 1).Cost, 2) PrevLeastExpensiveService = ($PrevCostPerService | Sort-Object Cost | Select-Object -First 1).Service PrevLeastExpensiveService_Cost = [math]::Round(($PrevCostPerService | Sort-Object Cost | Select-Object -First 1).Cost, 2) CostChange = [math]::Round($CostChange, 2) CostChange_Pct = "{0:p2}" -f $ChangePct DailyCostChange = $DailyCostChange } $CostObject += $ComparePreviousCostObject $CostObject['PSTypeName'] = 'Subscription.Cost.ComparePrev' } if ($Raw) { $RawCostObject = [ordered]@{ Consumption_Raw = $Consumption } $CostObject += $RawCostObject } if ($ComparePrevious -and $Raw) { $PrevRawCostObject = [ordered]@{ PrevConsumption_Raw = $PrevConsumption } $CostObject += $PrevRawCostObject } [pscustomobject]$CostObject } catch { Write-Error "Error retrieving costs for subscription ${Name}: $($_.Exception)" } } } } catch { throw $_ } finally { # Return the user to their previous AZ context (in case it has changed). $PreAzContext | Set-AzContext | Out-Null } } |