Configure AWS Security Hub Finding Aggregators

The aws:securityhub/findingAggregator:FindingAggregator resource, part of the Pulumi AWS provider, configures Security Hub finding aggregation: which regions send findings to a central home region. This guide focuses on three capabilities: all-regions aggregation, region exclusion patterns, and explicit region selection.

The aggregator requires Security Hub to be enabled in each region you want to aggregate from. The examples are intentionally small. Combine them with your own Security Hub account configuration and regional enablement.

Aggregate findings from all available regions

Organizations with multi-region deployments often need a single view of security findings across their entire AWS footprint.

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

const example = new aws.securityhub.Account("example", {});
const exampleFindingAggregator = new aws.securityhub.FindingAggregator("example", {linkingMode: "ALL_REGIONS"}, {
    dependsOn: [example],
});
import pulumi
import pulumi_aws as aws

example = aws.securityhub.Account("example")
example_finding_aggregator = aws.securityhub.FindingAggregator("example", linking_mode="ALL_REGIONS",
opts = pulumi.ResourceOptions(depends_on=[example]))
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := securityhub.NewAccount(ctx, "example", nil)
		if err != nil {
			return err
		}
		_, err = securityhub.NewFindingAggregator(ctx, "example", &securityhub.FindingAggregatorArgs{
			LinkingMode: pulumi.String("ALL_REGIONS"),
		}, pulumi.DependsOn([]pulumi.Resource{
			example,
		}))
		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.SecurityHub.Account("example");

    var exampleFindingAggregator = new Aws.SecurityHub.FindingAggregator("example", new()
    {
        LinkingMode = "ALL_REGIONS",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            example,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.securityhub.Account;
import com.pulumi.aws.securityhub.FindingAggregator;
import com.pulumi.aws.securityhub.FindingAggregatorArgs;
import com.pulumi.resources.CustomResourceOptions;
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 Account("example");

        var exampleFindingAggregator = new FindingAggregator("exampleFindingAggregator", FindingAggregatorArgs.builder()
            .linkingMode("ALL_REGIONS")
            .build(), CustomResourceOptions.builder()
                .dependsOn(example)
                .build());

    }
}
resources:
  example:
    type: aws:securityhub:Account
  exampleFindingAggregator:
    type: aws:securityhub:FindingAggregator
    name: example
    properties:
      linkingMode: ALL_REGIONS
    options:
      dependsOn:
        - ${example}

When linkingMode is set to ALL_REGIONS, Security Hub automatically pulls findings from every region where it’s enabled. The dependsOn relationship ensures the Security Hub account exists before creating the aggregator. As you enable Security Hub in new regions, they automatically feed findings to this aggregator without configuration changes.

Exclude specific regions from aggregation

Some organizations need to exclude certain regions from centralized monitoring due to compliance boundaries or separate security teams.

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

const example = new aws.securityhub.Account("example", {});
const exampleFindingAggregator = new aws.securityhub.FindingAggregator("example", {
    linkingMode: "ALL_REGIONS_EXCEPT_SPECIFIED",
    specifiedRegions: [
        "eu-west-1",
        "eu-west-2",
    ],
}, {
    dependsOn: [example],
});
import pulumi
import pulumi_aws as aws

example = aws.securityhub.Account("example")
example_finding_aggregator = aws.securityhub.FindingAggregator("example",
    linking_mode="ALL_REGIONS_EXCEPT_SPECIFIED",
    specified_regions=[
        "eu-west-1",
        "eu-west-2",
    ],
    opts = pulumi.ResourceOptions(depends_on=[example]))
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := securityhub.NewAccount(ctx, "example", nil)
		if err != nil {
			return err
		}
		_, err = securityhub.NewFindingAggregator(ctx, "example", &securityhub.FindingAggregatorArgs{
			LinkingMode: pulumi.String("ALL_REGIONS_EXCEPT_SPECIFIED"),
			SpecifiedRegions: pulumi.StringArray{
				pulumi.String("eu-west-1"),
				pulumi.String("eu-west-2"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			example,
		}))
		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.SecurityHub.Account("example");

    var exampleFindingAggregator = new Aws.SecurityHub.FindingAggregator("example", new()
    {
        LinkingMode = "ALL_REGIONS_EXCEPT_SPECIFIED",
        SpecifiedRegions = new[]
        {
            "eu-west-1",
            "eu-west-2",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            example,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.securityhub.Account;
import com.pulumi.aws.securityhub.FindingAggregator;
import com.pulumi.aws.securityhub.FindingAggregatorArgs;
import com.pulumi.resources.CustomResourceOptions;
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 Account("example");

        var exampleFindingAggregator = new FindingAggregator("exampleFindingAggregator", FindingAggregatorArgs.builder()
            .linkingMode("ALL_REGIONS_EXCEPT_SPECIFIED")
            .specifiedRegions(            
                "eu-west-1",
                "eu-west-2")
            .build(), CustomResourceOptions.builder()
                .dependsOn(example)
                .build());

    }
}
resources:
  example:
    type: aws:securityhub:Account
  exampleFindingAggregator:
    type: aws:securityhub:FindingAggregator
    name: example
    properties:
      linkingMode: ALL_REGIONS_EXCEPT_SPECIFIED
      specifiedRegions:
        - eu-west-1
        - eu-west-2
    options:
      dependsOn:
        - ${example}

The ALL_REGIONS_EXCEPT_SPECIFIED mode aggregates from all regions except those listed in specifiedRegions. This is useful when most regions should report centrally but a few need isolated monitoring. Like ALL_REGIONS, this mode automatically includes new regions unless you add them to the exclusion list.

Aggregate findings from selected regions only

Teams that operate in a limited set of regions can explicitly list which regions to include.

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

const example = new aws.securityhub.Account("example", {});
const exampleFindingAggregator = new aws.securityhub.FindingAggregator("example", {
    linkingMode: "SPECIFIED_REGIONS",
    specifiedRegions: [
        "eu-west-1",
        "eu-west-2",
    ],
}, {
    dependsOn: [example],
});
import pulumi
import pulumi_aws as aws

example = aws.securityhub.Account("example")
example_finding_aggregator = aws.securityhub.FindingAggregator("example",
    linking_mode="SPECIFIED_REGIONS",
    specified_regions=[
        "eu-west-1",
        "eu-west-2",
    ],
    opts = pulumi.ResourceOptions(depends_on=[example]))
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := securityhub.NewAccount(ctx, "example", nil)
		if err != nil {
			return err
		}
		_, err = securityhub.NewFindingAggregator(ctx, "example", &securityhub.FindingAggregatorArgs{
			LinkingMode: pulumi.String("SPECIFIED_REGIONS"),
			SpecifiedRegions: pulumi.StringArray{
				pulumi.String("eu-west-1"),
				pulumi.String("eu-west-2"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			example,
		}))
		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.SecurityHub.Account("example");

    var exampleFindingAggregator = new Aws.SecurityHub.FindingAggregator("example", new()
    {
        LinkingMode = "SPECIFIED_REGIONS",
        SpecifiedRegions = new[]
        {
            "eu-west-1",
            "eu-west-2",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            example,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.securityhub.Account;
import com.pulumi.aws.securityhub.FindingAggregator;
import com.pulumi.aws.securityhub.FindingAggregatorArgs;
import com.pulumi.resources.CustomResourceOptions;
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 Account("example");

        var exampleFindingAggregator = new FindingAggregator("exampleFindingAggregator", FindingAggregatorArgs.builder()
            .linkingMode("SPECIFIED_REGIONS")
            .specifiedRegions(            
                "eu-west-1",
                "eu-west-2")
            .build(), CustomResourceOptions.builder()
                .dependsOn(example)
                .build());

    }
}
resources:
  example:
    type: aws:securityhub:Account
  exampleFindingAggregator:
    type: aws:securityhub:FindingAggregator
    name: example
    properties:
      linkingMode: SPECIFIED_REGIONS
      specifiedRegions:
        - eu-west-1
        - eu-west-2
    options:
      dependsOn:
        - ${example}

The SPECIFIED_REGIONS mode pulls findings only from the regions you list in specifiedRegions. Unlike the all-regions modes, this won’t automatically aggregate from new regions; you must update specifiedRegions to include them.

Beyond these examples

These snippets focus on specific aggregator features: region linking modes and automatic aggregation from new regions. They’re intentionally minimal rather than full Security Hub deployments.

The examples assume pre-existing infrastructure such as Security Hub enabled in target regions and an AWS provider configured with a home region. They focus on configuring the aggregator rather than provisioning Security Hub itself.

To keep things focused, common Security Hub patterns are omitted, including:

  • Cross-account aggregation configuration
  • Finding filters or custom insights
  • Integration with other security services
  • Notification or automation based on aggregated findings

These omissions are intentional: the goal is to illustrate how each aggregation mode is wired, not provide drop-in security monitoring modules. See the Security Hub FindingAggregator resource reference for all available configuration options.

Let's configure AWS Security Hub Finding Aggregators

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Prerequisites & Setup
Why do I need to enable Security Hub before creating a finding aggregator?
Security Hub must be enabled in a region before the aggregator can pull through findings. All examples show using dependsOn with aws.securityhub.Account to ensure proper creation order.
What's the correct way to set up the dependency on Security Hub?
Create an aws.securityhub.Account resource first, then reference it in the finding aggregator’s dependsOn option to ensure Security Hub is enabled before the aggregator is created.
Configuration & Linking Modes
What linking modes are available for finding aggregation?
Four modes are available: ALL_REGIONS aggregates from all regions, ALL_REGIONS_EXCEPT_SPECIFIED excludes specific regions, SPECIFIED_REGIONS includes only specific regions, and NO_REGIONS doesn’t link any regions.
When do I need to provide the specifiedRegions parameter?
You must provide specifiedRegions when linkingMode is set to ALL_REGIONS_EXCEPT_SPECIFIED or SPECIFIED_REGIONS. It’s not needed for ALL_REGIONS or NO_REGIONS modes.
Will new AWS regions be automatically included in aggregation?
Yes, if you use ALL_REGIONS or ALL_REGIONS_EXCEPT_SPECIFIED. Security Hub automatically aggregates findings from new regions as they become available and you opt into them.

Using a different cloud?

Explore security guides for other cloud providers: