Configure AWS S3 Bucket Metrics

The aws:s3/bucketMetric:BucketMetric resource, part of the Pulumi AWS provider, configures CloudWatch metrics collection for S3 buckets, enabling monitoring of request rates, data transfer, and errors. This guide focuses on three capabilities: bucket-wide metrics collection, prefix and tag-based filtering, and Access Point integration.

Metric configurations reference existing S3 buckets and optionally filter by Access Points or object tags. The examples are intentionally small. Combine them with your own CloudWatch dashboards and alarms.

Track metrics for an entire bucket

CloudWatch metrics help teams monitor request rates, data transfer, and error counts. The simplest configuration tracks all objects without filtering.

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

const example = new aws.s3.Bucket("example", {bucket: "example"});
const example_entire_bucket = new aws.s3.BucketMetric("example-entire-bucket", {
    bucket: example.id,
    name: "EntireBucket",
});
import pulumi
import pulumi_aws as aws

example = aws.s3.Bucket("example", bucket="example")
example_entire_bucket = aws.s3.BucketMetric("example-entire-bucket",
    bucket=example.id,
    name="EntireBucket")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := s3.NewBucket(ctx, "example", &s3.BucketArgs{
			Bucket: pulumi.String("example"),
		})
		if err != nil {
			return err
		}
		_, err = s3.NewBucketMetric(ctx, "example-entire-bucket", &s3.BucketMetricArgs{
			Bucket: example.ID(),
			Name:   pulumi.String("EntireBucket"),
		})
		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 example = new Aws.S3.Bucket("example", new()
    {
        BucketName = "example",
    });

    var example_entire_bucket = new Aws.S3.BucketMetric("example-entire-bucket", new()
    {
        Bucket = example.Id,
        Name = "EntireBucket",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.s3.BucketMetric;
import com.pulumi.aws.s3.BucketMetricArgs;
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 example = new Bucket("example", BucketArgs.builder()
            .bucket("example")
            .build());

        var example_entire_bucket = new BucketMetric("example-entire-bucket", BucketMetricArgs.builder()
            .bucket(example.id())
            .name("EntireBucket")
            .build());

    }
}
resources:
  example:
    type: aws:s3:Bucket
    properties:
      bucket: example
  example-entire-bucket:
    type: aws:s3:BucketMetric
    properties:
      bucket: ${example.id}
      name: EntireBucket

The bucket property references the bucket to monitor. The name property provides a unique identifier for this metrics configuration (up to 64 characters). Without a filter, CloudWatch collects metrics for all objects in the bucket.

Filter metrics by prefix and tags

Large buckets often contain multiple workloads. Filtering by prefix and tags lets you track specific subsets independently.

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

const example = new aws.s3.Bucket("example", {bucket: "example"});
const example_filtered = new aws.s3.BucketMetric("example-filtered", {
    bucket: example.id,
    name: "ImportantBlueDocuments",
    filter: {
        prefix: "documents/",
        tags: {
            priority: "high",
            "class": "blue",
        },
    },
});
import pulumi
import pulumi_aws as aws

example = aws.s3.Bucket("example", bucket="example")
example_filtered = aws.s3.BucketMetric("example-filtered",
    bucket=example.id,
    name="ImportantBlueDocuments",
    filter={
        "prefix": "documents/",
        "tags": {
            "priority": "high",
            "class": "blue",
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := s3.NewBucket(ctx, "example", &s3.BucketArgs{
			Bucket: pulumi.String("example"),
		})
		if err != nil {
			return err
		}
		_, err = s3.NewBucketMetric(ctx, "example-filtered", &s3.BucketMetricArgs{
			Bucket: example.ID(),
			Name:   pulumi.String("ImportantBlueDocuments"),
			Filter: &s3.BucketMetricFilterArgs{
				Prefix: pulumi.String("documents/"),
				Tags: pulumi.StringMap{
					"priority": pulumi.String("high"),
					"class":    pulumi.String("blue"),
				},
			},
		})
		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 example = new Aws.S3.Bucket("example", new()
    {
        BucketName = "example",
    });

    var example_filtered = new Aws.S3.BucketMetric("example-filtered", new()
    {
        Bucket = example.Id,
        Name = "ImportantBlueDocuments",
        Filter = new Aws.S3.Inputs.BucketMetricFilterArgs
        {
            Prefix = "documents/",
            Tags = 
            {
                { "priority", "high" },
                { "class", "blue" },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.s3.BucketMetric;
import com.pulumi.aws.s3.BucketMetricArgs;
import com.pulumi.aws.s3.inputs.BucketMetricFilterArgs;
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 example = new Bucket("example", BucketArgs.builder()
            .bucket("example")
            .build());

        var example_filtered = new BucketMetric("example-filtered", BucketMetricArgs.builder()
            .bucket(example.id())
            .name("ImportantBlueDocuments")
            .filter(BucketMetricFilterArgs.builder()
                .prefix("documents/")
                .tags(Map.ofEntries(
                    Map.entry("priority", "high"),
                    Map.entry("class", "blue")
                ))
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:s3:Bucket
    properties:
      bucket: example
  example-filtered:
    type: aws:s3:BucketMetric
    properties:
      bucket: ${example.id}
      name: ImportantBlueDocuments
      filter:
        prefix: documents/
        tags:
          priority: high
          class: blue

The filter property narrows metrics collection to objects matching criteria. The prefix limits collection to objects under a specific path. The tags property requires objects to have matching key-value pairs. CloudWatch only tracks objects that satisfy both conditions.

Track metrics through an S3 Access Point

S3 Access Points provide named network endpoints with their own permissions and network controls. Filtering by Access Point isolates traffic through specific endpoints.

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

const example = new aws.s3.Bucket("example", {bucket: "example"});
const example_access_point = new aws.s3.AccessPoint("example-access-point", {
    bucket: example.id,
    name: "example-access-point",
});
const example_filtered = new aws.s3.BucketMetric("example-filtered", {
    bucket: example.id,
    name: "ImportantBlueDocuments",
    filter: {
        accessPoint: example_access_point.arn,
        tags: {
            priority: "high",
            "class": "blue",
        },
    },
});
import pulumi
import pulumi_aws as aws

example = aws.s3.Bucket("example", bucket="example")
example_access_point = aws.s3.AccessPoint("example-access-point",
    bucket=example.id,
    name="example-access-point")
example_filtered = aws.s3.BucketMetric("example-filtered",
    bucket=example.id,
    name="ImportantBlueDocuments",
    filter={
        "access_point": example_access_point.arn,
        "tags": {
            "priority": "high",
            "class": "blue",
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := s3.NewBucket(ctx, "example", &s3.BucketArgs{
			Bucket: pulumi.String("example"),
		})
		if err != nil {
			return err
		}
		example_access_point, err := s3.NewAccessPoint(ctx, "example-access-point", &s3.AccessPointArgs{
			Bucket: example.ID(),
			Name:   pulumi.String("example-access-point"),
		})
		if err != nil {
			return err
		}
		_, err = s3.NewBucketMetric(ctx, "example-filtered", &s3.BucketMetricArgs{
			Bucket: example.ID(),
			Name:   pulumi.String("ImportantBlueDocuments"),
			Filter: &s3.BucketMetricFilterArgs{
				AccessPoint: example_access_point.Arn,
				Tags: pulumi.StringMap{
					"priority": pulumi.String("high"),
					"class":    pulumi.String("blue"),
				},
			},
		})
		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 example = new Aws.S3.Bucket("example", new()
    {
        BucketName = "example",
    });

    var example_access_point = new Aws.S3.AccessPoint("example-access-point", new()
    {
        Bucket = example.Id,
        Name = "example-access-point",
    });

    var example_filtered = new Aws.S3.BucketMetric("example-filtered", new()
    {
        Bucket = example.Id,
        Name = "ImportantBlueDocuments",
        Filter = new Aws.S3.Inputs.BucketMetricFilterArgs
        {
            AccessPoint = example_access_point.Arn,
            Tags = 
            {
                { "priority", "high" },
                { "class", "blue" },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.s3.AccessPoint;
import com.pulumi.aws.s3.AccessPointArgs;
import com.pulumi.aws.s3.BucketMetric;
import com.pulumi.aws.s3.BucketMetricArgs;
import com.pulumi.aws.s3.inputs.BucketMetricFilterArgs;
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 example = new Bucket("example", BucketArgs.builder()
            .bucket("example")
            .build());

        var example_access_point = new AccessPoint("example-access-point", AccessPointArgs.builder()
            .bucket(example.id())
            .name("example-access-point")
            .build());

        var example_filtered = new BucketMetric("example-filtered", BucketMetricArgs.builder()
            .bucket(example.id())
            .name("ImportantBlueDocuments")
            .filter(BucketMetricFilterArgs.builder()
                .accessPoint(example_access_point.arn())
                .tags(Map.ofEntries(
                    Map.entry("priority", "high"),
                    Map.entry("class", "blue")
                ))
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:s3:Bucket
    properties:
      bucket: example
  example-access-point:
    type: aws:s3:AccessPoint
    properties:
      bucket: ${example.id}
      name: example-access-point
  example-filtered:
    type: aws:s3:BucketMetric
    properties:
      bucket: ${example.id}
      name: ImportantBlueDocuments
      filter:
        accessPoint: ${["example-access-point"].arn}
        tags:
          priority: high
          class: blue

The accessPoint property in the filter references an Access Point ARN. CloudWatch tracks only requests routed through that endpoint. You can combine Access Point filtering with tags to further narrow the scope.

Beyond these examples

These snippets focus on specific metrics configuration features: bucket-wide and filtered metric collection, prefix and tag-based filtering, and Access Point integration. They’re intentionally minimal rather than full monitoring solutions.

The examples reference pre-existing infrastructure such as S3 buckets, S3 Access Points (for Access Point example), and tagged objects (for tag filtering). They focus on configuring metrics collection rather than provisioning buckets or dashboards.

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

  • CloudWatch dashboard integration
  • Alarm configuration based on metrics
  • Cost implications of metrics collection
  • Metrics retention and querying

These omissions are intentional: the goal is to illustrate how each metrics feature is wired, not provide drop-in monitoring modules. See the S3 BucketMetric resource reference for all available configuration options.

Let's configure AWS S3 Bucket Metrics

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Setup
What's the difference between monitoring an entire bucket vs specific objects?
Omit the filter property to collect metrics for the entire bucket. Add a filter with prefix, tags, or both to monitor only specific objects.
How do I import an existing metrics configuration?
Use the format bucket:metric with pulumi import, for example: pulumi import aws:s3/bucketMetric:BucketMetric my-bucket-entire-bucket my-bucket:EntireBucket
Filtering & Scope
What filtering options are available for metrics?
You can filter by prefix, tags, Access Point ARN (accessPoint), or a logical AND of prefix and tags.
Can I use S3 Access Points with bucket metrics?
Yes, specify the Access Point ARN in the filter.accessPoint field to collect metrics for objects accessed through that Access Point.
Limitations & Constraints
Can I use this resource with S3 directory buckets?
No, this resource cannot be used with S3 directory buckets. Use it only with standard S3 buckets.
What properties can't I change after creating the metrics configuration?
Both bucket and name are immutable. Changing either requires replacing the resource.
Are there any naming constraints for the metrics configuration?
The name must be a unique identifier for the bucket and cannot exceed 64 characters in length.

Using a different cloud?

Explore monitoring guides for other cloud providers: