The aws:securityhub/insight:Insight resource, part of the Pulumi AWS provider, defines custom Security Hub insights that filter and group security findings based on attributes like account ID, date range, network destination, or resource tags. This guide focuses on four capabilities: account-based filtering, time-based filtering with rolling windows, network destination filtering, and resource tag filtering.
Insights require Security Hub to be enabled and depend on findings generated by Security Hub or integrated security services. The examples are intentionally small. Combine them with your own Security Hub configuration and finding sources.
Group findings by AWS account ID
Multi-account environments need visibility into which accounts generate security findings.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.securityhub.Account("example", {});
const exampleInsight = new aws.securityhub.Insight("example", {
filters: {
awsAccountIds: [
{
comparison: "EQUALS",
value: "1234567890",
},
{
comparison: "EQUALS",
value: "09876543210",
},
],
},
groupByAttribute: "AwsAccountId",
name: "example-insight",
}, {
dependsOn: [example],
});
import pulumi
import pulumi_aws as aws
example = aws.securityhub.Account("example")
example_insight = aws.securityhub.Insight("example",
filters={
"aws_account_ids": [
{
"comparison": "EQUALS",
"value": "1234567890",
},
{
"comparison": "EQUALS",
"value": "09876543210",
},
],
},
group_by_attribute="AwsAccountId",
name="example-insight",
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.NewInsight(ctx, "example", &securityhub.InsightArgs{
Filters: &securityhub.InsightFiltersArgs{
AwsAccountIds: securityhub.InsightFiltersAwsAccountIdArray{
&securityhub.InsightFiltersAwsAccountIdArgs{
Comparison: pulumi.String("EQUALS"),
Value: pulumi.String("1234567890"),
},
&securityhub.InsightFiltersAwsAccountIdArgs{
Comparison: pulumi.String("EQUALS"),
Value: pulumi.String("09876543210"),
},
},
},
GroupByAttribute: pulumi.String("AwsAccountId"),
Name: pulumi.String("example-insight"),
}, 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 exampleInsight = new Aws.SecurityHub.Insight("example", new()
{
Filters = new Aws.SecurityHub.Inputs.InsightFiltersArgs
{
AwsAccountIds = new[]
{
new Aws.SecurityHub.Inputs.InsightFiltersAwsAccountIdArgs
{
Comparison = "EQUALS",
Value = "1234567890",
},
new Aws.SecurityHub.Inputs.InsightFiltersAwsAccountIdArgs
{
Comparison = "EQUALS",
Value = "09876543210",
},
},
},
GroupByAttribute = "AwsAccountId",
Name = "example-insight",
}, 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.Insight;
import com.pulumi.aws.securityhub.InsightArgs;
import com.pulumi.aws.securityhub.inputs.InsightFiltersArgs;
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 exampleInsight = new Insight("exampleInsight", InsightArgs.builder()
.filters(InsightFiltersArgs.builder()
.awsAccountIds(
InsightFiltersAwsAccountIdArgs.builder()
.comparison("EQUALS")
.value("1234567890")
.build(),
InsightFiltersAwsAccountIdArgs.builder()
.comparison("EQUALS")
.value("09876543210")
.build())
.build())
.groupByAttribute("AwsAccountId")
.name("example-insight")
.build(), CustomResourceOptions.builder()
.dependsOn(example)
.build());
}
}
resources:
example:
type: aws:securityhub:Account
exampleInsight:
type: aws:securityhub:Insight
name: example
properties:
filters:
awsAccountIds:
- comparison: EQUALS
value: '1234567890'
- comparison: EQUALS
value: '09876543210'
groupByAttribute: AwsAccountId
name: example-insight
options:
dependsOn:
- ${example}
The filters property defines criteria for including findings in the insight. The awsAccountIds array accepts multiple account IDs, each with a comparison operator. The groupByAttribute property organizes results by account ID, showing which accounts have the most findings.
Track findings from the last N days
Security teams often focus on recent findings to prioritize remediation efforts.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.securityhub.Account("example", {});
const exampleInsight = new aws.securityhub.Insight("example", {
filters: {
createdAts: [{
dateRange: {
unit: "DAYS",
value: 5,
},
}],
},
groupByAttribute: "CreatedAt",
name: "example-insight",
}, {
dependsOn: [example],
});
import pulumi
import pulumi_aws as aws
example = aws.securityhub.Account("example")
example_insight = aws.securityhub.Insight("example",
filters={
"created_ats": [{
"date_range": {
"unit": "DAYS",
"value": 5,
},
}],
},
group_by_attribute="CreatedAt",
name="example-insight",
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.NewInsight(ctx, "example", &securityhub.InsightArgs{
Filters: &securityhub.InsightFiltersArgs{
CreatedAts: securityhub.InsightFiltersCreatedAtArray{
&securityhub.InsightFiltersCreatedAtArgs{
DateRange: &securityhub.InsightFiltersCreatedAtDateRangeArgs{
Unit: pulumi.String("DAYS"),
Value: pulumi.Int(5),
},
},
},
},
GroupByAttribute: pulumi.String("CreatedAt"),
Name: pulumi.String("example-insight"),
}, 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 exampleInsight = new Aws.SecurityHub.Insight("example", new()
{
Filters = new Aws.SecurityHub.Inputs.InsightFiltersArgs
{
CreatedAts = new[]
{
new Aws.SecurityHub.Inputs.InsightFiltersCreatedAtArgs
{
DateRange = new Aws.SecurityHub.Inputs.InsightFiltersCreatedAtDateRangeArgs
{
Unit = "DAYS",
Value = 5,
},
},
},
},
GroupByAttribute = "CreatedAt",
Name = "example-insight",
}, 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.Insight;
import com.pulumi.aws.securityhub.InsightArgs;
import com.pulumi.aws.securityhub.inputs.InsightFiltersArgs;
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 exampleInsight = new Insight("exampleInsight", InsightArgs.builder()
.filters(InsightFiltersArgs.builder()
.createdAts(InsightFiltersCreatedAtArgs.builder()
.dateRange(InsightFiltersCreatedAtDateRangeArgs.builder()
.unit("DAYS")
.value(5)
.build())
.build())
.build())
.groupByAttribute("CreatedAt")
.name("example-insight")
.build(), CustomResourceOptions.builder()
.dependsOn(example)
.build());
}
}
resources:
example:
type: aws:securityhub:Account
exampleInsight:
type: aws:securityhub:Insight
name: example
properties:
filters:
createdAts:
- dateRange:
unit: DAYS
value: 5
groupByAttribute: CreatedAt
name: example-insight
options:
dependsOn:
- ${example}
The createdAts filter uses a dateRange object to specify a rolling time window. The unit property accepts DAYS and the value property sets the window size. This insight automatically updates to show findings from the last 5 days without manual date adjustments.
Monitor network traffic to specific IP ranges
Network-based findings often need filtering by destination IP to identify traffic patterns to internal networks.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.securityhub.Account("example", {});
const exampleInsight = new aws.securityhub.Insight("example", {
filters: {
networkDestinationIpv4s: [{
cidr: "10.0.0.0/16",
}],
},
groupByAttribute: "NetworkDestinationIpV4",
name: "example-insight",
}, {
dependsOn: [example],
});
import pulumi
import pulumi_aws as aws
example = aws.securityhub.Account("example")
example_insight = aws.securityhub.Insight("example",
filters={
"network_destination_ipv4s": [{
"cidr": "10.0.0.0/16",
}],
},
group_by_attribute="NetworkDestinationIpV4",
name="example-insight",
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.NewInsight(ctx, "example", &securityhub.InsightArgs{
Filters: &securityhub.InsightFiltersArgs{
NetworkDestinationIpv4s: securityhub.InsightFiltersNetworkDestinationIpv4Array{
&securityhub.InsightFiltersNetworkDestinationIpv4Args{
Cidr: pulumi.String("10.0.0.0/16"),
},
},
},
GroupByAttribute: pulumi.String("NetworkDestinationIpV4"),
Name: pulumi.String("example-insight"),
}, 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 exampleInsight = new Aws.SecurityHub.Insight("example", new()
{
Filters = new Aws.SecurityHub.Inputs.InsightFiltersArgs
{
NetworkDestinationIpv4s = new[]
{
new Aws.SecurityHub.Inputs.InsightFiltersNetworkDestinationIpv4Args
{
Cidr = "10.0.0.0/16",
},
},
},
GroupByAttribute = "NetworkDestinationIpV4",
Name = "example-insight",
}, 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.Insight;
import com.pulumi.aws.securityhub.InsightArgs;
import com.pulumi.aws.securityhub.inputs.InsightFiltersArgs;
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 exampleInsight = new Insight("exampleInsight", InsightArgs.builder()
.filters(InsightFiltersArgs.builder()
.networkDestinationIpv4s(InsightFiltersNetworkDestinationIpv4Args.builder()
.cidr("10.0.0.0/16")
.build())
.build())
.groupByAttribute("NetworkDestinationIpV4")
.name("example-insight")
.build(), CustomResourceOptions.builder()
.dependsOn(example)
.build());
}
}
resources:
example:
type: aws:securityhub:Account
exampleInsight:
type: aws:securityhub:Insight
name: example
properties:
filters:
networkDestinationIpv4s:
- cidr: 10.0.0.0/16
groupByAttribute: NetworkDestinationIpV4
name: example-insight
options:
dependsOn:
- ${example}
The networkDestinationIpv4s filter accepts CIDR notation to match findings where network traffic was destined for the specified IP range. This only applies to findings that include network destination data; findings without this attribute won’t match.
Filter findings by resource tags
Organizations use tags to identify resource ownership, environment, or compliance scope.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.securityhub.Account("example", {});
const exampleInsight = new aws.securityhub.Insight("example", {
filters: {
resourceTags: [{
comparison: "EQUALS",
key: "Environment",
value: "Production",
}],
},
groupByAttribute: "ResourceTags",
name: "example-insight",
}, {
dependsOn: [example],
});
import pulumi
import pulumi_aws as aws
example = aws.securityhub.Account("example")
example_insight = aws.securityhub.Insight("example",
filters={
"resource_tags": [{
"comparison": "EQUALS",
"key": "Environment",
"value": "Production",
}],
},
group_by_attribute="ResourceTags",
name="example-insight",
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.NewInsight(ctx, "example", &securityhub.InsightArgs{
Filters: &securityhub.InsightFiltersArgs{
ResourceTags: securityhub.InsightFiltersResourceTagArray{
&securityhub.InsightFiltersResourceTagArgs{
Comparison: pulumi.String("EQUALS"),
Key: pulumi.String("Environment"),
Value: pulumi.String("Production"),
},
},
},
GroupByAttribute: pulumi.String("ResourceTags"),
Name: pulumi.String("example-insight"),
}, 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 exampleInsight = new Aws.SecurityHub.Insight("example", new()
{
Filters = new Aws.SecurityHub.Inputs.InsightFiltersArgs
{
ResourceTags = new[]
{
new Aws.SecurityHub.Inputs.InsightFiltersResourceTagArgs
{
Comparison = "EQUALS",
Key = "Environment",
Value = "Production",
},
},
},
GroupByAttribute = "ResourceTags",
Name = "example-insight",
}, 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.Insight;
import com.pulumi.aws.securityhub.InsightArgs;
import com.pulumi.aws.securityhub.inputs.InsightFiltersArgs;
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 exampleInsight = new Insight("exampleInsight", InsightArgs.builder()
.filters(InsightFiltersArgs.builder()
.resourceTags(InsightFiltersResourceTagArgs.builder()
.comparison("EQUALS")
.key("Environment")
.value("Production")
.build())
.build())
.groupByAttribute("ResourceTags")
.name("example-insight")
.build(), CustomResourceOptions.builder()
.dependsOn(example)
.build());
}
}
resources:
example:
type: aws:securityhub:Account
exampleInsight:
type: aws:securityhub:Insight
name: example
properties:
filters:
resourceTags:
- comparison: EQUALS
key: Environment
value: Production
groupByAttribute: ResourceTags
name: example-insight
options:
dependsOn:
- ${example}
The resourceTags filter matches findings on resources with specific tag key-value pairs. The comparison operator determines the match type (EQUALS, CONTAINS, etc.). Resources must have the specified tags for their findings to appear in the insight.
Beyond these examples
These snippets focus on specific insight-level features: account and time-based filtering, network destination filtering, and resource tag filtering. They’re intentionally minimal rather than full security monitoring solutions.
The examples assume pre-existing infrastructure such as Security Hub enabled in the AWS account, and security findings generated by Security Hub or integrated services. They focus on configuring the insight rather than provisioning Security Hub itself.
To keep things focused, common insight patterns are omitted, including:
- Confidence-based filtering (confidences with gte/lte)
- Multiple filter combinations (up to 10 distinct attributes)
- Other filter types (severity, compliance status, workflow state)
- Cross-region insight aggregation
These omissions are intentional: the goal is to illustrate how each insight filter is wired, not provide drop-in security monitoring modules. See the Security Hub Insight resource reference for all available configuration options.
Let's create AWS Security Hub Custom Insights
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Setup & Dependencies
dependsOn to ensure the aws.securityhub.Account resource is created first.Filters & Criteria
comparison and value for account IDs, dateRange for dates, or cidr for IP addresses.createdAts filter with a dateRange object that specifies unit (like DAYS) and value (like 5 for the last 5 days).awsAccountIds filter with multiple objects, each containing comparison: "EQUALS" and a value for each account ID.Grouping & Organization
groupByAttribute determines how findings are organized in the insight. For example, grouping by ResourceId produces a list of resource identifiers, while grouping by AwsAccountId organizes findings by account.groupByAttribute matching the primary filter being used (e.g., filtering by awsAccountIds with groupByAttribute: "AwsAccountId").