Configure AWS Budgets

The aws:budgets/budget:Budget resource, part of the Pulumi AWS provider, defines AWS Budgets that track spending or usage against limits and send notifications when thresholds are breached. This guide focuses on three capabilities: cost and usage budget types, email notifications and forecasted alerts, and filter expressions for service, tag, and region filtering.

Budgets operate at the AWS account level and reference existing services, tags, and cost categories. The examples are intentionally small. Combine them with your own notification targets and organizational tagging strategy.

Track EC2 costs with email notifications

Most budget deployments start by setting cost limits for specific services and configuring alerts when forecasted spending approaches those limits.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const ec2 = new aws.budgets.Budget("ec2", {
    name: "budget-ec2-monthly",
    budgetType: "COST",
    limitAmount: "1200",
    limitUnit: "USD",
    timePeriodEnd: "2087-06-15_00:00",
    timePeriodStart: "2017-07-01_00:00",
    timeUnit: "MONTHLY",
    costFilters: [{
        name: "Service",
        values: ["Amazon Elastic Compute Cloud - Compute"],
    }],
    notifications: [{
        comparisonOperator: "GREATER_THAN",
        threshold: 100,
        thresholdType: "PERCENTAGE",
        notificationType: "FORECASTED",
        subscriberEmailAddresses: ["test@example.com"],
    }],
    tags: {
        Tag1: "Value1",
        Tag2: "Value2",
    },
});
import pulumi
import pulumi_aws as aws

ec2 = aws.budgets.Budget("ec2",
    name="budget-ec2-monthly",
    budget_type="COST",
    limit_amount="1200",
    limit_unit="USD",
    time_period_end="2087-06-15_00:00",
    time_period_start="2017-07-01_00:00",
    time_unit="MONTHLY",
    cost_filters=[{
        "name": "Service",
        "values": ["Amazon Elastic Compute Cloud - Compute"],
    }],
    notifications=[{
        "comparison_operator": "GREATER_THAN",
        "threshold": 100,
        "threshold_type": "PERCENTAGE",
        "notification_type": "FORECASTED",
        "subscriber_email_addresses": ["test@example.com"],
    }],
    tags={
        "Tag1": "Value1",
        "Tag2": "Value2",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/budgets"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := budgets.NewBudget(ctx, "ec2", &budgets.BudgetArgs{
			Name:            pulumi.String("budget-ec2-monthly"),
			BudgetType:      pulumi.String("COST"),
			LimitAmount:     pulumi.String("1200"),
			LimitUnit:       pulumi.String("USD"),
			TimePeriodEnd:   pulumi.String("2087-06-15_00:00"),
			TimePeriodStart: pulumi.String("2017-07-01_00:00"),
			TimeUnit:        pulumi.String("MONTHLY"),
			CostFilters: budgets.BudgetCostFilterArray{
				&budgets.BudgetCostFilterArgs{
					Name: pulumi.String("Service"),
					Values: pulumi.StringArray{
						pulumi.String("Amazon Elastic Compute Cloud - Compute"),
					},
				},
			},
			Notifications: budgets.BudgetNotificationArray{
				&budgets.BudgetNotificationArgs{
					ComparisonOperator: pulumi.String("GREATER_THAN"),
					Threshold:          pulumi.Float64(100),
					ThresholdType:      pulumi.String("PERCENTAGE"),
					NotificationType:   pulumi.String("FORECASTED"),
					SubscriberEmailAddresses: pulumi.StringArray{
						pulumi.String("test@example.com"),
					},
				},
			},
			Tags: pulumi.StringMap{
				"Tag1": pulumi.String("Value1"),
				"Tag2": pulumi.String("Value2"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var ec2 = new Aws.Budgets.Budget("ec2", new()
    {
        Name = "budget-ec2-monthly",
        BudgetType = "COST",
        LimitAmount = "1200",
        LimitUnit = "USD",
        TimePeriodEnd = "2087-06-15_00:00",
        TimePeriodStart = "2017-07-01_00:00",
        TimeUnit = "MONTHLY",
        CostFilters = new[]
        {
            new Aws.Budgets.Inputs.BudgetCostFilterArgs
            {
                Name = "Service",
                Values = new[]
                {
                    "Amazon Elastic Compute Cloud - Compute",
                },
            },
        },
        Notifications = new[]
        {
            new Aws.Budgets.Inputs.BudgetNotificationArgs
            {
                ComparisonOperator = "GREATER_THAN",
                Threshold = 100,
                ThresholdType = "PERCENTAGE",
                NotificationType = "FORECASTED",
                SubscriberEmailAddresses = new[]
                {
                    "test@example.com",
                },
            },
        },
        Tags = 
        {
            { "Tag1", "Value1" },
            { "Tag2", "Value2" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.budgets.Budget;
import com.pulumi.aws.budgets.BudgetArgs;
import com.pulumi.aws.budgets.inputs.BudgetCostFilterArgs;
import com.pulumi.aws.budgets.inputs.BudgetNotificationArgs;
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 ec2 = new Budget("ec2", BudgetArgs.builder()
            .name("budget-ec2-monthly")
            .budgetType("COST")
            .limitAmount("1200")
            .limitUnit("USD")
            .timePeriodEnd("2087-06-15_00:00")
            .timePeriodStart("2017-07-01_00:00")
            .timeUnit("MONTHLY")
            .costFilters(BudgetCostFilterArgs.builder()
                .name("Service")
                .values("Amazon Elastic Compute Cloud - Compute")
                .build())
            .notifications(BudgetNotificationArgs.builder()
                .comparisonOperator("GREATER_THAN")
                .threshold(100.0)
                .thresholdType("PERCENTAGE")
                .notificationType("FORECASTED")
                .subscriberEmailAddresses("test@example.com")
                .build())
            .tags(Map.ofEntries(
                Map.entry("Tag1", "Value1"),
                Map.entry("Tag2", "Value2")
            ))
            .build());

    }
}
resources:
  ec2:
    type: aws:budgets:Budget
    properties:
      name: budget-ec2-monthly
      budgetType: COST
      limitAmount: '1200'
      limitUnit: USD
      timePeriodEnd: 2087-06-15_00:00
      timePeriodStart: 2017-07-01_00:00
      timeUnit: MONTHLY
      costFilters:
        - name: Service
          values:
            - Amazon Elastic Compute Cloud - Compute
      notifications:
        - comparisonOperator: GREATER_THAN
          threshold: 100
          thresholdType: PERCENTAGE
          notificationType: FORECASTED
          subscriberEmailAddresses:
            - test@example.com
      tags:
        Tag1: Value1
        Tag2: Value2

When AWS forecasts that your spending will exceed the threshold, it sends email notifications to the specified addresses. The budgetType determines whether you’re tracking cost or usage; costFilters narrows the scope to specific services. The notifications array defines when alerts fire: comparisonOperator sets the comparison logic, threshold sets the percentage, and notificationType controls whether you alert on actual or forecasted spending.

Define variable spending limits over time

Organizations with seasonal workloads or phased rollouts need budgets that adjust month-to-month rather than using a single fixed limit.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const cost = new aws.budgets.Budget("cost", {plannedLimits: [
    {
        startTime: "2017-07-01_00:00",
        amount: "100",
        unit: "USD",
    },
    {
        startTime: "2017-08-01_00:00",
        amount: "200",
        unit: "USD",
    },
]});
import pulumi
import pulumi_aws as aws

cost = aws.budgets.Budget("cost", planned_limits=[
    {
        "start_time": "2017-07-01_00:00",
        "amount": "100",
        "unit": "USD",
    },
    {
        "start_time": "2017-08-01_00:00",
        "amount": "200",
        "unit": "USD",
    },
])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/budgets"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := budgets.NewBudget(ctx, "cost", &budgets.BudgetArgs{
			PlannedLimits: budgets.BudgetPlannedLimitArray{
				&budgets.BudgetPlannedLimitArgs{
					StartTime: pulumi.String("2017-07-01_00:00"),
					Amount:    pulumi.String("100"),
					Unit:      pulumi.String("USD"),
				},
				&budgets.BudgetPlannedLimitArgs{
					StartTime: pulumi.String("2017-08-01_00:00"),
					Amount:    pulumi.String("200"),
					Unit:      pulumi.String("USD"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var cost = new Aws.Budgets.Budget("cost", new()
    {
        PlannedLimits = new[]
        {
            new Aws.Budgets.Inputs.BudgetPlannedLimitArgs
            {
                StartTime = "2017-07-01_00:00",
                Amount = "100",
                Unit = "USD",
            },
            new Aws.Budgets.Inputs.BudgetPlannedLimitArgs
            {
                StartTime = "2017-08-01_00:00",
                Amount = "200",
                Unit = "USD",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.budgets.Budget;
import com.pulumi.aws.budgets.BudgetArgs;
import com.pulumi.aws.budgets.inputs.BudgetPlannedLimitArgs;
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 cost = new Budget("cost", BudgetArgs.builder()
            .plannedLimits(            
                BudgetPlannedLimitArgs.builder()
                    .startTime("2017-07-01_00:00")
                    .amount("100")
                    .unit("USD")
                    .build(),
                BudgetPlannedLimitArgs.builder()
                    .startTime("2017-08-01_00:00")
                    .amount("200")
                    .unit("USD")
                    .build())
            .build());

    }
}
resources:
  cost:
    type: aws:budgets:Budget
    properties:
      plannedLimits:
        - startTime: 2017-07-01_00:00
          amount: '100'
          unit: USD
        - startTime: 2017-08-01_00:00
          amount: '200'
          unit: USD

The plannedLimits array replaces the single limitAmount property, allowing you to define different budget amounts for different time periods. Each entry specifies a startTime and the amount that applies from that date forward. This approach works well for projects with known spending ramps or seasonal traffic patterns.

Monitor S3 storage usage in gigabytes

Usage budgets track resource consumption rather than cost, useful for monitoring storage quotas or data transfer limits.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const s3 = new aws.budgets.Budget("s3", {
    budgetType: "USAGE",
    limitAmount: "3",
    limitUnit: "GB",
});
import pulumi
import pulumi_aws as aws

s3 = aws.budgets.Budget("s3",
    budget_type="USAGE",
    limit_amount="3",
    limit_unit="GB")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/budgets"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := budgets.NewBudget(ctx, "s3", &budgets.BudgetArgs{
			BudgetType:  pulumi.String("USAGE"),
			LimitAmount: pulumi.String("3"),
			LimitUnit:   pulumi.String("GB"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var s3 = new Aws.Budgets.Budget("s3", new()
    {
        BudgetType = "USAGE",
        LimitAmount = "3",
        LimitUnit = "GB",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.budgets.Budget;
import com.pulumi.aws.budgets.BudgetArgs;
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 s3 = new Budget("s3", BudgetArgs.builder()
            .budgetType("USAGE")
            .limitAmount("3")
            .limitUnit("GB")
            .build());

    }
}
resources:
  s3:
    type: aws:budgets:Budget
    properties:
      budgetType: USAGE
      limitAmount: '3'
      limitUnit: GB

Setting budgetType to USAGE shifts from dollar amounts to resource units. The limitUnit changes from USD to GB, and the budget tracks storage consumption rather than spending. This configuration alerts when storage exceeds the specified gigabyte threshold, regardless of pricing changes.

Track Savings Plan utilization percentage

Organizations with Savings Plans commitments need to monitor utilization rates to ensure they’re maximizing their commitment value.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const savingsPlanUtilization = new aws.budgets.Budget("savings_plan_utilization", {
    budgetType: "SAVINGS_PLANS_UTILIZATION",
    limitAmount: "100.0",
    limitUnit: "PERCENTAGE",
    costTypes: {
        includeCredit: false,
        includeDiscount: false,
        includeOtherSubscription: false,
        includeRecurring: false,
        includeRefund: false,
        includeSubscription: true,
        includeSupport: false,
        includeTax: false,
        includeUpfront: false,
        useBlended: false,
    },
});
import pulumi
import pulumi_aws as aws

savings_plan_utilization = aws.budgets.Budget("savings_plan_utilization",
    budget_type="SAVINGS_PLANS_UTILIZATION",
    limit_amount="100.0",
    limit_unit="PERCENTAGE",
    cost_types={
        "include_credit": False,
        "include_discount": False,
        "include_other_subscription": False,
        "include_recurring": False,
        "include_refund": False,
        "include_subscription": True,
        "include_support": False,
        "include_tax": False,
        "include_upfront": False,
        "use_blended": False,
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/budgets"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := budgets.NewBudget(ctx, "savings_plan_utilization", &budgets.BudgetArgs{
			BudgetType:  pulumi.String("SAVINGS_PLANS_UTILIZATION"),
			LimitAmount: pulumi.String("100.0"),
			LimitUnit:   pulumi.String("PERCENTAGE"),
			CostTypes: &budgets.BudgetCostTypesArgs{
				IncludeCredit:            pulumi.Bool(false),
				IncludeDiscount:          pulumi.Bool(false),
				IncludeOtherSubscription: pulumi.Bool(false),
				IncludeRecurring:         pulumi.Bool(false),
				IncludeRefund:            pulumi.Bool(false),
				IncludeSubscription:      pulumi.Bool(true),
				IncludeSupport:           pulumi.Bool(false),
				IncludeTax:               pulumi.Bool(false),
				IncludeUpfront:           pulumi.Bool(false),
				UseBlended:               pulumi.Bool(false),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var savingsPlanUtilization = new Aws.Budgets.Budget("savings_plan_utilization", new()
    {
        BudgetType = "SAVINGS_PLANS_UTILIZATION",
        LimitAmount = "100.0",
        LimitUnit = "PERCENTAGE",
        CostTypes = new Aws.Budgets.Inputs.BudgetCostTypesArgs
        {
            IncludeCredit = false,
            IncludeDiscount = false,
            IncludeOtherSubscription = false,
            IncludeRecurring = false,
            IncludeRefund = false,
            IncludeSubscription = true,
            IncludeSupport = false,
            IncludeTax = false,
            IncludeUpfront = false,
            UseBlended = false,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.budgets.Budget;
import com.pulumi.aws.budgets.BudgetArgs;
import com.pulumi.aws.budgets.inputs.BudgetCostTypesArgs;
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 savingsPlanUtilization = new Budget("savingsPlanUtilization", BudgetArgs.builder()
            .budgetType("SAVINGS_PLANS_UTILIZATION")
            .limitAmount("100.0")
            .limitUnit("PERCENTAGE")
            .costTypes(BudgetCostTypesArgs.builder()
                .includeCredit(false)
                .includeDiscount(false)
                .includeOtherSubscription(false)
                .includeRecurring(false)
                .includeRefund(false)
                .includeSubscription(true)
                .includeSupport(false)
                .includeTax(false)
                .includeUpfront(false)
                .useBlended(false)
                .build())
            .build());

    }
}
resources:
  savingsPlanUtilization:
    type: aws:budgets:Budget
    name: savings_plan_utilization
    properties:
      budgetType: SAVINGS_PLANS_UTILIZATION
      limitAmount: '100.0'
      limitUnit: PERCENTAGE
      costTypes:
        includeCredit: false
        includeDiscount: false
        includeOtherSubscription: false
        includeRecurring: false
        includeRefund: false
        includeSubscription: true
        includeSupport: false
        includeTax: false
        includeUpfront: false
        useBlended: false

Savings Plan utilization budgets use PERCENTAGE as the limitUnit and require specific costTypes configuration. Setting includeSubscription to true ensures the budget tracks your commitment usage. The other costTypes flags control which cost components factor into the calculation; for utilization tracking, most are set to false to focus purely on commitment usage.

Filter costs using dimension expressions

The filterExpression property provides more flexible cost filtering than the legacy costFilters array, supporting complex logical operations.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const simple = new aws.budgets.Budget("simple", {
    name: "budget-ec2-filter",
    budgetType: "COST",
    limitAmount: "500",
    limitUnit: "USD",
    timeUnit: "MONTHLY",
    filterExpression: {
        dimensions: {
            key: "SERVICE",
            values: ["Amazon Elastic Compute Cloud - Compute"],
        },
    },
});
import pulumi
import pulumi_aws as aws

simple = aws.budgets.Budget("simple",
    name="budget-ec2-filter",
    budget_type="COST",
    limit_amount="500",
    limit_unit="USD",
    time_unit="MONTHLY",
    filter_expression={
        "dimensions": {
            "key": "SERVICE",
            "values": ["Amazon Elastic Compute Cloud - Compute"],
        },
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/budgets"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := budgets.NewBudget(ctx, "simple", &budgets.BudgetArgs{
			Name:        pulumi.String("budget-ec2-filter"),
			BudgetType:  pulumi.String("COST"),
			LimitAmount: pulumi.String("500"),
			LimitUnit:   pulumi.String("USD"),
			TimeUnit:    pulumi.String("MONTHLY"),
			FilterExpression: &budgets.BudgetFilterExpressionArgs{
				Dimensions: &budgets.BudgetFilterExpressionDimensionsArgs{
					Key: pulumi.String("SERVICE"),
					Values: pulumi.StringArray{
						pulumi.String("Amazon Elastic Compute Cloud - Compute"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var simple = new Aws.Budgets.Budget("simple", new()
    {
        Name = "budget-ec2-filter",
        BudgetType = "COST",
        LimitAmount = "500",
        LimitUnit = "USD",
        TimeUnit = "MONTHLY",
        FilterExpression = new Aws.Budgets.Inputs.BudgetFilterExpressionArgs
        {
            Dimensions = new Aws.Budgets.Inputs.BudgetFilterExpressionDimensionsArgs
            {
                Key = "SERVICE",
                Values = new[]
                {
                    "Amazon Elastic Compute Cloud - Compute",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.budgets.Budget;
import com.pulumi.aws.budgets.BudgetArgs;
import com.pulumi.aws.budgets.inputs.BudgetFilterExpressionArgs;
import com.pulumi.aws.budgets.inputs.BudgetFilterExpressionDimensionsArgs;
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 simple = new Budget("simple", BudgetArgs.builder()
            .name("budget-ec2-filter")
            .budgetType("COST")
            .limitAmount("500")
            .limitUnit("USD")
            .timeUnit("MONTHLY")
            .filterExpression(BudgetFilterExpressionArgs.builder()
                .dimensions(BudgetFilterExpressionDimensionsArgs.builder()
                    .key("SERVICE")
                    .values("Amazon Elastic Compute Cloud - Compute")
                    .build())
                .build())
            .build());

    }
}
resources:
  simple:
    type: aws:budgets:Budget
    properties:
      name: budget-ec2-filter
      budgetType: COST
      limitAmount: '500'
      limitUnit: USD
      timeUnit: MONTHLY
      filterExpression:
        dimensions:
          key: SERVICE
          values:
            - Amazon Elastic Compute Cloud - Compute

Filter expressions use a structured format with dimensions, tags, and costCategories objects. The dimensions object filters by AWS-defined attributes like SERVICE or REGION. This approach replaces the older costFilters array and enables more complex filtering logic in subsequent examples.

Combine multiple filters with AND logic

Production cost tracking often requires filtering by both service type and environment tags to isolate specific workload costs.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const andExample = new aws.budgets.Budget("and_example", {
    name: "budget-and-filter",
    budgetType: "COST",
    limitAmount: "1200",
    limitUnit: "USD",
    timeUnit: "MONTHLY",
    filterExpression: {
        ands: [
            {
                dimensions: {
                    key: "SERVICE",
                    values: ["Amazon Elastic Compute Cloud - Compute"],
                },
            },
            {
                tags: {
                    key: "Environment",
                    values: ["Production"],
                },
            },
        ],
    },
});
import pulumi
import pulumi_aws as aws

and_example = aws.budgets.Budget("and_example",
    name="budget-and-filter",
    budget_type="COST",
    limit_amount="1200",
    limit_unit="USD",
    time_unit="MONTHLY",
    filter_expression={
        "ands": [
            {
                "dimensions": {
                    "key": "SERVICE",
                    "values": ["Amazon Elastic Compute Cloud - Compute"],
                },
            },
            {
                "tags": {
                    "key": "Environment",
                    "values": ["Production"],
                },
            },
        ],
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/budgets"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := budgets.NewBudget(ctx, "and_example", &budgets.BudgetArgs{
			Name:        pulumi.String("budget-and-filter"),
			BudgetType:  pulumi.String("COST"),
			LimitAmount: pulumi.String("1200"),
			LimitUnit:   pulumi.String("USD"),
			TimeUnit:    pulumi.String("MONTHLY"),
			FilterExpression: &budgets.BudgetFilterExpressionArgs{
				Ands: budgets.BudgetFilterExpressionAndArray{
					&budgets.BudgetFilterExpressionAndArgs{
						Dimensions: &budgets.BudgetFilterExpressionAndDimensionsArgs{
							Key: pulumi.String("SERVICE"),
							Values: pulumi.StringArray{
								pulumi.String("Amazon Elastic Compute Cloud - Compute"),
							},
						},
					},
					&budgets.BudgetFilterExpressionAndArgs{
						Tags: &budgets.BudgetFilterExpressionAndTagsArgs{
							Key: pulumi.String("Environment"),
							Values: pulumi.StringArray{
								pulumi.String("Production"),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var andExample = new Aws.Budgets.Budget("and_example", new()
    {
        Name = "budget-and-filter",
        BudgetType = "COST",
        LimitAmount = "1200",
        LimitUnit = "USD",
        TimeUnit = "MONTHLY",
        FilterExpression = new Aws.Budgets.Inputs.BudgetFilterExpressionArgs
        {
            Ands = new[]
            {
                new Aws.Budgets.Inputs.BudgetFilterExpressionAndArgs
                {
                    Dimensions = new Aws.Budgets.Inputs.BudgetFilterExpressionAndDimensionsArgs
                    {
                        Key = "SERVICE",
                        Values = new[]
                        {
                            "Amazon Elastic Compute Cloud - Compute",
                        },
                    },
                },
                new Aws.Budgets.Inputs.BudgetFilterExpressionAndArgs
                {
                    Tags = new Aws.Budgets.Inputs.BudgetFilterExpressionAndTagsArgs
                    {
                        Key = "Environment",
                        Values = new[]
                        {
                            "Production",
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.budgets.Budget;
import com.pulumi.aws.budgets.BudgetArgs;
import com.pulumi.aws.budgets.inputs.BudgetFilterExpressionArgs;
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 andExample = new Budget("andExample", BudgetArgs.builder()
            .name("budget-and-filter")
            .budgetType("COST")
            .limitAmount("1200")
            .limitUnit("USD")
            .timeUnit("MONTHLY")
            .filterExpression(BudgetFilterExpressionArgs.builder()
                .ands(                
                    BudgetFilterExpressionAndArgs.builder()
                        .dimensions(BudgetFilterExpressionAndDimensionsArgs.builder()
                            .key("SERVICE")
                            .values("Amazon Elastic Compute Cloud - Compute")
                            .build())
                        .build(),
                    BudgetFilterExpressionAndArgs.builder()
                        .tags(BudgetFilterExpressionAndTagsArgs.builder()
                            .key("Environment")
                            .values("Production")
                            .build())
                        .build())
                .build())
            .build());

    }
}
resources:
  andExample:
    type: aws:budgets:Budget
    name: and_example
    properties:
      name: budget-and-filter
      budgetType: COST
      limitAmount: '1200'
      limitUnit: USD
      timeUnit: MONTHLY
      filterExpression:
        ands:
          - dimensions:
              key: SERVICE
              values:
                - Amazon Elastic Compute Cloud - Compute
          - tags:
              key: Environment
              values:
                - Production

The ands array combines multiple filter conditions; all must match for costs to be included. This example tracks only EC2 costs tagged as Production, excluding development and staging environments. Each element in the ands array can use dimensions, tags, or costCategories.

Exclude specific regions with NOT filters

Organizations may want to track costs everywhere except specific regions, such as excluding a region used only for disaster recovery testing.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const notExample = new aws.budgets.Budget("not_example", {
    name: "budget-not-filter",
    budgetType: "COST",
    limitAmount: "1000",
    limitUnit: "USD",
    timeUnit: "MONTHLY",
    filterExpression: {
        not: {
            dimensions: {
                key: "REGION",
                values: ["us-west-2"],
            },
        },
    },
});
import pulumi
import pulumi_aws as aws

not_example = aws.budgets.Budget("not_example",
    name="budget-not-filter",
    budget_type="COST",
    limit_amount="1000",
    limit_unit="USD",
    time_unit="MONTHLY",
    filter_expression={
        "not_": {
            "dimensions": {
                "key": "REGION",
                "values": ["us-west-2"],
            },
        },
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/budgets"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := budgets.NewBudget(ctx, "not_example", &budgets.BudgetArgs{
			Name:        pulumi.String("budget-not-filter"),
			BudgetType:  pulumi.String("COST"),
			LimitAmount: pulumi.String("1000"),
			LimitUnit:   pulumi.String("USD"),
			TimeUnit:    pulumi.String("MONTHLY"),
			FilterExpression: &budgets.BudgetFilterExpressionArgs{
				Not: &budgets.BudgetFilterExpressionNotArgs{
					Dimensions: &budgets.BudgetFilterExpressionNotDimensionsArgs{
						Key: pulumi.String("REGION"),
						Values: pulumi.StringArray{
							pulumi.String("us-west-2"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var notExample = new Aws.Budgets.Budget("not_example", new()
    {
        Name = "budget-not-filter",
        BudgetType = "COST",
        LimitAmount = "1000",
        LimitUnit = "USD",
        TimeUnit = "MONTHLY",
        FilterExpression = new Aws.Budgets.Inputs.BudgetFilterExpressionArgs
        {
            Not = new Aws.Budgets.Inputs.BudgetFilterExpressionNotArgs
            {
                Dimensions = new Aws.Budgets.Inputs.BudgetFilterExpressionNotDimensionsArgs
                {
                    Key = "REGION",
                    Values = new[]
                    {
                        "us-west-2",
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.budgets.Budget;
import com.pulumi.aws.budgets.BudgetArgs;
import com.pulumi.aws.budgets.inputs.BudgetFilterExpressionArgs;
import com.pulumi.aws.budgets.inputs.BudgetFilterExpressionNotArgs;
import com.pulumi.aws.budgets.inputs.BudgetFilterExpressionNotDimensionsArgs;
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 notExample = new Budget("notExample", BudgetArgs.builder()
            .name("budget-not-filter")
            .budgetType("COST")
            .limitAmount("1000")
            .limitUnit("USD")
            .timeUnit("MONTHLY")
            .filterExpression(BudgetFilterExpressionArgs.builder()
                .not(BudgetFilterExpressionNotArgs.builder()
                    .dimensions(BudgetFilterExpressionNotDimensionsArgs.builder()
                        .key("REGION")
                        .values("us-west-2")
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  notExample:
    type: aws:budgets:Budget
    name: not_example
    properties:
      name: budget-not-filter
      budgetType: COST
      limitAmount: '1000'
      limitUnit: USD
      timeUnit: MONTHLY
      filterExpression:
        not:
          dimensions:
            key: REGION
            values:
              - us-west-2

The not object inverts a filter condition, excluding costs that match the specified criteria. This example tracks costs in all regions except us-west-2. NOT filters work with dimensions, tags, and costCategories, and can be combined with AND and OR logic for complex exclusion patterns.

Beyond these examples

These snippets focus on specific budget-level features: cost and usage budget types, email notifications and threshold alerts, and filter expressions (dimensions, tags, logical operators). They’re intentionally minimal rather than full cost management solutions.

The examples may reference pre-existing infrastructure such as valid email addresses for notifications. They focus on configuring the budget rather than provisioning notification infrastructure.

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

  • Auto-adjusting budgets (autoAdjustData)
  • SNS topic notifications (subscriberSnsTopicArns)
  • Cost category filters
  • Billing view integration (billingViewArn)

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

Let's configure AWS 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 & Immutability
What properties can't I change after creating a budget?
The accountId, name, and namePrefix properties are immutable. Changing them requires recreating the budget.
Should I use costFilters or filterExpression for filtering?
You must choose one; they conflict with each other. Use filterExpression for complex logic (AND/OR/NOT operators) or costFilters for simple name/value filtering. The filterExpression approach is newer and more flexible.
Budget Types & Limits
What budget types are available and when should I use each?

Four types are available:

  1. COST - Track monetary spend (use limitUnit: "USD")
  2. USAGE - Track resource consumption (use limitUnit: "GB" or similar)
  3. SAVINGS_PLANS_UTILIZATION - Track Savings Plan usage (use limitUnit: "PERCENTAGE")
  4. RI_UTILIZATION - Track Reserved Instance usage (use limitUnit: "PERCENTAGE")
What's the difference between fixed and planned budget limits?
Fixed limits use limitAmount and limitUnit for a constant budget. Planned limits use plannedLimits to specify varying amounts over time, with each entry defining a startTime, amount, and unit.
What time units can I use for budget resets?
Valid values are MONTHLY, QUARTERLY, ANNUALLY, and DAILY.
Filtering & Cost Tracking
How do I filter costs by resource tags?
Use costFilters with name: "TagKeyValue" and values in the format TagKey$TagValue. For example: "user:business-unit$human_resources" or "aws:createdBy$Pulumi".
How do I create complex filters with AND/OR/NOT logic?
Use filterExpression with ands, ors, and not properties. You can nest these operators to create compound filters combining dimensions, tags, and cost categories.
Can I filter by multiple services or dimensions?
Yes, use filterExpression with ors to match multiple services, or ands to require multiple conditions. For example, you can filter for EC2 OR RDS costs, or EC2 costs AND Production tag.
Time Periods & Notifications
How do I configure budget time periods?
Use timePeriodStart and timePeriodEnd in format 2017-01-01_12:00. If you don’t specify timePeriodStart, AWS defaults to the start of your chosen time period. There are no restrictions on the end date.
How do I set up budget notifications?
Use the notifications property with comparisonOperator, threshold, thresholdType, notificationType, and subscriberEmailAddresses. You can define multiple notifications for different thresholds.

Using a different cloud?

Explore monitoring guides for other cloud providers: