Skip to content

Repository files navigation

SubscriptionCostAlert

PowerShell tooling for subscription-scoped Azure Cost Management alerts. The module creates, inventories, updates, and removes monthly budgets and Cost Management InsightAlert anomaly alerts. It also includes bounded deployment and cleanup scripts for multiple subscriptions.

Get Started

The ready-built module is in SubscriptionCostAlert; building is not required to use it. From the repository root, authenticate to Azure and import the module manifest:

az login
Import-Module ./SubscriptionCostAlert/SubscriptionCostAlert.psd1 -Force
Get-Command -Module SubscriptionCostAlert

Run az bicep install before using New-SubscriptionCostAlert or the bulk deployment script. Build only when you modify files under src/; by default, the build writes the refreshed module to the root SubscriptionCostAlert folder:

./Build-SubscriptionCostAlertModule.ps1

Before creating or changing an alert, inventory the configured alerts visible to the current Azure CLI tenant:

Get-SubscriptionCostAlert

Preview a budget update before making any Azure changes:

Set-SubscriptionCostAlert `
    -SubscriptionId '00000000-0000-0000-0000-000000000000' `
    -AlertType Budget `
    -BudgetName 'engineering-monthly-budget' `
    -BudgetAmount 2000 `
    -ForecastedPercent 80 `
    -DisableActual `
    -WhatIf

Remove -WhatIf only after reviewing the preview. The module uses the current Azure CLI login and requires access to Cost Management resources in each target subscription.

Module Commands

The module exports four commands:

  • New-SubscriptionCostAlert creates one Budget or Anomaly alert.
  • Get-SubscriptionCostAlert inventories Cost Management alert rules.
  • Set-SubscriptionCostAlert updates recipients and existing budget settings.
  • Remove-SubscriptionCostAlert removes one named Budget or Anomaly alert.

Create an alert

Create or preview a monthly budget. -BudgetAmount and -EmailRecipient are required for Budget alerts. ActualPercent and ForecastedPercent accept multiple values, up to five of each:

New-SubscriptionCostAlert `
    -SubscriptionId '00000000-0000-0000-0000-000000000000' `
    -AlertType Budget `
    -BudgetName 'engineering-monthly-budget' `
    -BudgetAmount 1000 `
    -EmailRecipient 'finops@contoso.com', 'owner@contoso.com' `
    -ActualPercent 80, 120 `
    -ForecastedPercent 90, 110 `
    -WhatIf

Create or preview an anomaly alert. -NotificationEmail is optional and defaults to the first recipient:

New-SubscriptionCostAlert `
    -SubscriptionId '00000000-0000-0000-0000-000000000000' `
    -AlertType Anomaly `
    -EmailRecipient 'finops@contoso.com', 'owner@contoso.com' `
    -NotificationEmail 'owner@contoso.com' `
    -WhatIf

Inventory alerts

Inventory the current Azure CLI tenant, a specific subscription, missing standard coverage, or every visible tenant:

Get-SubscriptionCostAlert

Get-SubscriptionCostAlert `
    -SubscriptionId '00000000-0000-0000-0000-000000000000'

Get-SubscriptionCostAlert -Missing
Get-SubscriptionCostAlert -Missing -AllTenants

The command is read-only. It returns normalized PowerShell objects and retains endpoint errors so inaccessible subscriptions are not reported as missing.

Update recipients and budget settings

With the default -AlertType All, a recipient replacement applies to both the standard anomaly action and the budget named after the subscription:

Set-SubscriptionCostAlert `
    -SubscriptionId '00000000-0000-0000-0000-000000000000' `
    -EmailRecipient 'finops@contoso.com', 'owner@contoso.com' `
    -WhatIf

Use a hashtable to add or remove recipients incrementally:

Set-SubscriptionCostAlert `
    -SubscriptionId '00000000-0000-0000-0000-000000000000' `
    -AlertType Budget `
    -BudgetName 'engineering-monthly-budget' `
    -EmailRecipient @{ Add = 'new-owner@contoso.com'; Remove = 'former-owner@contoso.com' } `
    -WhatIf

For -AlertType Budget, -BudgetName selects an existing Cost Management budget; it does not rename it. Supply one or more budget settings to update only those values while preserving unrequested budget properties. The percent parameters accept arrays. A multi-value request reconciles that notification family to the supplied thresholds, while a single-value request preserves the existing single-threshold update behavior:

Set-SubscriptionCostAlert `
    -SubscriptionId '00000000-0000-0000-0000-000000000000' `
    -AlertType Budget `
    -BudgetName 'engineering-monthly-budget' `
    -BudgetAmount 2000 `
    -ActualPercent 90, 110 `
    -ForecastedPercent 80, 100 `
    -WhatIf

Use -DisableActual or -DisableForecasted to set that notification's enabled value to false without deleting it. When a supplied Actual or Forecasted percentage has no matching notification, the command creates one using the existing budget notification recipients. Omitted settings remain unchanged.

Remove an alert

Removal is high impact. Preview the targeted resource first:

Remove-SubscriptionCostAlert `
    -SubscriptionId '00000000-0000-0000-0000-000000000000' `
    -AlertType Budget `
    -BudgetName 'engineering-monthly-budget' `
    -WhatIf

Remove-SubscriptionCostAlert `
    -SubscriptionId '00000000-0000-0000-0000-000000000000' `
    -AlertType Anomaly `
    -ActionName 'SubscriptionAnomalyAlert' `
    -WhatIf

Budget removal requires -BudgetName; anomaly removal requires -ActionName. The budget workflow treats a not-found budget as an idempotent completed state.

Multi-Subscription Scripts

Deploy-AllSubscriptionCostAlerts.ps1 discovers Enabled subscriptions in the current Azure CLI account and deploys the standard anomaly and budget alerts to each one. It returns structured results and continues after an individual rule failure:

./Deploy-AllSubscriptionCostAlerts.ps1 `
    -EmailRecipient 'finops@contoso.com', 'owner@contoso.com' `
    -BudgetAmount 1000 `
    -WhatIf

For subscription-specific configuration, use the mutually exclusive -CsvPath mode. The CSV must contain EmailRecipient, AlertType, SubscriptionId, SubscriptionName, and BudgetAmount. An optional AlertName column selects the Azure budget name; when blank, the module uses the subscription-derived default. Budget rows must contain at least one Actual threshold column (BudgetPercent or BudgetPercent1) and at least one Forecasted threshold column (ForecastedPercent or ForecastedPercent1). Add BudgetPercent2 through BudgetPercent5 and ForecastedPercent2 through ForecastedPercent5 for additional thresholds. The unsuffixed columns and the 1 columns are aliases for the first threshold and must not contain conflicting values. Azure Cost budgets support up to five Actual and five Forecasted notifications per budget. Separate multiple recipients in one EmailRecipient cell with semicolons. SubscriptionName is optional report-only metadata; SubscriptionId identifies the Azure target. Budget rows require positive threshold values, while Anomaly rows must leave BudgetAmount and all threshold columns blank.

EmailRecipient,AlertType,SubscriptionId,SubscriptionName,AlertName,BudgetAmount,BudgetPercent,ForecastedPercent,BudgetPercent2,ForecastedPercent2
finops@contoso.com;owner@contoso.com,Budget,00000000-0000-0000-0000-000000000000,Engineering,engineering-monthly-budget,1000,80,90,120,110
finops@contoso.com,Anomaly,11111111-1111-1111-1111-111111111111,Shared Services,,,,,,

Preview every requested CSV deployment before applying it:

./Deploy-AllSubscriptionCostAlerts.ps1 `
    -CsvPath ./subscription-cost-alerts.csv `
    -WhatIf

Remove-AllSubscriptionCostAlerts.ps1 removes the standard budget named after each subscription and the SubscriptionAnomalyAlert action. Supply either a direct ID array or a tenant-aware CSV; the parameter sets are mutually exclusive. The script previews every target, requests one high-impact confirmation for a live cleanup, continues after independent failures, and supports -WhatIf.

For direct IDs, Azure CLI refreshes account discovery to resolve each subscription name and tenant before removal:

./Remove-AllSubscriptionCostAlerts.ps1 `
    -SubscriptionId `
        '00000000-0000-0000-0000-000000000000',
        '11111111-1111-1111-1111-111111111111' `
    -WhatIf

For explicitly tenant-scoped removal, the CSV must begin with the columns SubscriptionName,SubscriptionId,TenantID. SubscriptionName is the budget resource name to remove, and the script verifies that each ID belongs to its declared tenant after authentication:

SubscriptionName,SubscriptionId,TenantID
Engineering,00000000-0000-0000-0000-000000000000,aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
Shared Services,11111111-1111-1111-1111-111111111111,bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb
./Remove-AllSubscriptionCostAlerts.ps1 `
    -CsvPath ./subscription-cost-alert-removals.csv `
    -WhatIf

Billing Subscription Inventory

Get-BillingSubscriptions.ps1 lists current subscriptions billed to a billing account. With no parameters, it discovers one accessible billing account; if multiple accounts are available, it lists their IDs and requires an explicit selection. It also accepts either the billing-account name or its full ARM resource ID, follows all API result pages, and does not change the active Azure CLI subscription:

./Get-BillingSubscriptions.ps1

Select a specific billing account when more than one is accessible:

./Get-BillingSubscriptions.ps1 `
    -BillingAccountId '00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000_2019-05-31'

Validate Changes

Build the package after source changes, then run the mock-only Pester tests:

./Build-SubscriptionCostAlertModule.ps1

Invoke-Pester ./tests

The build parses source fragments, builds the generated module, validates the manifest, imports the package, and confirms the four exported functions. The tests do not make live Azure calls.

View Accessible Subscriptions

Before targeting a subscription, list the subscriptions available to your authenticated PowerShell or Azure CLI session. In PowerShell, use the Az module to return the subscription name and identifier as objects:

Get-AzSubscription |
    Select-Object Name, Id

Use Azure CLI directly to display the same values as a table:

az account subscription list --query '[].{DisplayName:displayName,SubscriptionId:subscriptionId}' -o table

Prerequisites and Permissions

  • PowerShell 7 or Windows PowerShell 5.1.
  • Azure CLI with Bicep installed (az bicep install).
  • An authenticated Azure CLI session (az login).
  • Read access to enumerate inventory targets.
  • Permission to create, update, or remove Cost Management budgets and scheduled actions in each target subscription.

The module restores Azure CLI subscription context for create and removal workflows where it changes the target subscription. Use -WhatIf for all write-capable commands before a live run.

About

Custom tooling for configuring Azure subscription budget and anomaly alerts

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages