Configure AWS CloudWatch Log Account Policies

The aws:cloudwatch/logAccountPolicy:LogAccountPolicy resource, part of the Pulumi AWS provider, defines account-wide CloudWatch Logs policies that apply data protection, subscription filters, or field indexing across log groups. This guide focuses on three capabilities: data protection with sensitive data detection, subscription filters with pattern matching, and field indexing for query optimization.

Account policies operate across all log groups in a region by default. Subscription filter policies can reference destination ARNs that must exist separately. The examples are intentionally small. Combine them with your own log group structure and processing destinations.

Detect and redact sensitive data across log groups

Organizations handling customer data often need to prevent sensitive information from being stored in CloudWatch Logs by scanning and masking detected patterns.

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

const dataProtection = new aws.cloudwatch.LogAccountPolicy("data_protection", {
    policyName: "data-protection",
    policyType: "DATA_PROTECTION_POLICY",
    policyDocument: JSON.stringify({
        Name: "DataProtection",
        Version: "2021-06-01",
        Statement: [
            {
                Sid: "Audit",
                DataIdentifier: ["arn:aws:dataprotection::aws:data-identifier/EmailAddress"],
                Operation: {
                    Audit: {
                        FindingsDestination: {},
                    },
                },
            },
            {
                Sid: "Redact",
                DataIdentifier: ["arn:aws:dataprotection::aws:data-identifier/EmailAddress"],
                Operation: {
                    Deidentify: {
                        MaskConfig: {},
                    },
                },
            },
        ],
    }),
});
import pulumi
import json
import pulumi_aws as aws

data_protection = aws.cloudwatch.LogAccountPolicy("data_protection",
    policy_name="data-protection",
    policy_type="DATA_PROTECTION_POLICY",
    policy_document=json.dumps({
        "Name": "DataProtection",
        "Version": "2021-06-01",
        "Statement": [
            {
                "Sid": "Audit",
                "DataIdentifier": ["arn:aws:dataprotection::aws:data-identifier/EmailAddress"],
                "Operation": {
                    "Audit": {
                        "FindingsDestination": {},
                    },
                },
            },
            {
                "Sid": "Redact",
                "DataIdentifier": ["arn:aws:dataprotection::aws:data-identifier/EmailAddress"],
                "Operation": {
                    "Deidentify": {
                        "MaskConfig": {},
                    },
                },
            },
        ],
    }))
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudwatch"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"Name":    "DataProtection",
			"Version": "2021-06-01",
			"Statement": []interface{}{
				map[string]interface{}{
					"Sid": "Audit",
					"DataIdentifier": []string{
						"arn:aws:dataprotection::aws:data-identifier/EmailAddress",
					},
					"Operation": map[string]interface{}{
						"Audit": map[string]interface{}{
							"FindingsDestination": map[string]interface{}{},
						},
					},
				},
				map[string]interface{}{
					"Sid": "Redact",
					"DataIdentifier": []string{
						"arn:aws:dataprotection::aws:data-identifier/EmailAddress",
					},
					"Operation": map[string]interface{}{
						"Deidentify": map[string]interface{}{
							"MaskConfig": map[string]interface{}{},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		_, err = cloudwatch.NewLogAccountPolicy(ctx, "data_protection", &cloudwatch.LogAccountPolicyArgs{
			PolicyName:     pulumi.String("data-protection"),
			PolicyType:     pulumi.String("DATA_PROTECTION_POLICY"),
			PolicyDocument: pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var dataProtection = new Aws.CloudWatch.LogAccountPolicy("data_protection", new()
    {
        PolicyName = "data-protection",
        PolicyType = "DATA_PROTECTION_POLICY",
        PolicyDocument = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Name"] = "DataProtection",
            ["Version"] = "2021-06-01",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Sid"] = "Audit",
                    ["DataIdentifier"] = new[]
                    {
                        "arn:aws:dataprotection::aws:data-identifier/EmailAddress",
                    },
                    ["Operation"] = new Dictionary<string, object?>
                    {
                        ["Audit"] = new Dictionary<string, object?>
                        {
                            ["FindingsDestination"] = new Dictionary<string, object?>
                            {
                            },
                        },
                    },
                },
                new Dictionary<string, object?>
                {
                    ["Sid"] = "Redact",
                    ["DataIdentifier"] = new[]
                    {
                        "arn:aws:dataprotection::aws:data-identifier/EmailAddress",
                    },
                    ["Operation"] = new Dictionary<string, object?>
                    {
                        ["Deidentify"] = new Dictionary<string, object?>
                        {
                            ["MaskConfig"] = new Dictionary<string, object?>
                            {
                            },
                        },
                    },
                },
            },
        }),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudwatch.LogAccountPolicy;
import com.pulumi.aws.cloudwatch.LogAccountPolicyArgs;
import static com.pulumi.codegen.internal.Serialization.*;
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 dataProtection = new LogAccountPolicy("dataProtection", LogAccountPolicyArgs.builder()
            .policyName("data-protection")
            .policyType("DATA_PROTECTION_POLICY")
            .policyDocument(serializeJson(
                jsonObject(
                    jsonProperty("Name", "DataProtection"),
                    jsonProperty("Version", "2021-06-01"),
                    jsonProperty("Statement", jsonArray(
                        jsonObject(
                            jsonProperty("Sid", "Audit"),
                            jsonProperty("DataIdentifier", jsonArray("arn:aws:dataprotection::aws:data-identifier/EmailAddress")),
                            jsonProperty("Operation", jsonObject(
                                jsonProperty("Audit", jsonObject(
                                    jsonProperty("FindingsDestination", jsonObject(

                                    ))
                                ))
                            ))
                        ), 
                        jsonObject(
                            jsonProperty("Sid", "Redact"),
                            jsonProperty("DataIdentifier", jsonArray("arn:aws:dataprotection::aws:data-identifier/EmailAddress")),
                            jsonProperty("Operation", jsonObject(
                                jsonProperty("Deidentify", jsonObject(
                                    jsonProperty("MaskConfig", jsonObject(

                                    ))
                                ))
                            ))
                        )
                    ))
                )))
            .build());

    }
}
resources:
  dataProtection:
    type: aws:cloudwatch:LogAccountPolicy
    name: data_protection
    properties:
      policyName: data-protection
      policyType: DATA_PROTECTION_POLICY
      policyDocument:
        fn::toJSON:
          Name: DataProtection
          Version: 2021-06-01
          Statement:
            - Sid: Audit
              DataIdentifier:
                - arn:aws:dataprotection::aws:data-identifier/EmailAddress
              Operation:
                Audit:
                  FindingsDestination: {}
            - Sid: Redact
              DataIdentifier:
                - arn:aws:dataprotection::aws:data-identifier/EmailAddress
              Operation:
                Deidentify:
                  MaskConfig: {}

The policyDocument defines two operations: Audit tracks where sensitive data appears, and Deidentify masks it using MaskConfig. The DataIdentifier property specifies which patterns to detect (here, email addresses using AWS-managed identifiers). This policy applies to all log groups in the account automatically.

Route logs to destinations with pattern matching

Teams centralizing logs forward specific log groups to Kinesis, Lambda, or Firehose for processing, while excluding certain groups from the filter.

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

const subscriptionFilter = new aws.cloudwatch.LogAccountPolicy("subscription_filter", {
    policyName: "subscription-filter",
    policyType: "SUBSCRIPTION_FILTER_POLICY",
    policyDocument: JSON.stringify({
        DestinationArn: test.arn,
        FilterPattern: "test",
    }),
    selectionCriteria: "LogGroupName NOT IN [\"excluded_log_group_name\"]",
});
import pulumi
import json
import pulumi_aws as aws

subscription_filter = aws.cloudwatch.LogAccountPolicy("subscription_filter",
    policy_name="subscription-filter",
    policy_type="SUBSCRIPTION_FILTER_POLICY",
    policy_document=json.dumps({
        "DestinationArn": test["arn"],
        "FilterPattern": "test",
    }),
    selection_criteria="LogGroupName NOT IN [\"excluded_log_group_name\"]")
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudwatch"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"DestinationArn": test.Arn,
			"FilterPattern":  "test",
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		_, err = cloudwatch.NewLogAccountPolicy(ctx, "subscription_filter", &cloudwatch.LogAccountPolicyArgs{
			PolicyName:        pulumi.String("subscription-filter"),
			PolicyType:        pulumi.String("SUBSCRIPTION_FILTER_POLICY"),
			PolicyDocument:    pulumi.String(json0),
			SelectionCriteria: pulumi.String("LogGroupName NOT IN [\"excluded_log_group_name\"]"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var subscriptionFilter = new Aws.CloudWatch.LogAccountPolicy("subscription_filter", new()
    {
        PolicyName = "subscription-filter",
        PolicyType = "SUBSCRIPTION_FILTER_POLICY",
        PolicyDocument = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["DestinationArn"] = test.Arn,
            ["FilterPattern"] = "test",
        }),
        SelectionCriteria = "LogGroupName NOT IN [\"excluded_log_group_name\"]",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudwatch.LogAccountPolicy;
import com.pulumi.aws.cloudwatch.LogAccountPolicyArgs;
import static com.pulumi.codegen.internal.Serialization.*;
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 subscriptionFilter = new LogAccountPolicy("subscriptionFilter", LogAccountPolicyArgs.builder()
            .policyName("subscription-filter")
            .policyType("SUBSCRIPTION_FILTER_POLICY")
            .policyDocument(serializeJson(
                jsonObject(
                    jsonProperty("DestinationArn", test.arn()),
                    jsonProperty("FilterPattern", "test")
                )))
            .selectionCriteria("LogGroupName NOT IN [\"excluded_log_group_name\"]")
            .build());

    }
}
resources:
  subscriptionFilter:
    type: aws:cloudwatch:LogAccountPolicy
    name: subscription_filter
    properties:
      policyName: subscription-filter
      policyType: SUBSCRIPTION_FILTER_POLICY
      policyDocument:
        fn::toJSON:
          DestinationArn: ${test.arn}
          FilterPattern: test
      selectionCriteria: LogGroupName NOT IN ["excluded_log_group_name"]

The policyDocument specifies the DestinationArn (where logs go) and FilterPattern (which events match). The selectionCriteria property uses a NOT IN clause to exclude specific log groups by name. Without selectionCriteria, the filter applies to all log groups.

Index custom fields for faster log queries

Applications logging structured JSON benefit from indexing specific fields to improve CloudWatch Logs Insights query performance.

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

const fieldIndex = new aws.cloudwatch.LogAccountPolicy("field_index", {
    policyName: "field-index",
    policyType: "FIELD_INDEX_POLICY",
    policyDocument: JSON.stringify({
        Fields: [
            "field1",
            "field2",
        ],
    }),
});
import pulumi
import json
import pulumi_aws as aws

field_index = aws.cloudwatch.LogAccountPolicy("field_index",
    policy_name="field-index",
    policy_type="FIELD_INDEX_POLICY",
    policy_document=json.dumps({
        "Fields": [
            "field1",
            "field2",
        ],
    }))
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudwatch"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"Fields": []string{
				"field1",
				"field2",
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		_, err = cloudwatch.NewLogAccountPolicy(ctx, "field_index", &cloudwatch.LogAccountPolicyArgs{
			PolicyName:     pulumi.String("field-index"),
			PolicyType:     pulumi.String("FIELD_INDEX_POLICY"),
			PolicyDocument: pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var fieldIndex = new Aws.CloudWatch.LogAccountPolicy("field_index", new()
    {
        PolicyName = "field-index",
        PolicyType = "FIELD_INDEX_POLICY",
        PolicyDocument = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Fields"] = new[]
            {
                "field1",
                "field2",
            },
        }),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudwatch.LogAccountPolicy;
import com.pulumi.aws.cloudwatch.LogAccountPolicyArgs;
import static com.pulumi.codegen.internal.Serialization.*;
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 fieldIndex = new LogAccountPolicy("fieldIndex", LogAccountPolicyArgs.builder()
            .policyName("field-index")
            .policyType("FIELD_INDEX_POLICY")
            .policyDocument(serializeJson(
                jsonObject(
                    jsonProperty("Fields", jsonArray(
                        "field1", 
                        "field2"
                    ))
                )))
            .build());

    }
}
resources:
  fieldIndex:
    type: aws:cloudwatch:LogAccountPolicy
    name: field_index
    properties:
      policyName: field-index
      policyType: FIELD_INDEX_POLICY
      policyDocument:
        fn::toJSON:
          Fields:
            - field1
            - field2

The policyDocument lists field names to index. CloudWatch Logs Insights queries against these fields run faster because the service pre-indexes them across all log groups. This is particularly useful for high-cardinality fields you query frequently.

Beyond these examples

These snippets focus on specific account policy features: data protection with audit and redaction, subscription filters with exclusion criteria, and field indexing for query performance. They’re intentionally minimal rather than full logging architectures.

The examples may reference pre-existing infrastructure such as destination ARNs for Kinesis streams, Lambda functions, or Firehose delivery streams. They focus on configuring the policy rather than provisioning destinations.

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

  • Transformer policies (TRANSFORMER_POLICY type)
  • Regional policy management (region property)
  • Scope configuration (currently only accepts ALL)

These omissions are intentional: the goal is to illustrate how each policy type is wired, not provide drop-in logging modules. See the CloudWatch Log Account Policy resource reference for all available configuration options.

Let's configure AWS CloudWatch Log Account Policies

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Policy Types & Limits
What policy types are available and how many can I create?
Four types are available: DATA_PROTECTION_POLICY, SUBSCRIPTION_FILTER_POLICY, FIELD_INDEX_POLICY, and TRANSFORMER_POLICY. You can have only one account policy per type in an account.
What values does the scope property accept?
The scope property currently defaults to and only accepts the value ALL.
Policy Document Structure
How do I structure the policy document for data protection?
Use JSON with Name, Version, and Statement array. Each statement includes DataIdentifier (ARN of data type to detect) and Operation (either Audit with FindingsDestination or Deidentify with MaskConfig).
How do I structure the policy document for subscription filters?
Use JSON with DestinationArn (where logs are sent) and FilterPattern (pattern to match log events).
How do I structure the policy document for field indexing?
Use JSON with a Fields array containing the field names you want to index.
Selection & Filtering
How do I exclude specific log groups from a subscription filter policy?
Use selectionCriteria with the format LogGroupName NOT IN ["excluded_log_group_name"]. This is the only allowable criteria selector syntax.
Immutability & Updates
What properties can't be changed after creation?
Three properties are immutable: policyName, policyType, and selectionCriteria. Changing any of these will force resource replacement.

Using a different cloud?

Explore monitoring guides for other cloud providers: