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 Configuration
What's the difference between Cost and ReservationUtilization budgets?
Cost budgets track spending against a monetary amount and require the amount property. ReservationUtilization budgets monitor reservation usage percentages and don’t use the amount property. They also have different time grain options and notification limits.
What time grains are available for my budget?
For Cost budgets, you can use Monthly, Quarterly, Annually, BillingMonth, BillingQuarter, or BillingAnnual (the last three only for Web Direct customers). For ReservationUtilization budgets, use Last7Days or Last30Days.
Why is the amount property required for some budgets but not others?
The amount property is required for Cost budgets because they track spending against a monetary limit. ReservationUtilization budgets monitor usage percentages instead, so they don’t need an amount.
Can I change the scope after creating a budget?
No, the scope property is immutable and cannot be changed after budget creation. Choose your scope carefully (subscription, resource group, billing account, etc.) before creating the budget.
What's the eTag property used for?
The eTag field handles concurrent update scenarios by determining whether you’re updating the latest version of the budget. It prevents conflicts when multiple users modify the same budget.
Notifications & Alerts
How many notifications can I configure for my budget?
Cost budgets support up to 5 notifications with thresholdType: Actual and 5 with thresholdType: Forecasted (10 total). ReservationUtilization budgets support only 1 notification, and thresholdType doesn’t apply.
What notification frequencies are available?
Notifications support Daily and Weekly frequencies. The examples show Weekly frequency for Last7Days time grains and Daily frequency for Last30Days time grains.
How do I configure notification contacts?
Use contactEmails for email addresses, contactRoles for Azure RBAC roles (like Contributor or Reader), and contactGroups for Azure Monitor action groups. You can combine all three in a single notification.
Filtering & Scoping
What scopes can I use for Cost budgets?
Cost budgets support subscription, resource group, management group (Azure RBAC), billing account, department, enrollment account (EA), billing profile, invoice section, and customer (MCA/CSP) scopes.
What scopes can I use for ReservationUtilization budgets?
ReservationUtilization budgets support billing account scope (EA), billing profile scope (MCA non-CSP), and customer scope (MCA CSP only).
How do I filter a budget by specific resources or reservations?
Use the filter property with dimensions. For resources, use ResourceId with operator In and a list of resource IDs. For reservations, use ReservationId or ReservedResourceType (like VirtualMachines, SqlDatabases, CosmosDb).
Can I combine dimension and tag filters?
Yes, use the and array within the filter property to combine multiple dimension and tag filters. Each filter can target different dimensions or tags.

Using a different cloud?

Explore monitoring guides for other cloud providers: