Configure AWS Budgets

The aws:budgets/budget:Budget resource, part of the Pulumi AWS provider, defines AWS Budgets that track spending or usage against thresholds and trigger notifications when limits are approached or exceeded. This guide focuses on three capabilities: cost and usage budget types, email notifications with threshold alerts, and planned limits and tag-based cost filtering.

Budgets require Cost Explorer to be enabled in your AWS account and may reference cost allocation tags or email addresses for notifications. The examples are intentionally small. Combine them with your own notification targets and cost allocation strategy.

Track EC2 costs with email notifications

Teams monitoring cloud spend often set cost budgets for specific services, with email alerts when spending approaches thresholds.

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 forecasted spending exceeds the threshold percentage, AWS sends email notifications to the specified addresses. The budgetType determines whether you’re tracking cost or usage; costFilters narrow the scope to specific services like EC2. The notifications array defines when alerts fire: comparisonOperator sets the direction (greater than, less than), threshold sets the percentage, and notificationType controls whether you’re alerted on actual or forecasted spend.

Define variable monthly spending limits

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, letting you define different spending caps for each time period. Each entry specifies a startTime and the amount/unit for that period. This approach works well for projects with known spending curves or teams ramping up infrastructure over time.

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 becomes GB instead of USD, and the budget tracks storage consumption rather than spending. This configuration alerts when S3 storage crosses a capacity threshold, regardless of pricing changes.

Track Savings Plan utilization percentage

Organizations with Savings Plans commitments monitor utilization to ensure they’re maximizing discount coverage.

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 SAVINGS_PLANS_UTILIZATION as the budgetType and measure in PERCENTAGE. The costTypes object controls which cost components are included; for utilization tracking, you typically enable only includeSubscription and disable credits, discounts, and other adjustments. This shows whether you’re using the capacity you’ve committed to purchase.

Monitor Reserved Instance utilization for RDS

Teams with Reserved Instance commitments track utilization by service to identify underused capacity.

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

const riUtilization = new aws.budgets.Budget("ri_utilization", {
    budgetType: "RI_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,
    },
    costFilters: [{
        name: "Service",
        values: ["Amazon Relational Database Service"],
    }],
});
import pulumi
import pulumi_aws as aws

ri_utilization = aws.budgets.Budget("ri_utilization",
    budget_type="RI_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,
    },
    cost_filters=[{
        "name": "Service",
        "values": ["Amazon Relational Database Service"],
    }])
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, "ri_utilization", &budgets.BudgetArgs{
			BudgetType:  pulumi.String("RI_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),
			},
			CostFilters: budgets.BudgetCostFilterArray{
				&budgets.BudgetCostFilterArgs{
					Name: pulumi.String("Service"),
					Values: pulumi.StringArray{
						pulumi.String("Amazon Relational Database Service"),
					},
				},
			},
		})
		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 riUtilization = new Aws.Budgets.Budget("ri_utilization", new()
    {
        BudgetType = "RI_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,
        },
        CostFilters = new[]
        {
            new Aws.Budgets.Inputs.BudgetCostFilterArgs
            {
                Name = "Service",
                Values = new[]
                {
                    "Amazon Relational Database Service",
                },
            },
        },
    });

});
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 com.pulumi.aws.budgets.inputs.BudgetCostFilterArgs;
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 riUtilization = new Budget("riUtilization", BudgetArgs.builder()
            .budgetType("RI_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())
            .costFilters(BudgetCostFilterArgs.builder()
                .name("Service")
                .values("Amazon Relational Database Service")
                .build())
            .build());

    }
}
resources:
  riUtilization:
    type: aws:budgets:Budget
    name: ri_utilization
    properties:
      budgetType: RI_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
      costFilters:
        - name: Service
          values:
            - Amazon Relational Database Service

RI utilization budgets work like Savings Plan budgets but use RI_UTILIZATION as the budgetType. The costFilters array narrows tracking to specific services; here, it monitors only RDS Reserved Instances. This helps identify whether your RI purchases match actual usage patterns.

Filter costs by resource tags

Cost allocation tags enable budget tracking by team, project, or business unit without creating separate AWS accounts.

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

const cost = new aws.budgets.Budget("cost", {costFilters: [{
    name: "TagKeyValue",
    values: [
        "aws:createdBy$Pulumi",
        "user:business-unit$human_resources",
    ],
}]});
import pulumi
import pulumi_aws as aws

cost = aws.budgets.Budget("cost", cost_filters=[{
    "name": "TagKeyValue",
    "values": [
        "aws:createdBy$Pulumi",
        "user:business-unit$human_resources",
    ],
}])
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{
			CostFilters: budgets.BudgetCostFilterArray{
				&budgets.BudgetCostFilterArgs{
					Name: pulumi.String("TagKeyValue"),
					Values: pulumi.StringArray{
						pulumi.String("aws:createdBy$Pulumi"),
						pulumi.String("user:business-unit$human_resources"),
					},
				},
			},
		})
		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()
    {
        CostFilters = new[]
        {
            new Aws.Budgets.Inputs.BudgetCostFilterArgs
            {
                Name = "TagKeyValue",
                Values = new[]
                {
                    "aws:createdBy$Pulumi",
                    "user:business-unit$human_resources",
                },
            },
        },
    });

});
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 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()
            .costFilters(BudgetCostFilterArgs.builder()
                .name("TagKeyValue")
                .values(                
                    "aws:createdBy$Pulumi",
                    "user:business-unit$human_resources")
                .build())
            .build());

    }
}
resources:
  cost:
    type: aws:budgets:Budget
    properties:
      costFilters:
        - name: TagKeyValue
          values:
            - aws:createdBy$Pulumi
            - user:business-unit$human_resources

The costFilters array accepts TagKeyValue entries that match resources with specific tags. The format is “TagKey$TagValue” (using a literal dollar sign as separator). You can track costs for resources created by specific tools (aws:createdBy$Pulumi) or assigned to business units (user:business-unit$human_resources). This requires cost allocation tags to be enabled and applied to your resources.

Beyond these examples

These snippets focus on specific budget features: cost and usage budget types, email notifications and threshold alerts, and planned limits and tag-based filtering. They’re intentionally minimal rather than full cost management solutions.

The examples may reference pre-existing infrastructure such as Cost Explorer enabled in the AWS account, valid email addresses for notifications, and cost allocation tags on resources for tag-based filtering. They focus on configuring the budget rather than provisioning the surrounding cost management infrastructure.

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

  • Auto-adjusting budgets (autoAdjustData)
  • SNS topic notifications (subscriberSnsTopicArns)
  • Time period boundaries (timePeriodStart, timePeriodEnd)
  • 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 Types & Configuration
What types of budgets can I create?
You can create four types: COST for monetary tracking, USAGE for resource consumption (like GB), SAVINGS_PLANS_UTILIZATION for Savings Plan utilization percentage, and RI_UTILIZATION for Reserved Instance utilization percentage.
What units can I use for budget limits?
The limitUnit depends on your budgetType: use USD for cost budgets, GB for usage budgets, and PERCENTAGE for utilization budgets (Savings Plans or RI).
What time periods are supported for budget resets?
The timeUnit property accepts MONTHLY, QUARTERLY, ANNUALLY, or DAILY to control how often the budget resets.
Time Periods & Scheduling
What date format should I use for time periods?
Use the format 2017-01-01_12:00 for both timePeriodStart and timePeriodEnd properties.
What happens if I don't specify a start date?
AWS defaults to the start of your chosen time period if you omit timePeriodStart. The start date must come before the end date.
Can I set different budget limits for different months?
Yes, use plannedLimits with multiple entries, each specifying startTime, amount, and unit for different time periods.
Cost Filtering & Tracking
How do I filter a budget by resource tags?
Use costFilters with name TagKeyValue and values formatted as key$value, like aws:createdBy$Pulumi or user:business-unit$human_resources.
What cost types should I configure for utilization budgets?
For SAVINGS_PLANS_UTILIZATION and RI_UTILIZATION budgets, set most costTypes to false except includeSubscription: true, as shown in the examples.
Immutability & Constraints
What properties can't I change after creating a budget?
The name, namePrefix, and accountId properties are immutable. Changing them will force resource replacement.

Using a different cloud?

Explore monitoring guides for other cloud providers: