Configure Azure Cost Management Budgets

The azure-native:costmanagement:Budget resource, part of the Pulumi Azure Native provider, defines cost budgets and reservation utilization alerts at various Azure billing scopes. This guide focuses on three capabilities: cost tracking with filtered notifications, reservation utilization monitoring, and dimension-based filtering for targeted alerts.

Budgets reference existing billing scopes and may trigger Action Groups for notifications. The examples are intentionally small. Combine them with your own billing hierarchy, notification channels, and filtering logic.

Track subscription costs with filtered notifications

Most cost management workflows begin by setting a spending limit and configuring alerts when actual costs approach that threshold.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const budget = new azure_native.costmanagement.Budget("budget", {
    amount: 100.65,
    budgetName: "TestBudget",
    category: azure_native.costmanagement.CategoryType.Cost,
    eTag: "\"1d34d016a593709\"",
    filter: {
        and: [
            {
                dimensions: {
                    name: "ResourceId",
                    operator: azure_native.costmanagement.BudgetOperatorType.In,
                    values: [
                        "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/MSVM2",
                        "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/platformcloudplatformGeneric1",
                    ],
                },
            },
            {
                tags: {
                    name: "category",
                    operator: azure_native.costmanagement.BudgetOperatorType.In,
                    values: [
                        "Dev",
                        "Prod",
                    ],
                },
            },
            {
                tags: {
                    name: "department",
                    operator: azure_native.costmanagement.BudgetOperatorType.In,
                    values: [
                        "engineering",
                        "sales",
                    ],
                },
            },
        ],
    },
    notifications: {
        Actual_GreaterThan_80_Percent: {
            contactEmails: [
                "johndoe@contoso.com",
                "janesmith@contoso.com",
            ],
            contactGroups: ["/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/microsoft.insights/actionGroups/SampleActionGroup"],
            contactRoles: [
                "Contributor",
                "Reader",
            ],
            enabled: true,
            locale: azure_native.costmanagement.CultureCode.En_us,
            operator: azure_native.costmanagement.BudgetNotificationOperatorType.GreaterThan,
            threshold: 80,
            thresholdType: azure_native.costmanagement.ThresholdType.Actual,
        },
    },
    scope: "subscriptions/00000000-0000-0000-0000-000000000000",
    timeGrain: azure_native.costmanagement.TimeGrainType.Monthly,
    timePeriod: {
        endDate: "2024-10-31T00:00:00Z",
        startDate: "2023-04-01T00:00:00Z",
    },
});
import pulumi
import pulumi_azure_native as azure_native

budget = azure_native.costmanagement.Budget("budget",
    amount=100.65,
    budget_name="TestBudget",
    category=azure_native.costmanagement.CategoryType.COST,
    e_tag="\"1d34d016a593709\"",
    filter={
        "and_": [
            {
                "dimensions": {
                    "name": "ResourceId",
                    "operator": azure_native.costmanagement.BudgetOperatorType.IN_,
                    "values": [
                        "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/MSVM2",
                        "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/platformcloudplatformGeneric1",
                    ],
                },
            },
            {
                "tags": {
                    "name": "category",
                    "operator": azure_native.costmanagement.BudgetOperatorType.IN_,
                    "values": [
                        "Dev",
                        "Prod",
                    ],
                },
            },
            {
                "tags": {
                    "name": "department",
                    "operator": azure_native.costmanagement.BudgetOperatorType.IN_,
                    "values": [
                        "engineering",
                        "sales",
                    ],
                },
            },
        ],
    },
    notifications={
        "Actual_GreaterThan_80_Percent": {
            "contact_emails": [
                "johndoe@contoso.com",
                "janesmith@contoso.com",
            ],
            "contact_groups": ["/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/microsoft.insights/actionGroups/SampleActionGroup"],
            "contact_roles": [
                "Contributor",
                "Reader",
            ],
            "enabled": True,
            "locale": azure_native.costmanagement.CultureCode.EN_US,
            "operator": azure_native.costmanagement.BudgetNotificationOperatorType.GREATER_THAN,
            "threshold": 80,
            "threshold_type": azure_native.costmanagement.ThresholdType.ACTUAL,
        },
    },
    scope="subscriptions/00000000-0000-0000-0000-000000000000",
    time_grain=azure_native.costmanagement.TimeGrainType.MONTHLY,
    time_period={
        "end_date": "2024-10-31T00:00:00Z",
        "start_date": "2023-04-01T00:00:00Z",
    })
package main

import (
	costmanagement "github.com/pulumi/pulumi-azure-native-sdk/costmanagement/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := costmanagement.NewBudget(ctx, "budget", &costmanagement.BudgetArgs{
			Amount:     pulumi.Float64(100.65),
			BudgetName: pulumi.String("TestBudget"),
			Category:   pulumi.String(costmanagement.CategoryTypeCost),
			ETag:       pulumi.String("\"1d34d016a593709\""),
			Filter: &costmanagement.BudgetFilterArgs{
				And: costmanagement.BudgetFilterPropertiesArray{
					&costmanagement.BudgetFilterPropertiesArgs{
						Dimensions: &costmanagement.BudgetComparisonExpressionArgs{
							Name:     pulumi.String("ResourceId"),
							Operator: pulumi.String(costmanagement.BudgetOperatorTypeIn),
							Values: pulumi.StringArray{
								pulumi.String("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/MSVM2"),
								pulumi.String("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/platformcloudplatformGeneric1"),
							},
						},
					},
					&costmanagement.BudgetFilterPropertiesArgs{
						Tags: &costmanagement.BudgetComparisonExpressionArgs{
							Name:     pulumi.String("category"),
							Operator: pulumi.String(costmanagement.BudgetOperatorTypeIn),
							Values: pulumi.StringArray{
								pulumi.String("Dev"),
								pulumi.String("Prod"),
							},
						},
					},
					&costmanagement.BudgetFilterPropertiesArgs{
						Tags: &costmanagement.BudgetComparisonExpressionArgs{
							Name:     pulumi.String("department"),
							Operator: pulumi.String(costmanagement.BudgetOperatorTypeIn),
							Values: pulumi.StringArray{
								pulumi.String("engineering"),
								pulumi.String("sales"),
							},
						},
					},
				},
			},
			Notifications: costmanagement.NotificationMap{
				"Actual_GreaterThan_80_Percent": &costmanagement.NotificationArgs{
					ContactEmails: pulumi.StringArray{
						pulumi.String("johndoe@contoso.com"),
						pulumi.String("janesmith@contoso.com"),
					},
					ContactGroups: pulumi.StringArray{
						pulumi.String("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/microsoft.insights/actionGroups/SampleActionGroup"),
					},
					ContactRoles: pulumi.StringArray{
						pulumi.String("Contributor"),
						pulumi.String("Reader"),
					},
					Enabled:       pulumi.Bool(true),
					Locale:        pulumi.String(costmanagement.CultureCode_En_Us),
					Operator:      pulumi.String(costmanagement.BudgetNotificationOperatorTypeGreaterThan),
					Threshold:     pulumi.Float64(80),
					ThresholdType: pulumi.String(costmanagement.ThresholdTypeActual),
				},
			},
			Scope:     pulumi.String("subscriptions/00000000-0000-0000-0000-000000000000"),
			TimeGrain: pulumi.String(costmanagement.TimeGrainTypeMonthly),
			TimePeriod: &costmanagement.BudgetTimePeriodArgs{
				EndDate:   pulumi.String("2024-10-31T00:00:00Z"),
				StartDate: pulumi.String("2023-04-01T00:00:00Z"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var budget = new AzureNative.CostManagement.Budget("budget", new()
    {
        Amount = 100.65,
        BudgetName = "TestBudget",
        Category = AzureNative.CostManagement.CategoryType.Cost,
        ETag = "\"1d34d016a593709\"",
        Filter = new AzureNative.CostManagement.Inputs.BudgetFilterArgs
        {
            And = new[]
            {
                new AzureNative.CostManagement.Inputs.BudgetFilterPropertiesArgs
                {
                    Dimensions = new AzureNative.CostManagement.Inputs.BudgetComparisonExpressionArgs
                    {
                        Name = "ResourceId",
                        Operator = AzureNative.CostManagement.BudgetOperatorType.In,
                        Values = new[]
                        {
                            "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/MSVM2",
                            "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/platformcloudplatformGeneric1",
                        },
                    },
                },
                new AzureNative.CostManagement.Inputs.BudgetFilterPropertiesArgs
                {
                    Tags = new AzureNative.CostManagement.Inputs.BudgetComparisonExpressionArgs
                    {
                        Name = "category",
                        Operator = AzureNative.CostManagement.BudgetOperatorType.In,
                        Values = new[]
                        {
                            "Dev",
                            "Prod",
                        },
                    },
                },
                new AzureNative.CostManagement.Inputs.BudgetFilterPropertiesArgs
                {
                    Tags = new AzureNative.CostManagement.Inputs.BudgetComparisonExpressionArgs
                    {
                        Name = "department",
                        Operator = AzureNative.CostManagement.BudgetOperatorType.In,
                        Values = new[]
                        {
                            "engineering",
                            "sales",
                        },
                    },
                },
            },
        },
        Notifications = 
        {
            { "Actual_GreaterThan_80_Percent", new AzureNative.CostManagement.Inputs.NotificationArgs
            {
                ContactEmails = new[]
                {
                    "johndoe@contoso.com",
                    "janesmith@contoso.com",
                },
                ContactGroups = new[]
                {
                    "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/microsoft.insights/actionGroups/SampleActionGroup",
                },
                ContactRoles = new[]
                {
                    "Contributor",
                    "Reader",
                },
                Enabled = true,
                Locale = AzureNative.CostManagement.CultureCode.En_us,
                Operator = AzureNative.CostManagement.BudgetNotificationOperatorType.GreaterThan,
                Threshold = 80,
                ThresholdType = AzureNative.CostManagement.ThresholdType.Actual,
            } },
        },
        Scope = "subscriptions/00000000-0000-0000-0000-000000000000",
        TimeGrain = AzureNative.CostManagement.TimeGrainType.Monthly,
        TimePeriod = new AzureNative.CostManagement.Inputs.BudgetTimePeriodArgs
        {
            EndDate = "2024-10-31T00:00:00Z",
            StartDate = "2023-04-01T00:00:00Z",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.costmanagement.Budget;
import com.pulumi.azurenative.costmanagement.BudgetArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetFilterArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetTimePeriodArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var budget = new Budget("budget", BudgetArgs.builder()
            .amount(100.65)
            .budgetName("TestBudget")
            .category("Cost")
            .eTag("\"1d34d016a593709\"")
            .filter(BudgetFilterArgs.builder()
                .and(                
                    BudgetFilterPropertiesArgs.builder()
                        .dimensions(BudgetComparisonExpressionArgs.builder()
                            .name("ResourceId")
                            .operator("In")
                            .values(                            
                                "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/MSVM2",
                                "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/platformcloudplatformGeneric1")
                            .build())
                        .build(),
                    BudgetFilterPropertiesArgs.builder()
                        .tags(BudgetComparisonExpressionArgs.builder()
                            .name("category")
                            .operator("In")
                            .values(                            
                                "Dev",
                                "Prod")
                            .build())
                        .build(),
                    BudgetFilterPropertiesArgs.builder()
                        .tags(BudgetComparisonExpressionArgs.builder()
                            .name("department")
                            .operator("In")
                            .values(                            
                                "engineering",
                                "sales")
                            .build())
                        .build())
                .build())
            .notifications(Map.of("Actual_GreaterThan_80_Percent", NotificationArgs.builder()
                .contactEmails(                
                    "johndoe@contoso.com",
                    "janesmith@contoso.com")
                .contactGroups("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/microsoft.insights/actionGroups/SampleActionGroup")
                .contactRoles(                
                    "Contributor",
                    "Reader")
                .enabled(true)
                .locale("en-us")
                .operator("GreaterThan")
                .threshold(80.0)
                .thresholdType("Actual")
                .build()))
            .scope("subscriptions/00000000-0000-0000-0000-000000000000")
            .timeGrain("Monthly")
            .timePeriod(BudgetTimePeriodArgs.builder()
                .endDate("2024-10-31T00:00:00Z")
                .startDate("2023-04-01T00:00:00Z")
                .build())
            .build());

    }
}
resources:
  budget:
    type: azure-native:costmanagement:Budget
    properties:
      amount: 100.65
      budgetName: TestBudget
      category: Cost
      eTag: '"1d34d016a593709"'
      filter:
        and:
          - dimensions:
              name: ResourceId
              operator: In
              values:
                - /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/MSVM2
                - /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/Microsoft.Compute/virtualMachines/platformcloudplatformGeneric1
          - tags:
              name: category
              operator: In
              values:
                - Dev
                - Prod
          - tags:
              name: department
              operator: In
              values:
                - engineering
                - sales
      notifications:
        Actual_GreaterThan_80_Percent:
          contactEmails:
            - johndoe@contoso.com
            - janesmith@contoso.com
          contactGroups:
            - /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MYDEVTESTRG/providers/microsoft.insights/actionGroups/SampleActionGroup
          contactRoles:
            - Contributor
            - Reader
          enabled: true
          locale: en-us
          operator: GreaterThan
          threshold: 80
          thresholdType: Actual
      scope: subscriptions/00000000-0000-0000-0000-000000000000
      timeGrain: Monthly
      timePeriod:
        endDate: 2024-10-31T00:00:00Z
        startDate: 2023-04-01T00:00:00Z

When actual spending crosses the threshold percentage, Azure sends notifications to the specified email addresses, Action Groups, and RBAC roles. The filter property narrows the budget scope using dimensions (like ResourceId) and tags (like category or department), allowing you to track specific workloads or cost centers. The timeGrain controls how often the budget resets; Monthly budgets reset on the first of each month. The timePeriod defines when the budget is active.

Monitor reservation utilization across billing account

Organizations with Azure reservations need visibility into whether purchased capacity is being used efficiently.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const budget = new azure_native.costmanagement.Budget("budget", {
    budgetName: "TestAlertRule",
    category: azure_native.costmanagement.CategoryType.ReservationUtilization,
    eTag: "\"1d34d016a593709\"",
    filter: {},
    notifications: {
        Actual_LessThan_99_Percent: {
            contactEmails: [
                "johndoe@contoso.com",
                "janesmith@contoso.com",
            ],
            enabled: true,
            frequency: azure_native.costmanagement.Frequency.Weekly,
            locale: azure_native.costmanagement.CultureCode.En_us,
            operator: azure_native.costmanagement.BudgetNotificationOperatorType.LessThan,
            threshold: 99,
        },
    },
    scope: "providers/Microsoft.Billing/billingAccounts/123456",
    timeGrain: azure_native.costmanagement.TimeGrainType.Last7Days,
    timePeriod: {
        endDate: "2025-04-01T00:00:00Z",
        startDate: "2023-04-01T00:00:00Z",
    },
});
import pulumi
import pulumi_azure_native as azure_native

budget = azure_native.costmanagement.Budget("budget",
    budget_name="TestAlertRule",
    category=azure_native.costmanagement.CategoryType.RESERVATION_UTILIZATION,
    e_tag="\"1d34d016a593709\"",
    filter={},
    notifications={
        "Actual_LessThan_99_Percent": {
            "contact_emails": [
                "johndoe@contoso.com",
                "janesmith@contoso.com",
            ],
            "enabled": True,
            "frequency": azure_native.costmanagement.Frequency.WEEKLY,
            "locale": azure_native.costmanagement.CultureCode.EN_US,
            "operator": azure_native.costmanagement.BudgetNotificationOperatorType.LESS_THAN,
            "threshold": 99,
        },
    },
    scope="providers/Microsoft.Billing/billingAccounts/123456",
    time_grain=azure_native.costmanagement.TimeGrainType.LAST7_DAYS,
    time_period={
        "end_date": "2025-04-01T00:00:00Z",
        "start_date": "2023-04-01T00:00:00Z",
    })
package main

import (
	costmanagement "github.com/pulumi/pulumi-azure-native-sdk/costmanagement/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := costmanagement.NewBudget(ctx, "budget", &costmanagement.BudgetArgs{
			BudgetName: pulumi.String("TestAlertRule"),
			Category:   pulumi.String(costmanagement.CategoryTypeReservationUtilization),
			ETag:       pulumi.String("\"1d34d016a593709\""),
			Filter:     &costmanagement.BudgetFilterArgs{},
			Notifications: costmanagement.NotificationMap{
				"Actual_LessThan_99_Percent": &costmanagement.NotificationArgs{
					ContactEmails: pulumi.StringArray{
						pulumi.String("johndoe@contoso.com"),
						pulumi.String("janesmith@contoso.com"),
					},
					Enabled:   pulumi.Bool(true),
					Frequency: pulumi.String(costmanagement.FrequencyWeekly),
					Locale:    pulumi.String(costmanagement.CultureCode_En_Us),
					Operator:  pulumi.String(costmanagement.BudgetNotificationOperatorTypeLessThan),
					Threshold: pulumi.Float64(99),
				},
			},
			Scope:     pulumi.String("providers/Microsoft.Billing/billingAccounts/123456"),
			TimeGrain: pulumi.String(costmanagement.TimeGrainTypeLast7Days),
			TimePeriod: &costmanagement.BudgetTimePeriodArgs{
				EndDate:   pulumi.String("2025-04-01T00:00:00Z"),
				StartDate: pulumi.String("2023-04-01T00:00:00Z"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var budget = new AzureNative.CostManagement.Budget("budget", new()
    {
        BudgetName = "TestAlertRule",
        Category = AzureNative.CostManagement.CategoryType.ReservationUtilization,
        ETag = "\"1d34d016a593709\"",
        Filter = null,
        Notifications = 
        {
            { "Actual_LessThan_99_Percent", new AzureNative.CostManagement.Inputs.NotificationArgs
            {
                ContactEmails = new[]
                {
                    "johndoe@contoso.com",
                    "janesmith@contoso.com",
                },
                Enabled = true,
                Frequency = AzureNative.CostManagement.Frequency.Weekly,
                Locale = AzureNative.CostManagement.CultureCode.En_us,
                Operator = AzureNative.CostManagement.BudgetNotificationOperatorType.LessThan,
                Threshold = 99,
            } },
        },
        Scope = "providers/Microsoft.Billing/billingAccounts/123456",
        TimeGrain = AzureNative.CostManagement.TimeGrainType.Last7Days,
        TimePeriod = new AzureNative.CostManagement.Inputs.BudgetTimePeriodArgs
        {
            EndDate = "2025-04-01T00:00:00Z",
            StartDate = "2023-04-01T00:00:00Z",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.costmanagement.Budget;
import com.pulumi.azurenative.costmanagement.BudgetArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetFilterArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetTimePeriodArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var budget = new Budget("budget", BudgetArgs.builder()
            .budgetName("TestAlertRule")
            .category("ReservationUtilization")
            .eTag("\"1d34d016a593709\"")
            .filter(BudgetFilterArgs.builder()
                .build())
            .notifications(Map.of("Actual_LessThan_99_Percent", NotificationArgs.builder()
                .contactEmails(                
                    "johndoe@contoso.com",
                    "janesmith@contoso.com")
                .enabled(true)
                .frequency("Weekly")
                .locale("en-us")
                .operator("LessThan")
                .threshold(99.0)
                .build()))
            .scope("providers/Microsoft.Billing/billingAccounts/123456")
            .timeGrain("Last7Days")
            .timePeriod(BudgetTimePeriodArgs.builder()
                .endDate("2025-04-01T00:00:00Z")
                .startDate("2023-04-01T00:00:00Z")
                .build())
            .build());

    }
}
resources:
  budget:
    type: azure-native:costmanagement:Budget
    properties:
      budgetName: TestAlertRule
      category: ReservationUtilization
      eTag: '"1d34d016a593709"'
      filter: {}
      notifications:
        Actual_LessThan_99_Percent:
          contactEmails:
            - johndoe@contoso.com
            - janesmith@contoso.com
          enabled: true
          frequency: Weekly
          locale: en-us
          operator: LessThan
          threshold: 99
      scope: providers/Microsoft.Billing/billingAccounts/123456
      timeGrain: Last7Days
      timePeriod:
        endDate: 2025-04-01T00:00:00Z
        startDate: 2023-04-01T00:00:00Z

Reservation utilization budgets track usage percentage rather than cost amounts. The category property switches from Cost to ReservationUtilization, and the timeGrain uses Last7Days or Last30Days instead of monthly periods. When utilization falls below the threshold (here, 99%), notifications fire. The frequency property controls how often Azure evaluates the condition; Weekly checks reduce alert fatigue compared to daily checks.

Track specific reservations by ID

When managing multiple reservations, teams often need to monitor specific purchases separately.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const budget = new azure_native.costmanagement.Budget("budget", {
    budgetName: "TestAlertRule",
    category: azure_native.costmanagement.CategoryType.ReservationUtilization,
    eTag: "\"1d34d016a593709\"",
    filter: {
        dimensions: {
            name: "ReservationId",
            operator: azure_native.costmanagement.BudgetOperatorType.In,
            values: [
                "00000000-0000-0000-0000-000000000000",
                "00000000-0000-0000-0000-000000000001",
                "00000000-0000-0000-0000-000000000002",
            ],
        },
    },
    notifications: {
        Actual_LessThan_99_Percent: {
            contactEmails: [
                "johndoe@contoso.com",
                "janesmith@contoso.com",
            ],
            enabled: true,
            frequency: azure_native.costmanagement.Frequency.Weekly,
            locale: azure_native.costmanagement.CultureCode.En_us,
            operator: azure_native.costmanagement.BudgetNotificationOperatorType.LessThan,
            threshold: 99,
        },
    },
    scope: "providers/Microsoft.Billing/billingAccounts/123456",
    timeGrain: azure_native.costmanagement.TimeGrainType.Last7Days,
    timePeriod: {
        endDate: "2025-04-01T00:00:00Z",
        startDate: "2023-04-01T00:00:00Z",
    },
});
import pulumi
import pulumi_azure_native as azure_native

budget = azure_native.costmanagement.Budget("budget",
    budget_name="TestAlertRule",
    category=azure_native.costmanagement.CategoryType.RESERVATION_UTILIZATION,
    e_tag="\"1d34d016a593709\"",
    filter={
        "dimensions": {
            "name": "ReservationId",
            "operator": azure_native.costmanagement.BudgetOperatorType.IN_,
            "values": [
                "00000000-0000-0000-0000-000000000000",
                "00000000-0000-0000-0000-000000000001",
                "00000000-0000-0000-0000-000000000002",
            ],
        },
    },
    notifications={
        "Actual_LessThan_99_Percent": {
            "contact_emails": [
                "johndoe@contoso.com",
                "janesmith@contoso.com",
            ],
            "enabled": True,
            "frequency": azure_native.costmanagement.Frequency.WEEKLY,
            "locale": azure_native.costmanagement.CultureCode.EN_US,
            "operator": azure_native.costmanagement.BudgetNotificationOperatorType.LESS_THAN,
            "threshold": 99,
        },
    },
    scope="providers/Microsoft.Billing/billingAccounts/123456",
    time_grain=azure_native.costmanagement.TimeGrainType.LAST7_DAYS,
    time_period={
        "end_date": "2025-04-01T00:00:00Z",
        "start_date": "2023-04-01T00:00:00Z",
    })
package main

import (
	costmanagement "github.com/pulumi/pulumi-azure-native-sdk/costmanagement/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := costmanagement.NewBudget(ctx, "budget", &costmanagement.BudgetArgs{
			BudgetName: pulumi.String("TestAlertRule"),
			Category:   pulumi.String(costmanagement.CategoryTypeReservationUtilization),
			ETag:       pulumi.String("\"1d34d016a593709\""),
			Filter: &costmanagement.BudgetFilterArgs{
				Dimensions: &costmanagement.BudgetComparisonExpressionArgs{
					Name:     pulumi.String("ReservationId"),
					Operator: pulumi.String(costmanagement.BudgetOperatorTypeIn),
					Values: pulumi.StringArray{
						pulumi.String("00000000-0000-0000-0000-000000000000"),
						pulumi.String("00000000-0000-0000-0000-000000000001"),
						pulumi.String("00000000-0000-0000-0000-000000000002"),
					},
				},
			},
			Notifications: costmanagement.NotificationMap{
				"Actual_LessThan_99_Percent": &costmanagement.NotificationArgs{
					ContactEmails: pulumi.StringArray{
						pulumi.String("johndoe@contoso.com"),
						pulumi.String("janesmith@contoso.com"),
					},
					Enabled:   pulumi.Bool(true),
					Frequency: pulumi.String(costmanagement.FrequencyWeekly),
					Locale:    pulumi.String(costmanagement.CultureCode_En_Us),
					Operator:  pulumi.String(costmanagement.BudgetNotificationOperatorTypeLessThan),
					Threshold: pulumi.Float64(99),
				},
			},
			Scope:     pulumi.String("providers/Microsoft.Billing/billingAccounts/123456"),
			TimeGrain: pulumi.String(costmanagement.TimeGrainTypeLast7Days),
			TimePeriod: &costmanagement.BudgetTimePeriodArgs{
				EndDate:   pulumi.String("2025-04-01T00:00:00Z"),
				StartDate: pulumi.String("2023-04-01T00:00:00Z"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var budget = new AzureNative.CostManagement.Budget("budget", new()
    {
        BudgetName = "TestAlertRule",
        Category = AzureNative.CostManagement.CategoryType.ReservationUtilization,
        ETag = "\"1d34d016a593709\"",
        Filter = new AzureNative.CostManagement.Inputs.BudgetFilterArgs
        {
            Dimensions = new AzureNative.CostManagement.Inputs.BudgetComparisonExpressionArgs
            {
                Name = "ReservationId",
                Operator = AzureNative.CostManagement.BudgetOperatorType.In,
                Values = new[]
                {
                    "00000000-0000-0000-0000-000000000000",
                    "00000000-0000-0000-0000-000000000001",
                    "00000000-0000-0000-0000-000000000002",
                },
            },
        },
        Notifications = 
        {
            { "Actual_LessThan_99_Percent", new AzureNative.CostManagement.Inputs.NotificationArgs
            {
                ContactEmails = new[]
                {
                    "johndoe@contoso.com",
                    "janesmith@contoso.com",
                },
                Enabled = true,
                Frequency = AzureNative.CostManagement.Frequency.Weekly,
                Locale = AzureNative.CostManagement.CultureCode.En_us,
                Operator = AzureNative.CostManagement.BudgetNotificationOperatorType.LessThan,
                Threshold = 99,
            } },
        },
        Scope = "providers/Microsoft.Billing/billingAccounts/123456",
        TimeGrain = AzureNative.CostManagement.TimeGrainType.Last7Days,
        TimePeriod = new AzureNative.CostManagement.Inputs.BudgetTimePeriodArgs
        {
            EndDate = "2025-04-01T00:00:00Z",
            StartDate = "2023-04-01T00:00:00Z",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.costmanagement.Budget;
import com.pulumi.azurenative.costmanagement.BudgetArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetFilterArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetComparisonExpressionArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetTimePeriodArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var budget = new Budget("budget", BudgetArgs.builder()
            .budgetName("TestAlertRule")
            .category("ReservationUtilization")
            .eTag("\"1d34d016a593709\"")
            .filter(BudgetFilterArgs.builder()
                .dimensions(BudgetComparisonExpressionArgs.builder()
                    .name("ReservationId")
                    .operator("In")
                    .values(                    
                        "00000000-0000-0000-0000-000000000000",
                        "00000000-0000-0000-0000-000000000001",
                        "00000000-0000-0000-0000-000000000002")
                    .build())
                .build())
            .notifications(Map.of("Actual_LessThan_99_Percent", NotificationArgs.builder()
                .contactEmails(                
                    "johndoe@contoso.com",
                    "janesmith@contoso.com")
                .enabled(true)
                .frequency("Weekly")
                .locale("en-us")
                .operator("LessThan")
                .threshold(99.0)
                .build()))
            .scope("providers/Microsoft.Billing/billingAccounts/123456")
            .timeGrain("Last7Days")
            .timePeriod(BudgetTimePeriodArgs.builder()
                .endDate("2025-04-01T00:00:00Z")
                .startDate("2023-04-01T00:00:00Z")
                .build())
            .build());

    }
}
resources:
  budget:
    type: azure-native:costmanagement:Budget
    properties:
      budgetName: TestAlertRule
      category: ReservationUtilization
      eTag: '"1d34d016a593709"'
      filter:
        dimensions:
          name: ReservationId
          operator: In
          values:
            - 00000000-0000-0000-0000-000000000000
            - 00000000-0000-0000-0000-000000000001
            - 00000000-0000-0000-0000-000000000002
      notifications:
        Actual_LessThan_99_Percent:
          contactEmails:
            - johndoe@contoso.com
            - janesmith@contoso.com
          enabled: true
          frequency: Weekly
          locale: en-us
          operator: LessThan
          threshold: 99
      scope: providers/Microsoft.Billing/billingAccounts/123456
      timeGrain: Last7Days
      timePeriod:
        endDate: 2025-04-01T00:00:00Z
        startDate: 2023-04-01T00:00:00Z

The filter dimensions property accepts ReservationId to target specific reservation purchases. The operator In allows you to list multiple reservation IDs in the values array. This configuration monitors three reservations independently of others in the billing account. Each reservation’s utilization is evaluated separately; if any falls below 99%, the alert triggers.

Filter reservations by resource type

Different reservation types often have different utilization patterns and business priorities.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const budget = new azure_native.costmanagement.Budget("budget", {
    budgetName: "TestAlertRule",
    category: azure_native.costmanagement.CategoryType.ReservationUtilization,
    eTag: "\"1d34d016a593709\"",
    filter: {
        dimensions: {
            name: "ReservedResourceType",
            operator: azure_native.costmanagement.BudgetOperatorType.In,
            values: [
                "VirtualMachines",
                "SqlDatabases",
                "CosmosDb",
            ],
        },
    },
    notifications: {
        Actual_LessThan_99_Percent: {
            contactEmails: [
                "johndoe@contoso.com",
                "janesmith@contoso.com",
            ],
            enabled: true,
            frequency: azure_native.costmanagement.Frequency.Weekly,
            locale: azure_native.costmanagement.CultureCode.En_us,
            operator: azure_native.costmanagement.BudgetNotificationOperatorType.LessThan,
            threshold: 99,
        },
    },
    scope: "providers/Microsoft.Billing/billingAccounts/123456",
    timeGrain: azure_native.costmanagement.TimeGrainType.Last7Days,
    timePeriod: {
        endDate: "2025-04-01T00:00:00Z",
        startDate: "2023-04-01T00:00:00Z",
    },
});
import pulumi
import pulumi_azure_native as azure_native

budget = azure_native.costmanagement.Budget("budget",
    budget_name="TestAlertRule",
    category=azure_native.costmanagement.CategoryType.RESERVATION_UTILIZATION,
    e_tag="\"1d34d016a593709\"",
    filter={
        "dimensions": {
            "name": "ReservedResourceType",
            "operator": azure_native.costmanagement.BudgetOperatorType.IN_,
            "values": [
                "VirtualMachines",
                "SqlDatabases",
                "CosmosDb",
            ],
        },
    },
    notifications={
        "Actual_LessThan_99_Percent": {
            "contact_emails": [
                "johndoe@contoso.com",
                "janesmith@contoso.com",
            ],
            "enabled": True,
            "frequency": azure_native.costmanagement.Frequency.WEEKLY,
            "locale": azure_native.costmanagement.CultureCode.EN_US,
            "operator": azure_native.costmanagement.BudgetNotificationOperatorType.LESS_THAN,
            "threshold": 99,
        },
    },
    scope="providers/Microsoft.Billing/billingAccounts/123456",
    time_grain=azure_native.costmanagement.TimeGrainType.LAST7_DAYS,
    time_period={
        "end_date": "2025-04-01T00:00:00Z",
        "start_date": "2023-04-01T00:00:00Z",
    })
package main

import (
	costmanagement "github.com/pulumi/pulumi-azure-native-sdk/costmanagement/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := costmanagement.NewBudget(ctx, "budget", &costmanagement.BudgetArgs{
			BudgetName: pulumi.String("TestAlertRule"),
			Category:   pulumi.String(costmanagement.CategoryTypeReservationUtilization),
			ETag:       pulumi.String("\"1d34d016a593709\""),
			Filter: &costmanagement.BudgetFilterArgs{
				Dimensions: &costmanagement.BudgetComparisonExpressionArgs{
					Name:     pulumi.String("ReservedResourceType"),
					Operator: pulumi.String(costmanagement.BudgetOperatorTypeIn),
					Values: pulumi.StringArray{
						pulumi.String("VirtualMachines"),
						pulumi.String("SqlDatabases"),
						pulumi.String("CosmosDb"),
					},
				},
			},
			Notifications: costmanagement.NotificationMap{
				"Actual_LessThan_99_Percent": &costmanagement.NotificationArgs{
					ContactEmails: pulumi.StringArray{
						pulumi.String("johndoe@contoso.com"),
						pulumi.String("janesmith@contoso.com"),
					},
					Enabled:   pulumi.Bool(true),
					Frequency: pulumi.String(costmanagement.FrequencyWeekly),
					Locale:    pulumi.String(costmanagement.CultureCode_En_Us),
					Operator:  pulumi.String(costmanagement.BudgetNotificationOperatorTypeLessThan),
					Threshold: pulumi.Float64(99),
				},
			},
			Scope:     pulumi.String("providers/Microsoft.Billing/billingAccounts/123456"),
			TimeGrain: pulumi.String(costmanagement.TimeGrainTypeLast7Days),
			TimePeriod: &costmanagement.BudgetTimePeriodArgs{
				EndDate:   pulumi.String("2025-04-01T00:00:00Z"),
				StartDate: pulumi.String("2023-04-01T00:00:00Z"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var budget = new AzureNative.CostManagement.Budget("budget", new()
    {
        BudgetName = "TestAlertRule",
        Category = AzureNative.CostManagement.CategoryType.ReservationUtilization,
        ETag = "\"1d34d016a593709\"",
        Filter = new AzureNative.CostManagement.Inputs.BudgetFilterArgs
        {
            Dimensions = new AzureNative.CostManagement.Inputs.BudgetComparisonExpressionArgs
            {
                Name = "ReservedResourceType",
                Operator = AzureNative.CostManagement.BudgetOperatorType.In,
                Values = new[]
                {
                    "VirtualMachines",
                    "SqlDatabases",
                    "CosmosDb",
                },
            },
        },
        Notifications = 
        {
            { "Actual_LessThan_99_Percent", new AzureNative.CostManagement.Inputs.NotificationArgs
            {
                ContactEmails = new[]
                {
                    "johndoe@contoso.com",
                    "janesmith@contoso.com",
                },
                Enabled = true,
                Frequency = AzureNative.CostManagement.Frequency.Weekly,
                Locale = AzureNative.CostManagement.CultureCode.En_us,
                Operator = AzureNative.CostManagement.BudgetNotificationOperatorType.LessThan,
                Threshold = 99,
            } },
        },
        Scope = "providers/Microsoft.Billing/billingAccounts/123456",
        TimeGrain = AzureNative.CostManagement.TimeGrainType.Last7Days,
        TimePeriod = new AzureNative.CostManagement.Inputs.BudgetTimePeriodArgs
        {
            EndDate = "2025-04-01T00:00:00Z",
            StartDate = "2023-04-01T00:00:00Z",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.costmanagement.Budget;
import com.pulumi.azurenative.costmanagement.BudgetArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetFilterArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetComparisonExpressionArgs;
import com.pulumi.azurenative.costmanagement.inputs.BudgetTimePeriodArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var budget = new Budget("budget", BudgetArgs.builder()
            .budgetName("TestAlertRule")
            .category("ReservationUtilization")
            .eTag("\"1d34d016a593709\"")
            .filter(BudgetFilterArgs.builder()
                .dimensions(BudgetComparisonExpressionArgs.builder()
                    .name("ReservedResourceType")
                    .operator("In")
                    .values(                    
                        "VirtualMachines",
                        "SqlDatabases",
                        "CosmosDb")
                    .build())
                .build())
            .notifications(Map.of("Actual_LessThan_99_Percent", NotificationArgs.builder()
                .contactEmails(                
                    "johndoe@contoso.com",
                    "janesmith@contoso.com")
                .enabled(true)
                .frequency("Weekly")
                .locale("en-us")
                .operator("LessThan")
                .threshold(99.0)
                .build()))
            .scope("providers/Microsoft.Billing/billingAccounts/123456")
            .timeGrain("Last7Days")
            .timePeriod(BudgetTimePeriodArgs.builder()
                .endDate("2025-04-01T00:00:00Z")
                .startDate("2023-04-01T00:00:00Z")
                .build())
            .build());

    }
}
resources:
  budget:
    type: azure-native:costmanagement:Budget
    properties:
      budgetName: TestAlertRule
      category: ReservationUtilization
      eTag: '"1d34d016a593709"'
      filter:
        dimensions:
          name: ReservedResourceType
          operator: In
          values:
            - VirtualMachines
            - SqlDatabases
            - CosmosDb
      notifications:
        Actual_LessThan_99_Percent:
          contactEmails:
            - johndoe@contoso.com
            - janesmith@contoso.com
          enabled: true
          frequency: Weekly
          locale: en-us
          operator: LessThan
          threshold: 99
      scope: providers/Microsoft.Billing/billingAccounts/123456
      timeGrain: Last7Days
      timePeriod:
        endDate: 2025-04-01T00:00:00Z
        startDate: 2023-04-01T00:00:00Z

Instead of filtering by reservation ID, you can filter by ReservedResourceType to monitor all reservations of a given type. This example tracks VirtualMachines, SqlDatabases, and CosmosDb reservations together. When any reservation of these types drops below 99% utilization, the alert fires. This approach works well when you care about resource type utilization trends rather than individual reservation performance.

Beyond these examples

These snippets focus on specific budget-level features: cost budgets with resource and tag filtering, reservation utilization alerts, and dimension-based filtering. They’re intentionally minimal rather than full cost management solutions.

The examples may reference pre-existing infrastructure such as Azure subscriptions, billing accounts, billing profiles, or customer scopes, Action Groups for notification delivery, and active reservations for utilization monitoring. They focus on configuring the budget rather than provisioning the billing hierarchy around it.

To keep things focused, common budget patterns are omitted, including:

  • Forecast-based notifications (thresholdType: Forecasted)
  • Multiple notification thresholds per budget
  • Management group and department scopes
  • Tag-based filtering beyond the basic example
  • Billing period time grains (BillingMonth, BillingQuarter, BillingAnnual)

These omissions are intentional: the goal is to illustrate how each budget feature is wired, not provide drop-in cost management modules. See the Budget resource reference for all available configuration options.

Let's configure Azure Cost Management Budgets

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Budget Types & Categories
What's the difference between Cost budgets and ReservationUtilization alerts?
Cost budgets track spending against a monetary amount and support multiple notification thresholds. ReservationUtilization alerts monitor reservation usage without an amount limit and support only one notification.
When is the amount property required?
The amount property is required for Cost budgets but not applicable for ReservationUtilization alerts.
Notifications & Alerts
How many notifications can I configure?
Cost budgets support up to 10 notifications: 5 with thresholdType: Actual and 5 with thresholdType: Forecasted. ReservationUtilization alerts support only 1 notification, and thresholdType is not applicable.
How do I set up budget notifications?
Configure the notifications dictionary with entries containing threshold, operator (GreaterThan or LessThan), contactEmails, and optionally contactGroups, contactRoles, enabled, frequency, and locale.
Can I get alerts for forecasted costs?
Yes, for Cost budgets set thresholdType to Forecasted in your notification configuration. You can have up to 5 forecasted notifications.
Filtering & Scoping
What scopes are supported for budgets?
Cost budgets support subscription, resource group, management group, billing account, department, enrollment account, billing profile, invoice section, and customer scopes. ReservationUtilization alerts support only billing account, billing profile, and customer scopes.
How do I filter a budget by specific resources?
Use the filter property with dimensions to filter by ResourceId, or use tags to filter by tag name and values. You can combine multiple filters using the and array.
How do I filter reservation alerts by specific reservations or resource types?
Set filter.dimensions.name to ReservationId with an array of reservation IDs, or use ReservedResourceType with values like VirtualMachines, SqlDatabases, or CosmosDb.
Time Configuration & Limitations
What timeGrain options are available?
Cost budgets support Monthly, Quarterly, Annually, and for Web Direct customers only: BillingMonth, BillingQuarter, BillingAnnual. ReservationUtilization alerts support Last7Days and Last30Days.
Can I use BillingMonth, BillingQuarter, or BillingAnnual timeGrain?
These timeGrain values are only supported for Web Direct customers. Other customers should use Monthly, Quarterly, or Annually.
What is the eTag property used for?
The eTag handles concurrent update scenarios by determining whether you’re updating the latest version of the budget resource.
How do I set the budget time period?
Configure timePeriod with startDate and endDate in ISO 8601 format. The budget evaluates data on or after the startDate and expires on the endDate.

Using a different cloud?

Explore monitoring guides for other cloud providers: