The aws:accessanalyzer/analyzer:Analyzer resource, part of the Pulumi AWS provider, defines an Access Analyzer that continuously scans AWS resources to identify external access or unused permissions. This guide focuses on three capabilities: account vs organization scope, unused access tracking, and analysis rules for filtering.
Organization-level analyzers require AWS Organizations to be enabled and the Access Analyzer service to be registered as a trusted service. The examples are intentionally small. Combine them with your own archive rules and EventBridge integrations for complete monitoring workflows.
Analyze access within a single AWS account
Most teams start by scanning resources in a single account to identify external access grants.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.accessanalyzer.Analyzer("example", {analyzerName: "example"});
import pulumi
import pulumi_aws as aws
example = aws.accessanalyzer.Analyzer("example", analyzer_name="example")
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/accessanalyzer"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := accessanalyzer.NewAnalyzer(ctx, "example", &accessanalyzer.AnalyzerArgs{
AnalyzerName: pulumi.String("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.AccessAnalyzer.Analyzer("example", new()
{
AnalyzerName = "example",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.accessanalyzer.Analyzer;
import com.pulumi.aws.accessanalyzer.AnalyzerArgs;
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 Analyzer("example", AnalyzerArgs.builder()
.analyzerName("example")
.build());
}
}
resources:
example:
type: aws:accessanalyzer:Analyzer
properties:
analyzerName: example
The analyzerName property sets a unique identifier for the analyzer. Without specifying the type property, the analyzer defaults to ACCOUNT scope, which scans only resources in the current account for external access.
Analyze access across an AWS Organization
Organizations with multiple accounts need centralized visibility from a single analyzer.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.organizations.Organization("example", {awsServiceAccessPrincipals: ["access-analyzer.amazonaws.com"]});
const exampleAnalyzer = new aws.accessanalyzer.Analyzer("example", {
analyzerName: "example",
type: "ORGANIZATION",
}, {
dependsOn: [example],
});
import pulumi
import pulumi_aws as aws
example = aws.organizations.Organization("example", aws_service_access_principals=["access-analyzer.amazonaws.com"])
example_analyzer = aws.accessanalyzer.Analyzer("example",
analyzer_name="example",
type="ORGANIZATION",
opts = pulumi.ResourceOptions(depends_on=[example]))
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/accessanalyzer"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/organizations"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
example, err := organizations.NewOrganization(ctx, "example", &organizations.OrganizationArgs{
AwsServiceAccessPrincipals: pulumi.StringArray{
pulumi.String("access-analyzer.amazonaws.com"),
},
})
if err != nil {
return err
}
_, err = accessanalyzer.NewAnalyzer(ctx, "example", &accessanalyzer.AnalyzerArgs{
AnalyzerName: pulumi.String("example"),
Type: pulumi.String("ORGANIZATION"),
}, 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.Organizations.Organization("example", new()
{
AwsServiceAccessPrincipals = new[]
{
"access-analyzer.amazonaws.com",
},
});
var exampleAnalyzer = new Aws.AccessAnalyzer.Analyzer("example", new()
{
AnalyzerName = "example",
Type = "ORGANIZATION",
}, new CustomResourceOptions
{
DependsOn =
{
example,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.organizations.Organization;
import com.pulumi.aws.organizations.OrganizationArgs;
import com.pulumi.aws.accessanalyzer.Analyzer;
import com.pulumi.aws.accessanalyzer.AnalyzerArgs;
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 Organization("example", OrganizationArgs.builder()
.awsServiceAccessPrincipals("access-analyzer.amazonaws.com")
.build());
var exampleAnalyzer = new Analyzer("exampleAnalyzer", AnalyzerArgs.builder()
.analyzerName("example")
.type("ORGANIZATION")
.build(), CustomResourceOptions.builder()
.dependsOn(example)
.build());
}
}
resources:
example:
type: aws:organizations:Organization
properties:
awsServiceAccessPrincipals:
- access-analyzer.amazonaws.com
exampleAnalyzer:
type: aws:accessanalyzer:Analyzer
name: example
properties:
analyzerName: example
type: ORGANIZATION
options:
dependsOn:
- ${example}
Setting type to ORGANIZATION enables the analyzer to scan all member accounts. The awsServiceAccessPrincipals property on the Organization resource grants Access Analyzer permission to read resources across accounts. The dependsOn relationship ensures the service access is configured before creating the analyzer.
Track unused permissions with exclusion rules
Security teams often need to identify IAM permissions that haven’t been used recently, while excluding specific accounts or resources.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.accessanalyzer.Analyzer("example", {
analyzerName: "example",
type: "ORGANIZATION_UNUSED_ACCESS",
configuration: {
unusedAccess: {
unusedAccessAge: 180,
analysisRule: {
exclusions: [
{
accountIds: [
"123456789012",
"234567890123",
],
},
{
resourceTags: [
{
key1: "value1",
},
{
key2: "value2",
},
],
},
],
},
},
},
});
import pulumi
import pulumi_aws as aws
example = aws.accessanalyzer.Analyzer("example",
analyzer_name="example",
type="ORGANIZATION_UNUSED_ACCESS",
configuration={
"unused_access": {
"unused_access_age": 180,
"analysis_rule": {
"exclusions": [
{
"account_ids": [
"123456789012",
"234567890123",
],
},
{
"resource_tags": [
{
"key1": "value1",
},
{
"key2": "value2",
},
],
},
],
},
},
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/accessanalyzer"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := accessanalyzer.NewAnalyzer(ctx, "example", &accessanalyzer.AnalyzerArgs{
AnalyzerName: pulumi.String("example"),
Type: pulumi.String("ORGANIZATION_UNUSED_ACCESS"),
Configuration: &accessanalyzer.AnalyzerConfigurationArgs{
UnusedAccess: &accessanalyzer.AnalyzerConfigurationUnusedAccessArgs{
UnusedAccessAge: pulumi.Int(180),
AnalysisRule: &accessanalyzer.AnalyzerConfigurationUnusedAccessAnalysisRuleArgs{
Exclusions: accessanalyzer.AnalyzerConfigurationUnusedAccessAnalysisRuleExclusionArray{
&accessanalyzer.AnalyzerConfigurationUnusedAccessAnalysisRuleExclusionArgs{
AccountIds: pulumi.StringArray{
pulumi.String("123456789012"),
pulumi.String("234567890123"),
},
},
&accessanalyzer.AnalyzerConfigurationUnusedAccessAnalysisRuleExclusionArgs{
ResourceTags: pulumi.StringMapArray{
pulumi.StringMap{
"key1": pulumi.String("value1"),
},
pulumi.StringMap{
"key2": pulumi.String("value2"),
},
},
},
},
},
},
},
})
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.AccessAnalyzer.Analyzer("example", new()
{
AnalyzerName = "example",
Type = "ORGANIZATION_UNUSED_ACCESS",
Configuration = new Aws.AccessAnalyzer.Inputs.AnalyzerConfigurationArgs
{
UnusedAccess = new Aws.AccessAnalyzer.Inputs.AnalyzerConfigurationUnusedAccessArgs
{
UnusedAccessAge = 180,
AnalysisRule = new Aws.AccessAnalyzer.Inputs.AnalyzerConfigurationUnusedAccessAnalysisRuleArgs
{
Exclusions = new[]
{
new Aws.AccessAnalyzer.Inputs.AnalyzerConfigurationUnusedAccessAnalysisRuleExclusionArgs
{
AccountIds = new[]
{
"123456789012",
"234567890123",
},
},
new Aws.AccessAnalyzer.Inputs.AnalyzerConfigurationUnusedAccessAnalysisRuleExclusionArgs
{
ResourceTags = new[]
{
{
{ "key1", "value1" },
},
{
{ "key2", "value2" },
},
},
},
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.accessanalyzer.Analyzer;
import com.pulumi.aws.accessanalyzer.AnalyzerArgs;
import com.pulumi.aws.accessanalyzer.inputs.AnalyzerConfigurationArgs;
import com.pulumi.aws.accessanalyzer.inputs.AnalyzerConfigurationUnusedAccessArgs;
import com.pulumi.aws.accessanalyzer.inputs.AnalyzerConfigurationUnusedAccessAnalysisRuleArgs;
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 Analyzer("example", AnalyzerArgs.builder()
.analyzerName("example")
.type("ORGANIZATION_UNUSED_ACCESS")
.configuration(AnalyzerConfigurationArgs.builder()
.unusedAccess(AnalyzerConfigurationUnusedAccessArgs.builder()
.unusedAccessAge(180)
.analysisRule(AnalyzerConfigurationUnusedAccessAnalysisRuleArgs.builder()
.exclusions(
AnalyzerConfigurationUnusedAccessAnalysisRuleExclusionArgs.builder()
.accountIds(
"123456789012",
"234567890123")
.build(),
AnalyzerConfigurationUnusedAccessAnalysisRuleExclusionArgs.builder()
.resourceTags(
Map.of("key1", "value1"),
Map.of("key2", "value2"))
.build())
.build())
.build())
.build())
.build());
}
}
resources:
example:
type: aws:accessanalyzer:Analyzer
properties:
analyzerName: example
type: ORGANIZATION_UNUSED_ACCESS
configuration:
unusedAccess:
unusedAccessAge: 180
analysisRule:
exclusions:
- accountIds:
- '123456789012'
- '234567890123'
- resourceTags:
- key1: value1
- key2: value2
The ORGANIZATION_UNUSED_ACCESS type tracks permissions that haven’t been exercised within the unusedAccessAge threshold (180 days in this example). The analysisRule property defines exclusions: the first exclusion skips specific account IDs, while the second excludes resources with particular tags. This reduces noise by filtering out known exceptions.
Scope analysis to specific resource types
When focusing on specific services, you can limit analysis to particular resource types.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const test = new aws.accessanalyzer.Analyzer("test", {
analyzerName: "example",
type: "ORGANIZATION_INTERNAL_ACCESS",
configuration: {
internalAccess: {
analysisRule: {
inclusions: [{
resourceTypes: [
"AWS::S3::Bucket",
"AWS::RDS::DBSnapshot",
"AWS::DynamoDB::Table",
],
}],
},
},
},
});
import pulumi
import pulumi_aws as aws
test = aws.accessanalyzer.Analyzer("test",
analyzer_name="example",
type="ORGANIZATION_INTERNAL_ACCESS",
configuration={
"internal_access": {
"analysis_rule": {
"inclusions": [{
"resource_types": [
"AWS::S3::Bucket",
"AWS::RDS::DBSnapshot",
"AWS::DynamoDB::Table",
],
}],
},
},
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/accessanalyzer"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := accessanalyzer.NewAnalyzer(ctx, "test", &accessanalyzer.AnalyzerArgs{
AnalyzerName: pulumi.String("example"),
Type: pulumi.String("ORGANIZATION_INTERNAL_ACCESS"),
Configuration: &accessanalyzer.AnalyzerConfigurationArgs{
InternalAccess: &accessanalyzer.AnalyzerConfigurationInternalAccessArgs{
AnalysisRule: &accessanalyzer.AnalyzerConfigurationInternalAccessAnalysisRuleArgs{
Inclusions: accessanalyzer.AnalyzerConfigurationInternalAccessAnalysisRuleInclusionArray{
&accessanalyzer.AnalyzerConfigurationInternalAccessAnalysisRuleInclusionArgs{
ResourceTypes: pulumi.StringArray{
pulumi.String("AWS::S3::Bucket"),
pulumi.String("AWS::RDS::DBSnapshot"),
pulumi.String("AWS::DynamoDB::Table"),
},
},
},
},
},
},
})
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 test = new Aws.AccessAnalyzer.Analyzer("test", new()
{
AnalyzerName = "example",
Type = "ORGANIZATION_INTERNAL_ACCESS",
Configuration = new Aws.AccessAnalyzer.Inputs.AnalyzerConfigurationArgs
{
InternalAccess = new Aws.AccessAnalyzer.Inputs.AnalyzerConfigurationInternalAccessArgs
{
AnalysisRule = new Aws.AccessAnalyzer.Inputs.AnalyzerConfigurationInternalAccessAnalysisRuleArgs
{
Inclusions = new[]
{
new Aws.AccessAnalyzer.Inputs.AnalyzerConfigurationInternalAccessAnalysisRuleInclusionArgs
{
ResourceTypes = new[]
{
"AWS::S3::Bucket",
"AWS::RDS::DBSnapshot",
"AWS::DynamoDB::Table",
},
},
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.accessanalyzer.Analyzer;
import com.pulumi.aws.accessanalyzer.AnalyzerArgs;
import com.pulumi.aws.accessanalyzer.inputs.AnalyzerConfigurationArgs;
import com.pulumi.aws.accessanalyzer.inputs.AnalyzerConfigurationInternalAccessArgs;
import com.pulumi.aws.accessanalyzer.inputs.AnalyzerConfigurationInternalAccessAnalysisRuleArgs;
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 test = new Analyzer("test", AnalyzerArgs.builder()
.analyzerName("example")
.type("ORGANIZATION_INTERNAL_ACCESS")
.configuration(AnalyzerConfigurationArgs.builder()
.internalAccess(AnalyzerConfigurationInternalAccessArgs.builder()
.analysisRule(AnalyzerConfigurationInternalAccessAnalysisRuleArgs.builder()
.inclusions(AnalyzerConfigurationInternalAccessAnalysisRuleInclusionArgs.builder()
.resourceTypes(
"AWS::S3::Bucket",
"AWS::RDS::DBSnapshot",
"AWS::DynamoDB::Table")
.build())
.build())
.build())
.build())
.build());
}
}
resources:
test:
type: aws:accessanalyzer:Analyzer
properties:
analyzerName: example
type: ORGANIZATION_INTERNAL_ACCESS
configuration:
internalAccess:
analysisRule:
inclusions:
- resourceTypes:
- AWS::S3::Bucket
- AWS::RDS::DBSnapshot
- AWS::DynamoDB::Table
The ORGANIZATION_INTERNAL_ACCESS type analyzes access patterns within the organization boundary. The inclusions property with resourceTypes restricts scanning to S3 buckets, RDS snapshots, and DynamoDB tables, ignoring other resource types. This focuses findings on high-value data stores.
Beyond these examples
These snippets focus on specific analyzer-level features: account and organization-level analysis, unused access tracking with age thresholds, and analysis rules for inclusions and exclusions. They’re intentionally minimal rather than full security monitoring solutions.
The examples may reference pre-existing infrastructure such as AWS Organizations (for organization-level analyzers) and IAM service-linked roles for Access Analyzer. They focus on configuring the analyzer rather than provisioning the surrounding security infrastructure.
To keep things focused, common analyzer patterns are omitted, including:
- Archive rules for automated finding suppression
- Tags for resource organization (tags property)
- Cross-region analyzer configuration
- Integration with EventBridge for finding notifications
These omissions are intentional: the goal is to illustrate how each analyzer feature is wired, not provide drop-in security modules. See the Access Analyzer Analyzer resource reference for all available configuration options.
Let's configure AWS Access Analyzer
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Analyzer Types & Scope
ACCOUNT (default), ORGANIZATION, ACCOUNT_INTERNAL_ACCESS, ORGANIZATION_INTERNAL_ACCESS, ACCOUNT_UNUSED_ACCESS, and ORGANIZATION_UNUSED_ACCESS. Account types analyze a single account, while organization types analyze all accounts in your AWS Organization.unusedAccessAge).Organization Setup
type to ORGANIZATION (or ORGANIZATION_INTERNAL_ACCESS/ORGANIZATION_UNUSED_ACCESS) and ensure AWS Organizations has access-analyzer.amazonaws.com as a service access principal. Use dependsOn to reference your organization resource.Configuration & Filtering
configuration block. For internal access analyzers, use inclusions with resourceTypes, accountIds, or resourceArns. For unused access analyzers, use exclusions with accountIds or resourceTags.configuration.unusedAccess.analysisRule.exclusions with an accountIds array listing the account IDs to exclude.configuration.internalAccess.analysisRule.inclusions with a resourceTypes array containing AWS resource types like AWS::S3::Bucket, AWS::RDS::DBSnapshot, or AWS::DynamoDB::Table.unusedAccessAge specifies the number of days to consider when identifying unused permissions (e.g., 180 days).Resource Management
analyzerName, type, and configuration properties are immutable. Changing any of these will force replacement of the analyzer.region property defaults to the region configured in your provider unless explicitly specified.