The aws:cfg/recorder:Recorder resource, part of the Pulumi AWS provider, defines an AWS Config recorder that captures resource configuration changes across your account. This guide focuses on three capabilities: basic recorder setup, resource type filtering, and recording frequency control.
Config recorders require an IAM role with appropriate permissions and don’t start automatically. You must create a separate DeliveryChannel resource to store configuration snapshots and use RecorderStatus to activate the recorder. The examples are intentionally small. Combine them with your own delivery channel and activation logic.
Create a recorder with minimal configuration
AWS Config tracks resource configuration changes across your account, capturing snapshots and sending them to a delivery channel.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const assumeRole = aws.iam.getPolicyDocument({
statements: [{
effect: "Allow",
principals: [{
type: "Service",
identifiers: ["config.amazonaws.com"],
}],
actions: ["sts:AssumeRole"],
}],
});
const r = new aws.iam.Role("r", {
name: "awsconfig-example",
assumeRolePolicy: assumeRole.then(assumeRole => assumeRole.json),
});
const foo = new aws.cfg.Recorder("foo", {
name: "example",
roleArn: r.arn,
});
import pulumi
import pulumi_aws as aws
assume_role = aws.iam.get_policy_document(statements=[{
"effect": "Allow",
"principals": [{
"type": "Service",
"identifiers": ["config.amazonaws.com"],
}],
"actions": ["sts:AssumeRole"],
}])
r = aws.iam.Role("r",
name="awsconfig-example",
assume_role_policy=assume_role.json)
foo = aws.cfg.Recorder("foo",
name="example",
role_arn=r.arn)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cfg"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
assumeRole, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
{
Effect: pulumi.StringRef("Allow"),
Principals: []iam.GetPolicyDocumentStatementPrincipal{
{
Type: "Service",
Identifiers: []string{
"config.amazonaws.com",
},
},
},
Actions: []string{
"sts:AssumeRole",
},
},
},
}, nil)
if err != nil {
return err
}
r, err := iam.NewRole(ctx, "r", &iam.RoleArgs{
Name: pulumi.String("awsconfig-example"),
AssumeRolePolicy: pulumi.String(assumeRole.Json),
})
if err != nil {
return err
}
_, err = cfg.NewRecorder(ctx, "foo", &cfg.RecorderArgs{
Name: pulumi.String("example"),
RoleArn: r.Arn,
})
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 assumeRole = Aws.Iam.GetPolicyDocument.Invoke(new()
{
Statements = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
{
Effect = "Allow",
Principals = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
{
Type = "Service",
Identifiers = new[]
{
"config.amazonaws.com",
},
},
},
Actions = new[]
{
"sts:AssumeRole",
},
},
},
});
var r = new Aws.Iam.Role("r", new()
{
Name = "awsconfig-example",
AssumeRolePolicy = assumeRole.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
});
var foo = new Aws.Cfg.Recorder("foo", new()
{
Name = "example",
RoleArn = r.Arn,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.cfg.Recorder;
import com.pulumi.aws.cfg.RecorderArgs;
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) {
final var assumeRole = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
.statements(GetPolicyDocumentStatementArgs.builder()
.effect("Allow")
.principals(GetPolicyDocumentStatementPrincipalArgs.builder()
.type("Service")
.identifiers("config.amazonaws.com")
.build())
.actions("sts:AssumeRole")
.build())
.build());
var r = new Role("r", RoleArgs.builder()
.name("awsconfig-example")
.assumeRolePolicy(assumeRole.json())
.build());
var foo = new Recorder("foo", RecorderArgs.builder()
.name("example")
.roleArn(r.arn())
.build());
}
}
resources:
foo:
type: aws:cfg:Recorder
properties:
name: example
roleArn: ${r.arn}
r:
type: aws:iam:Role
properties:
name: awsconfig-example
assumeRolePolicy: ${assumeRole.json}
variables:
assumeRole:
fn::invoke:
function: aws:iam:getPolicyDocument
arguments:
statements:
- effect: Allow
principals:
- type: Service
identifiers:
- config.amazonaws.com
actions:
- sts:AssumeRole
The roleArn grants Config permissions to read resource configurations and write to the delivery channel. The name defaults to “default” if omitted. This example creates the IAM role inline with the basic assume-role policy Config requires. Without recordingGroup or recordingMode properties, the recorder uses AWS Config defaults.
Exclude specific resource types from recording
Large accounts often reduce costs by excluding high-churn resources from continuous tracking.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const foo = new aws.cfg.Recorder("foo", {
name: "example",
roleArn: r.arn,
recordingGroup: {
allSupported: false,
exclusionByResourceTypes: [{
resourceTypes: ["AWS::EC2::Instance"],
}],
recordingStrategies: [{
useOnly: "EXCLUSION_BY_RESOURCE_TYPES",
}],
},
});
import pulumi
import pulumi_aws as aws
foo = aws.cfg.Recorder("foo",
name="example",
role_arn=r["arn"],
recording_group={
"all_supported": False,
"exclusion_by_resource_types": [{
"resource_types": ["AWS::EC2::Instance"],
}],
"recording_strategies": [{
"use_only": "EXCLUSION_BY_RESOURCE_TYPES",
}],
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cfg"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := cfg.NewRecorder(ctx, "foo", &cfg.RecorderArgs{
Name: pulumi.String("example"),
RoleArn: pulumi.Any(r.Arn),
RecordingGroup: &cfg.RecorderRecordingGroupArgs{
AllSupported: pulumi.Bool(false),
ExclusionByResourceTypes: cfg.RecorderRecordingGroupExclusionByResourceTypeArray{
&cfg.RecorderRecordingGroupExclusionByResourceTypeArgs{
ResourceTypes: pulumi.StringArray{
pulumi.String("AWS::EC2::Instance"),
},
},
},
RecordingStrategies: cfg.RecorderRecordingGroupRecordingStrategyArray{
&cfg.RecorderRecordingGroupRecordingStrategyArgs{
UseOnly: pulumi.String("EXCLUSION_BY_RESOURCE_TYPES"),
},
},
},
})
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 foo = new Aws.Cfg.Recorder("foo", new()
{
Name = "example",
RoleArn = r.Arn,
RecordingGroup = new Aws.Cfg.Inputs.RecorderRecordingGroupArgs
{
AllSupported = false,
ExclusionByResourceTypes = new[]
{
new Aws.Cfg.Inputs.RecorderRecordingGroupExclusionByResourceTypeArgs
{
ResourceTypes = new[]
{
"AWS::EC2::Instance",
},
},
},
RecordingStrategies = new[]
{
new Aws.Cfg.Inputs.RecorderRecordingGroupRecordingStrategyArgs
{
UseOnly = "EXCLUSION_BY_RESOURCE_TYPES",
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cfg.Recorder;
import com.pulumi.aws.cfg.RecorderArgs;
import com.pulumi.aws.cfg.inputs.RecorderRecordingGroupArgs;
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 foo = new Recorder("foo", RecorderArgs.builder()
.name("example")
.roleArn(r.arn())
.recordingGroup(RecorderRecordingGroupArgs.builder()
.allSupported(false)
.exclusionByResourceTypes(RecorderRecordingGroupExclusionByResourceTypeArgs.builder()
.resourceTypes("AWS::EC2::Instance")
.build())
.recordingStrategies(RecorderRecordingGroupRecordingStrategyArgs.builder()
.useOnly("EXCLUSION_BY_RESOURCE_TYPES")
.build())
.build())
.build());
}
}
resources:
foo:
type: aws:cfg:Recorder
properties:
name: example
roleArn: ${r.arn}
recordingGroup:
allSupported: false
exclusionByResourceTypes:
- resourceTypes:
- AWS::EC2::Instance
recordingStrategies:
- useOnly: EXCLUSION_BY_RESOURCE_TYPES
The recordingGroup property controls which resources Config tracks. Setting allSupported to false disables the default “record everything” behavior. The exclusionByResourceTypes block lists resource types to skip, and recordingStrategies tells Config to use exclusion-based filtering. This configuration tracks all resources except EC2 instances.
Mix continuous and periodic recording frequencies
Some resources need continuous monitoring while others can be checked daily to balance compliance with cost.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const foo = new aws.cfg.Recorder("foo", {
name: "example",
roleArn: r.arn,
recordingGroup: {
allSupported: false,
includeGlobalResourceTypes: false,
resourceTypes: [
"AWS::EC2::Instance",
"AWS::EC2::NetworkInterface",
],
},
recordingMode: {
recordingFrequency: "CONTINUOUS",
recordingModeOverride: {
description: "Only record EC2 network interfaces daily",
resourceTypes: ["AWS::EC2::NetworkInterface"],
recordingFrequency: "DAILY",
},
},
});
import pulumi
import pulumi_aws as aws
foo = aws.cfg.Recorder("foo",
name="example",
role_arn=r["arn"],
recording_group={
"all_supported": False,
"include_global_resource_types": False,
"resource_types": [
"AWS::EC2::Instance",
"AWS::EC2::NetworkInterface",
],
},
recording_mode={
"recording_frequency": "CONTINUOUS",
"recording_mode_override": {
"description": "Only record EC2 network interfaces daily",
"resource_types": ["AWS::EC2::NetworkInterface"],
"recording_frequency": "DAILY",
},
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cfg"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := cfg.NewRecorder(ctx, "foo", &cfg.RecorderArgs{
Name: pulumi.String("example"),
RoleArn: pulumi.Any(r.Arn),
RecordingGroup: &cfg.RecorderRecordingGroupArgs{
AllSupported: pulumi.Bool(false),
IncludeGlobalResourceTypes: pulumi.Bool(false),
ResourceTypes: pulumi.StringArray{
pulumi.String("AWS::EC2::Instance"),
pulumi.String("AWS::EC2::NetworkInterface"),
},
},
RecordingMode: &cfg.RecorderRecordingModeArgs{
RecordingFrequency: pulumi.String("CONTINUOUS"),
RecordingModeOverride: &cfg.RecorderRecordingModeRecordingModeOverrideArgs{
Description: pulumi.String("Only record EC2 network interfaces daily"),
ResourceTypes: pulumi.StringArray{
pulumi.String("AWS::EC2::NetworkInterface"),
},
RecordingFrequency: pulumi.String("DAILY"),
},
},
})
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 foo = new Aws.Cfg.Recorder("foo", new()
{
Name = "example",
RoleArn = r.Arn,
RecordingGroup = new Aws.Cfg.Inputs.RecorderRecordingGroupArgs
{
AllSupported = false,
IncludeGlobalResourceTypes = false,
ResourceTypes = new[]
{
"AWS::EC2::Instance",
"AWS::EC2::NetworkInterface",
},
},
RecordingMode = new Aws.Cfg.Inputs.RecorderRecordingModeArgs
{
RecordingFrequency = "CONTINUOUS",
RecordingModeOverride = new Aws.Cfg.Inputs.RecorderRecordingModeRecordingModeOverrideArgs
{
Description = "Only record EC2 network interfaces daily",
ResourceTypes = new[]
{
"AWS::EC2::NetworkInterface",
},
RecordingFrequency = "DAILY",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cfg.Recorder;
import com.pulumi.aws.cfg.RecorderArgs;
import com.pulumi.aws.cfg.inputs.RecorderRecordingGroupArgs;
import com.pulumi.aws.cfg.inputs.RecorderRecordingModeArgs;
import com.pulumi.aws.cfg.inputs.RecorderRecordingModeRecordingModeOverrideArgs;
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 foo = new Recorder("foo", RecorderArgs.builder()
.name("example")
.roleArn(r.arn())
.recordingGroup(RecorderRecordingGroupArgs.builder()
.allSupported(false)
.includeGlobalResourceTypes(false)
.resourceTypes(
"AWS::EC2::Instance",
"AWS::EC2::NetworkInterface")
.build())
.recordingMode(RecorderRecordingModeArgs.builder()
.recordingFrequency("CONTINUOUS")
.recordingModeOverride(RecorderRecordingModeRecordingModeOverrideArgs.builder()
.description("Only record EC2 network interfaces daily")
.resourceTypes("AWS::EC2::NetworkInterface")
.recordingFrequency("DAILY")
.build())
.build())
.build());
}
}
resources:
foo:
type: aws:cfg:Recorder
properties:
name: example
roleArn: ${r.arn}
recordingGroup:
allSupported: false
includeGlobalResourceTypes: false
resourceTypes:
- AWS::EC2::Instance
- AWS::EC2::NetworkInterface
recordingMode:
recordingFrequency: CONTINUOUS
recordingModeOverride:
description: Only record EC2 network interfaces daily
resourceTypes:
- AWS::EC2::NetworkInterface
recordingFrequency: DAILY
The recordingMode property sets the default recording frequency for all resources. The recordingModeOverride block defines exceptions: here, EC2 network interfaces are recorded daily while other resources (EC2 instances) follow the continuous default. The resourceTypes list in recordingGroup specifies which resources to track when allSupported is false.
Beyond these examples
These snippets focus on specific recorder-level features: resource type filtering and exclusion, and recording frequency control. They’re intentionally minimal rather than full Config deployments.
The examples reference pre-existing infrastructure such as IAM roles with Config service permissions. They focus on configuring the recorder rather than provisioning the complete Config pipeline.
To keep things focused, common Config patterns are omitted, including:
- Delivery channel configuration (separate resource)
- Recorder activation (requires RecorderStatus resource)
- Global resource recording (includeGlobalResourceTypes)
- SNS notifications for configuration changes
These omissions are intentional: the goal is to illustrate how each recorder feature is wired, not provide drop-in compliance modules. See the Config Recorder resource reference for all available configuration options.
Let's configure AWS Config Recorders
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Getting Started & Common Issues
aws.cfg.RecorderStatus to start the recorder after creating it.aws.cfg.RecorderStatus to start recording. This separate resource exists because of the circular dependency between recorder and delivery channel.name property is immutable. Changing it will destroy and recreate the recorder resource.Configuration & Recording
recordingGroup with exclusionByResourceTypes containing the resource types to exclude (e.g., AWS::EC2::Instance), and set recordingStrategies.useOnly to EXCLUSION_BY_RESOURCE_TYPES.recordingModeOverride within recordingMode to specify different frequencies per resource type. For example, you can record most resources continuously while recording network interfaces daily.recordingFrequency to CONTINUOUS (default) or DAILY. Use recordingModeOverride to apply different frequencies to specific resource types.IAM & Permissions
config.amazonaws.com service principal to assume it.default if not specified.Using a different cloud?
Explore monitoring guides for other cloud providers: