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 multiple log groups. This guide focuses on three capabilities: data protection and redaction, subscription filter routing, and field indexing for queries.

Account policies operate across all log groups in a region by default. Subscription filter policies require existing CloudWatch Logs destinations like Kinesis streams or Lambda functions. The examples are intentionally small. Combine them with your own log group structure and destinations.

Detect and redact sensitive data across log groups

Organizations handling regulated data need to prevent sensitive information from appearing in CloudWatch Logs. Data protection policies scan incoming log events and either audit or mask 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: {}

When log events arrive, CloudWatch scans them against the DataIdentifier patterns. The Audit operation records findings without modifying logs; the Deidentify operation masks matching data with the MaskConfig. This policy applies to all log groups in the account, protecting email addresses automatically.

Route filtered logs to a destination across log groups

Teams often need to forward logs matching specific patterns to external systems. Subscription filter policies apply a single filter and destination to multiple log groups.

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 (Kinesis, Firehose, or Lambda) and FilterPattern for matching log events. The selectionCriteria property excludes specific log groups using the LogGroupName NOT IN syntax. This avoids creating individual subscription filters per log group.

Index custom fields for faster log queries

CloudWatch Logs Insights queries scan all log data by default. Field indexing accelerates queries by pre-indexing specific JSON fields.

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 Fields array lists JSON field names to index across all log groups. CloudWatch pre-processes incoming logs to extract and index these fields, speeding up queries that filter or aggregate on them. This is most useful for high-volume log groups with frequent queries.

Beyond these examples

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

The examples may reference pre-existing infrastructure such as CloudWatch Logs destinations (Kinesis, Firehose, Lambda) for subscription filters. 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)
  • Scope configuration (currently only accepts ALL)
  • Multiple policies per type (limited to one per type per account)
  • Cross-region policy management

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 for CloudWatch Log Account Policies?
CloudWatch Logs supports four policy types: DATA_PROTECTION_POLICY, SUBSCRIPTION_FILTER_POLICY, FIELD_INDEX_POLICY, and TRANSFORMER_POLICY.
How many account policies can I create per type?
You can have one account policy per type in an account. Creating a second policy of the same type will replace the existing one.
Configuration & Immutability
What properties are immutable after creation?
The policyName, policyType, and selectionCriteria properties are immutable. Changing any of these forces resource replacement.
How do I exclude specific log groups from a subscription filter policy?
Use selectionCriteria with the syntax LogGroupName NOT IN ["excluded_log_group_name"]. This is the only allowable criteria selector format.
Policy Document Structure
What's the structure of a data protection policy document?
A data protection policy document is JSON with Name, Version, and Statement array. Each statement includes Sid, DataIdentifier (ARNs), and Operation (either Audit with FindingsDestination or Deidentify with MaskConfig).
What's the structure of a subscription filter policy document?
A subscription filter policy document is JSON with DestinationArn (where logs are sent) and FilterPattern (log filtering criteria).
What's the structure of a field index policy document?
A field index policy document is JSON with a Fields array containing the field names you want to index.

Using a different cloud?

Explore monitoring guides for other cloud providers: