Configure AWS Config Rules

The aws:cfg/rule:Rule resource, part of the Pulumi AWS provider, defines AWS Config rules that evaluate resource compliance against AWS managed rules, custom Lambda logic, or Guard policies. This guide focuses on three capabilities: AWS managed rule configuration, custom Lambda-based evaluation, and Guard policy language rules.

Config rules require an existing Configuration Recorder and IAM permissions. Custom rules also need Lambda functions or Guard policies. The examples are intentionally small. Combine them with your own recorder setup and compliance logic.

Evaluate resources with AWS managed rules

Most Config deployments start with AWS managed rules that check common compliance requirements without custom code.

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 rRole = new aws.iam.Role("r", {
    name: "my-awsconfig-role",
    assumeRolePolicy: assumeRole.then(assumeRole => assumeRole.json),
});
const foo = new aws.cfg.Recorder("foo", {
    name: "example",
    roleArn: rRole.arn,
});
const r = new aws.cfg.Rule("r", {
    name: "example",
    source: {
        owner: "AWS",
        sourceIdentifier: "S3_BUCKET_VERSIONING_ENABLED",
    },
}, {
    dependsOn: [foo],
});
const p = aws.iam.getPolicyDocument({
    statements: [{
        effect: "Allow",
        actions: ["config:Put*"],
        resources: ["*"],
    }],
});
const pRolePolicy = new aws.iam.RolePolicy("p", {
    name: "my-awsconfig-policy",
    role: rRole.id,
    policy: p.then(p => p.json),
});
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_role = aws.iam.Role("r",
    name="my-awsconfig-role",
    assume_role_policy=assume_role.json)
foo = aws.cfg.Recorder("foo",
    name="example",
    role_arn=r_role.arn)
r = aws.cfg.Rule("r",
    name="example",
    source={
        "owner": "AWS",
        "source_identifier": "S3_BUCKET_VERSIONING_ENABLED",
    },
    opts = pulumi.ResourceOptions(depends_on=[foo]))
p = aws.iam.get_policy_document(statements=[{
    "effect": "Allow",
    "actions": ["config:Put*"],
    "resources": ["*"],
}])
p_role_policy = aws.iam.RolePolicy("p",
    name="my-awsconfig-policy",
    role=r_role.id,
    policy=p.json)
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
		}
		rRole, err := iam.NewRole(ctx, "r", &iam.RoleArgs{
			Name:             pulumi.String("my-awsconfig-role"),
			AssumeRolePolicy: pulumi.String(assumeRole.Json),
		})
		if err != nil {
			return err
		}
		foo, err := cfg.NewRecorder(ctx, "foo", &cfg.RecorderArgs{
			Name:    pulumi.String("example"),
			RoleArn: rRole.Arn,
		})
		if err != nil {
			return err
		}
		_, err = cfg.NewRule(ctx, "r", &cfg.RuleArgs{
			Name: pulumi.String("example"),
			Source: &cfg.RuleSourceArgs{
				Owner:            pulumi.String("AWS"),
				SourceIdentifier: pulumi.String("S3_BUCKET_VERSIONING_ENABLED"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			foo,
		}))
		if err != nil {
			return err
		}
		p, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
			Statements: []iam.GetPolicyDocumentStatement{
				{
					Effect: pulumi.StringRef("Allow"),
					Actions: []string{
						"config:Put*",
					},
					Resources: []string{
						"*",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		_, err = iam.NewRolePolicy(ctx, "p", &iam.RolePolicyArgs{
			Name:   pulumi.String("my-awsconfig-policy"),
			Role:   rRole.ID(),
			Policy: pulumi.String(p.Json),
		})
		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 rRole = new Aws.Iam.Role("r", new()
    {
        Name = "my-awsconfig-role",
        AssumeRolePolicy = assumeRole.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
    });

    var foo = new Aws.Cfg.Recorder("foo", new()
    {
        Name = "example",
        RoleArn = rRole.Arn,
    });

    var r = new Aws.Cfg.Rule("r", new()
    {
        Name = "example",
        Source = new Aws.Cfg.Inputs.RuleSourceArgs
        {
            Owner = "AWS",
            SourceIdentifier = "S3_BUCKET_VERSIONING_ENABLED",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            foo,
        },
    });

    var p = Aws.Iam.GetPolicyDocument.Invoke(new()
    {
        Statements = new[]
        {
            new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
            {
                Effect = "Allow",
                Actions = new[]
                {
                    "config:Put*",
                },
                Resources = new[]
                {
                    "*",
                },
            },
        },
    });

    var pRolePolicy = new Aws.Iam.RolePolicy("p", new()
    {
        Name = "my-awsconfig-policy",
        Role = rRole.Id,
        Policy = p.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
    });

});
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 com.pulumi.aws.cfg.Rule;
import com.pulumi.aws.cfg.RuleArgs;
import com.pulumi.aws.cfg.inputs.RuleSourceArgs;
import com.pulumi.aws.iam.RolePolicy;
import com.pulumi.aws.iam.RolePolicyArgs;
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) {
        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 rRole = new Role("rRole", RoleArgs.builder()
            .name("my-awsconfig-role")
            .assumeRolePolicy(assumeRole.json())
            .build());

        var foo = new Recorder("foo", RecorderArgs.builder()
            .name("example")
            .roleArn(rRole.arn())
            .build());

        var r = new Rule("r", RuleArgs.builder()
            .name("example")
            .source(RuleSourceArgs.builder()
                .owner("AWS")
                .sourceIdentifier("S3_BUCKET_VERSIONING_ENABLED")
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(foo)
                .build());

        final var p = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
            .statements(GetPolicyDocumentStatementArgs.builder()
                .effect("Allow")
                .actions("config:Put*")
                .resources("*")
                .build())
            .build());

        var pRolePolicy = new RolePolicy("pRolePolicy", RolePolicyArgs.builder()
            .name("my-awsconfig-policy")
            .role(rRole.id())
            .policy(p.json())
            .build());

    }
}
resources:
  r:
    type: aws:cfg:Rule
    properties:
      name: example
      source:
        owner: AWS
        sourceIdentifier: S3_BUCKET_VERSIONING_ENABLED
    options:
      dependsOn:
        - ${foo}
  foo:
    type: aws:cfg:Recorder
    properties:
      name: example
      roleArn: ${rRole.arn}
  rRole:
    type: aws:iam:Role
    name: r
    properties:
      name: my-awsconfig-role
      assumeRolePolicy: ${assumeRole.json}
  pRolePolicy:
    type: aws:iam:RolePolicy
    name: p
    properties:
      name: my-awsconfig-policy
      role: ${rRole.id}
      policy: ${p.json}
variables:
  assumeRole:
    fn::invoke:
      function: aws:iam:getPolicyDocument
      arguments:
        statements:
          - effect: Allow
            principals:
              - type: Service
                identifiers:
                  - config.amazonaws.com
            actions:
              - sts:AssumeRole
  p:
    fn::invoke:
      function: aws:iam:getPolicyDocument
      arguments:
        statements:
          - effect: Allow
            actions:
              - config:Put*
            resources:
              - '*'

The source property defines the rule owner and identifier. Setting owner to “AWS” and sourceIdentifier to a managed rule name (like “S3_BUCKET_VERSIONING_ENABLED”) tells Config to use AWS-provided evaluation logic. The dependsOn relationship ensures the Configuration Recorder exists before creating the rule, avoiding race conditions during deployment.

Run custom compliance logic with Lambda functions

When AWS managed rules don’t cover your specific requirements, you can write custom evaluation logic in Lambda.

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

const example = new aws.cfg.Recorder("example", {});
const exampleFunction = new aws.lambda.Function("example", {});
const examplePermission = new aws.lambda.Permission("example", {
    action: "lambda:InvokeFunction",
    "function": exampleFunction.arn,
    principal: "config.amazonaws.com",
    statementId: "AllowExecutionFromConfig",
});
const exampleRule = new aws.cfg.Rule("example", {source: {
    owner: "CUSTOM_LAMBDA",
    sourceIdentifier: exampleFunction.arn,
}}, {
    dependsOn: [
        example,
        examplePermission,
    ],
});
import pulumi
import pulumi_aws as aws

example = aws.cfg.Recorder("example")
example_function = aws.lambda_.Function("example")
example_permission = aws.lambda_.Permission("example",
    action="lambda:InvokeFunction",
    function=example_function.arn,
    principal="config.amazonaws.com",
    statement_id="AllowExecutionFromConfig")
example_rule = aws.cfg.Rule("example", source={
    "owner": "CUSTOM_LAMBDA",
    "source_identifier": example_function.arn,
},
opts = pulumi.ResourceOptions(depends_on=[
        example,
        example_permission,
    ]))
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := cfg.NewRecorder(ctx, "example", nil)
		if err != nil {
			return err
		}
		exampleFunction, err := lambda.NewFunction(ctx, "example", nil)
		if err != nil {
			return err
		}
		examplePermission, err := lambda.NewPermission(ctx, "example", &lambda.PermissionArgs{
			Action:      pulumi.String("lambda:InvokeFunction"),
			Function:    exampleFunction.Arn,
			Principal:   pulumi.String("config.amazonaws.com"),
			StatementId: pulumi.String("AllowExecutionFromConfig"),
		})
		if err != nil {
			return err
		}
		_, err = cfg.NewRule(ctx, "example", &cfg.RuleArgs{
			Source: &cfg.RuleSourceArgs{
				Owner:            pulumi.String("CUSTOM_LAMBDA"),
				SourceIdentifier: exampleFunction.Arn,
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			example,
			examplePermission,
		}))
		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.Cfg.Recorder("example");

    var exampleFunction = new Aws.Lambda.Function("example");

    var examplePermission = new Aws.Lambda.Permission("example", new()
    {
        Action = "lambda:InvokeFunction",
        Function = exampleFunction.Arn,
        Principal = "config.amazonaws.com",
        StatementId = "AllowExecutionFromConfig",
    });

    var exampleRule = new Aws.Cfg.Rule("example", new()
    {
        Source = new Aws.Cfg.Inputs.RuleSourceArgs
        {
            Owner = "CUSTOM_LAMBDA",
            SourceIdentifier = exampleFunction.Arn,
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            example,
            examplePermission,
        },
    });

});
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.lambda.Function;
import com.pulumi.aws.lambda.Permission;
import com.pulumi.aws.lambda.PermissionArgs;
import com.pulumi.aws.cfg.Rule;
import com.pulumi.aws.cfg.RuleArgs;
import com.pulumi.aws.cfg.inputs.RuleSourceArgs;
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 Recorder("example");

        var exampleFunction = new Function("exampleFunction");

        var examplePermission = new Permission("examplePermission", PermissionArgs.builder()
            .action("lambda:InvokeFunction")
            .function(exampleFunction.arn())
            .principal("config.amazonaws.com")
            .statementId("AllowExecutionFromConfig")
            .build());

        var exampleRule = new Rule("exampleRule", RuleArgs.builder()
            .source(RuleSourceArgs.builder()
                .owner("CUSTOM_LAMBDA")
                .sourceIdentifier(exampleFunction.arn())
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(                
                    example,
                    examplePermission)
                .build());

    }
}
resources:
  example:
    type: aws:cfg:Recorder
  exampleFunction:
    type: aws:lambda:Function
    name: example
  examplePermission:
    type: aws:lambda:Permission
    name: example
    properties:
      action: lambda:InvokeFunction
      function: ${exampleFunction.arn}
      principal: config.amazonaws.com
      statementId: AllowExecutionFromConfig
  exampleRule:
    type: aws:cfg:Rule
    name: example
    properties:
      source:
        owner: CUSTOM_LAMBDA
        sourceIdentifier: ${exampleFunction.arn}
    options:
      dependsOn:
        - ${example}
        - ${examplePermission}

Setting owner to “CUSTOM_LAMBDA” and sourceIdentifier to your Lambda function ARN tells Config to invoke your function for evaluations. The lambda.Permission resource grants config.amazonaws.com permission to invoke the function. The dependsOn array ensures both the recorder and Lambda permissions exist before creating the rule.

Define compliance rules with Guard policy language

Guard policies let you write compliance rules declaratively without managing Lambda functions.

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

const example = new aws.cfg.Rule("example", {
    name: "example",
    source: {
        owner: "CUSTOM_POLICY",
        sourceDetails: [{
            messageType: "ConfigurationItemChangeNotification",
        }],
        customPolicyDetails: {
            policyRuntime: "guard-2.x.x",
            policyText: `\\t  rule tableisactive when
\\t\\t  resourceType == \\"AWS::DynamoDB::Table\\" {
\\t\\t  configuration.tableStatus == ['ACTIVE']
\\t  }
\\t  
\\t  rule checkcompliance when
\\t\\t  resourceType == \\"AWS::DynamoDB::Table\\"
\\t\\t  tableisactive {
\\t\\t\\t  supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus == \\"ENABLED\\"
\\t  }
`,
        },
    },
});
import pulumi
import pulumi_aws as aws

example = aws.cfg.Rule("example",
    name="example",
    source={
        "owner": "CUSTOM_POLICY",
        "source_details": [{
            "message_type": "ConfigurationItemChangeNotification",
        }],
        "custom_policy_details": {
            "policy_runtime": "guard-2.x.x",
            "policy_text": """\t  rule tableisactive when
\t\t  resourceType == \"AWS::DynamoDB::Table\" {
\t\t  configuration.tableStatus == ['ACTIVE']
\t  }
\t  
\t  rule checkcompliance when
\t\t  resourceType == \"AWS::DynamoDB::Table\"
\t\t  tableisactive {
\t\t\t  supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus == \"ENABLED\"
\t  }
""",
        },
    })
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.NewRule(ctx, "example", &cfg.RuleArgs{
			Name: pulumi.String("example"),
			Source: &cfg.RuleSourceArgs{
				Owner: pulumi.String("CUSTOM_POLICY"),
				SourceDetails: cfg.RuleSourceSourceDetailArray{
					&cfg.RuleSourceSourceDetailArgs{
						MessageType: pulumi.String("ConfigurationItemChangeNotification"),
					},
				},
				CustomPolicyDetails: &cfg.RuleSourceCustomPolicyDetailsArgs{
					PolicyRuntime: pulumi.String("guard-2.x.x"),
					PolicyText: pulumi.String(`\t  rule tableisactive when
\t\t  resourceType == \"AWS::DynamoDB::Table\" {
\t\t  configuration.tableStatus == ['ACTIVE']
\t  }
\t  
\t  rule checkcompliance when
\t\t  resourceType == \"AWS::DynamoDB::Table\"
\t\t  tableisactive {
\t\t\t  supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus == \"ENABLED\"
\t  }
`),
				},
			},
		})
		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.Cfg.Rule("example", new()
    {
        Name = "example",
        Source = new Aws.Cfg.Inputs.RuleSourceArgs
        {
            Owner = "CUSTOM_POLICY",
            SourceDetails = new[]
            {
                new Aws.Cfg.Inputs.RuleSourceSourceDetailArgs
                {
                    MessageType = "ConfigurationItemChangeNotification",
                },
            },
            CustomPolicyDetails = new Aws.Cfg.Inputs.RuleSourceCustomPolicyDetailsArgs
            {
                PolicyRuntime = "guard-2.x.x",
                PolicyText = @"\t  rule tableisactive when
\t\t  resourceType == \""AWS::DynamoDB::Table\"" {
\t\t  configuration.tableStatus == ['ACTIVE']
\t  }
\t  
\t  rule checkcompliance when
\t\t  resourceType == \""AWS::DynamoDB::Table\""
\t\t  tableisactive {
\t\t\t  supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus == \""ENABLED\""
\t  }
",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cfg.Rule;
import com.pulumi.aws.cfg.RuleArgs;
import com.pulumi.aws.cfg.inputs.RuleSourceArgs;
import com.pulumi.aws.cfg.inputs.RuleSourceCustomPolicyDetailsArgs;
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 Rule("example", RuleArgs.builder()
            .name("example")
            .source(RuleSourceArgs.builder()
                .owner("CUSTOM_POLICY")
                .sourceDetails(RuleSourceSourceDetailArgs.builder()
                    .messageType("ConfigurationItemChangeNotification")
                    .build())
                .customPolicyDetails(RuleSourceCustomPolicyDetailsArgs.builder()
                    .policyRuntime("guard-2.x.x")
                    .policyText("""
\t  rule tableisactive when
\t\t  resourceType == \"AWS::DynamoDB::Table\" {
\t\t  configuration.tableStatus == ['ACTIVE']
\t  }
\t  
\t  rule checkcompliance when
\t\t  resourceType == \"AWS::DynamoDB::Table\"
\t\t  tableisactive {
\t\t\t  supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus == \"ENABLED\"
\t  }
                    """)
                    .build())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:cfg:Rule
    properties:
      name: example
      source:
        owner: CUSTOM_POLICY
        sourceDetails:
          - messageType: ConfigurationItemChangeNotification
        customPolicyDetails:
          policyRuntime: guard-2.x.x
          policyText: "\\t  rule tableisactive when\n\\t\\t  resourceType == \\\"AWS::DynamoDB::Table\\\" {\n\\t\\t  configuration.tableStatus == ['ACTIVE']\n\\t  }\n\\t  \n\\t  rule checkcompliance when\n\\t\\t  resourceType == \\\"AWS::DynamoDB::Table\\\"\n\\t\\t  tableisactive {\n\\t\\t\\t  supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus == \\\"ENABLED\\\"\n\\t  }\n"

The CUSTOM_POLICY owner type uses Guard policy language for evaluation. The customPolicyDetails property specifies the policy runtime (guard-2.x.x) and the policy text itself. The sourceDetails property defines when Config triggers evaluation, in this case on configuration changes. Guard policies are useful for straightforward resource property checks that don’t require complex logic.

Beyond these examples

These snippets focus on specific Config rule features: AWS managed rules and custom Lambda evaluation, and Guard policy language for declarative checks. They’re intentionally minimal rather than full compliance frameworks.

The examples rely on pre-existing infrastructure such as Configuration Recorder (required for all rules), IAM roles with config:Put* permissions, and Lambda functions for custom rule logic. They focus on rule configuration rather than provisioning the surrounding infrastructure.

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

  • Resource scope filtering (scope property)
  • Input parameters for rule configuration (inputParameters)
  • Evaluation frequency controls (maximumExecutionFrequency)
  • Evaluation modes for proactive vs detective checks

These omissions are intentional: the goal is to illustrate how each rule type is wired, not provide drop-in compliance modules. See the Config Rule resource reference for all available configuration options.

Let's configure AWS Config Rules

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Setup & Dependencies
Why am I getting race condition errors when creating a Config rule?
Config rules require an existing Configuration Recorder. Use dependsOn to ensure the Recorder is created before the Rule to avoid race conditions.
What resources must exist before creating a Config rule?
You need an aws.cfg.Recorder resource. For custom Lambda rules, you also need the Lambda function and an aws.lambda.Permission allowing config.amazonaws.com to invoke it.
Rule Types & Configuration
What types of Config rules can I create?
You can create three types: AWS managed rules (owner: "AWS"), custom Lambda rules (owner: "CUSTOM_LAMBDA"), and custom policy rules (owner: "CUSTOM_POLICY").
How do I use an AWS managed Config rule?
Set source.owner to "AWS" and source.sourceIdentifier to the managed rule name, such as "S3_BUCKET_VERSIONING_ENABLED". Find available rules in the AWS Config Developer Guide.
How do I create a custom Lambda-based Config rule?
Set source.owner to "CUSTOM_LAMBDA" and source.sourceIdentifier to your Lambda function’s ARN. You must also create an aws.lambda.Permission with principal: "config.amazonaws.com" and action: "lambda:InvokeFunction".
How do I create a custom policy-based Config rule?
Set source.owner to "CUSTOM_POLICY" and provide customPolicyDetails with policyRuntime (e.g., "guard-2.x.x") and policyText containing your Guard policy.
What's the difference between sourceIdentifier for AWS and custom rules?
For AWS managed rules, sourceIdentifier is the rule name (like "S3_BUCKET_VERSIONING_ENABLED"). For custom Lambda rules, it’s the Lambda function ARN.
Configuration & Scope
How do I pass parameters to my custom Lambda Config rule?
Use the inputParameters property with a JSON string that will be passed to your Lambda function.
How do I control which resources trigger my Config rule?
Configure the scope property to define which resources can trigger evaluation. Without it, the rule evaluates based on the source configuration.
What does the evaluationModes property control?
The evaluationModes property specifies the modes in which the Config rule can be evaluated. This is a required property.
Limitations & Permissions
Can I rename a Config rule after creation?
No, the name property is immutable. Changing it requires destroying and recreating the rule.
Why is my custom Lambda rule failing to invoke the function?
AWS Config needs explicit permission to invoke your Lambda. Create an aws.lambda.Permission resource with principal: "config.amazonaws.com" and include it in your rule’s dependsOn.

Using a different cloud?

Explore monitoring guides for other cloud providers: