Skip to main content
Pulumi logo Pulumi logo
  1. Docs
  2. Reference
  3. Pre-built Policy Packs
  4. Pulumi Best Practices
  5. AWS

Pulumi Best Practices - AWS

    This page lists all 55 policies in the Pulumi Best Practices pack for AWS, as published in pulumi-best-practices-aws version 1.4.1.

    Policies by control

    1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.

    2. Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.

    2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.

    3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.

    4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.

    5. Tagging — Enforce standardized resource tags for ownership, environment, and compliance tracking.

    6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.

    7. High Availability — Deploy resources in redundant, fault-tolerant configurations to ensure service continuity.

    8. Require DLQ — Ensure all asynchronous messaging systems are configured with a dead-letter queue to handle failures.

    9. Resource Availability — Define and enforce timeouts, quotas, and capacity limits to prevent resource exhaustion.

    11. Networking — Only allow required inbound and outbound traffic through network security groups, firewalls, or ACLs.

    12. Documentation — Maintain up-to-date documentation of architectures, configurations, policies, and procedures to ensure clarity, consistency, and auditability.

    13. Data Backup and Recovery — Regularly back up critical data and systems, store backups securely, and test recovery procedures to ensure timely restoration after failures or disasters.

    14. Key Management & Rotation — Manage encryption keys securely and enforce periodic key rotation to reduce the risk of compromise.

    Policy details

    api-gateway-access-logging

    Severity: medium · Enforcement: advisory

    Ensures API Gateway stages have access logging enabled

    • 6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
    Remediation
    Fix: Enable Access Logging for API Gateway Stage

    Configure the accessLogSettings property with a CloudWatch Log Group ARN to enable comprehensive audit logging:

    const logGroup = new aws.cloudwatch.LogGroup("api-logs", {
        retentionInDays: 30,
    });
    
    const stage = new aws.apigateway.Stage("my-stage", {
        restApi: api.id,
        stageName: "prod",
        deployment: deployment.id,
        accessLogSettings: { // Enable access logging
            destinationArn: logGroup.arn, // Specify CloudWatch Log Group
            format: "$context.requestId",
        },
    });
    

    api-gateway-ssl-certificate-required

    Severity: high · Enforcement: advisory

    Ensures API Gateway REST API stages have client certificates configured for SSL/TLS authentication to protect data in transit.

    • 3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.
    Remediation
    Fix: Configure Client Certificate for API Gateway Stage

    First, create a client certificate:

    import * as aws from "@pulumi/aws";
    
    // Create a client certificate for SSL/TLS authentication
    const clientCert = new aws.apigateway.ClientCertificate("api-client-cert", {
        description: "Client certificate for API Gateway SSL authentication",
    });
    

    Then, associate the certificate with your API Gateway stage:

    const stage = new aws.apigateway.Stage("api-stage", {
        restApi: restApi.id,
        deployment: deployment.id,
        stageName: "production",
        clientCertificateId: clientCert.id, // Add this property to enable SSL certificate validation
    });
    

    api-gateway-v2-access-logging

    Severity: medium · Enforcement: advisory

    Ensures API Gateway V2 stages have access logging enabled

    • 6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
    Remediation
    Fix: Enable Access Logging for API Gateway V2 Stage

    Configure the accessLogSettings property with a CloudWatch Log Group ARN to enable comprehensive audit logging:

    const logGroup = new aws.cloudwatch.LogGroup("api-v2-logs", {
        retentionInDays: 30,
    });
    
    const stage = new aws.apigatewayv2.Stage("my-stage", {
        apiId: api.id,
        name: "prod",
        accessLogSettings: { // Enable access logging
            destinationArn: logGroup.arn, // Specify CloudWatch Log Group
            format: "$context.requestId",
        },
    });
    

    cloudtrail-enabled

    Severity: critical · Enforcement: advisory

    Ensures CloudTrail is enabled with at least one active trail for audit logging.

    • 6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
    Remediation
    Fix: Enable CloudTrail for audit logging

    Create a CloudTrail trail to capture API activity and management events:

    import * as aws from "@pulumi/aws";
    
    // Create an S3 bucket for CloudTrail logs
    const trailBucket = new aws.s3.Bucket("cloudtrail-logs", {
        forceDestroy: true, // Only for demo - remove in production
    });
    
    // Create CloudTrail trail
    const trail = new aws.cloudtrail.Trail("main-trail", {
        s3BucketName: trailBucket.bucket,
        isMultiRegionTrail: true, // Enable for all regions
        includeGlobalServiceEvents: true,
        enableLogFileValidation: true, // Ensure log integrity
    });
    

    Key configuration:

    • Set isMultiRegionTrail: true to capture events across all AWS regions
    • Enable includeGlobalServiceEvents to capture IAM and STS events
    • Use enableLogFileValidation to ensure log integrity

    cloudwatch-log-retention

    Severity: medium · Enforcement: advisory

    Ensures CloudWatch log groups have appropriate retention periods for compliance.

    • 6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
    Remediation
    Fix: Configure CloudWatch Log Group Retention Period

    Set the retentionInDays property to at least 365 days to meet audit record retention requirements.

    import * as aws from "@pulumi/aws";
    
    const logGroup = new aws.cloudwatch.LogGroup("my-log-group", {
        name: "/aws/lambda/my-function",
        retentionInDays: 365, // Set retention to at least 365 days for compliance
    });
    

    dms-no-public-access

    Severity: high · Enforcement: advisory

    Ensures DMS replication instances are not publicly accessible to maintain security.

    • 4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
    Remediation
    Fix: Disable Public Accessibility
    const replicationInstance = new aws.dms.ReplicationInstance("my-replication-instance", {
        replicationInstanceClass: "dms.t3.micro",
        publiclyAccessible: false,  // Set to false to prevent public access
        // ... other config
    });
    

    dynamodb-auto-scaling-enabled

    Severity: medium · Enforcement: advisory

    Ensures DynamoDB tables have auto-scaling or on-demand mode enabled for capacity management.

    • 9. Resource Availability — Define and enforce timeouts, quotas, and capacity limits to prevent resource exhaustion.
    Remediation
    Fix: Enable DynamoDB Auto-Scaling or On-Demand Mode

    Configure auto-scaling targets and policies for your DynamoDB table’s read and write capacity, or switch to on-demand billing mode:

    import * as aws from "@pulumi/aws";
    
    const table = new aws.dynamodb.Table("myTable", {
        name: "my-table",
        billingMode: "PROVISIONED", // Use provisioned mode with auto-scaling
        readCapacity: 5,
        writeCapacity: 5,
        attributes: [{ name: "id", type: "S" }],
        hashKey: "id",
    });
    
    // Configure read capacity auto-scaling
    const readTarget = new aws.appautoscaling.Target("readTarget", {
        resourceId: pulumi.interpolate`table/${table.name}`,
        scalableDimension: "dynamodb:table:ReadCapacityUnits",
        serviceNamespace: "dynamodb",
        minCapacity: 5,
        maxCapacity: 100, // Set appropriate scaling limits
    });
    
    new aws.appautoscaling.Policy("readPolicy", {
        resourceId: readTarget.resourceId,
        scalableDimension: readTarget.scalableDimension,
        serviceNamespace: readTarget.serviceNamespace,
        policyType: "TargetTrackingScaling",
        targetTrackingScalingPolicyConfiguration: {
            targetValue: 70.0, // Target utilization percentage
            predefinedMetricSpecification: {
                predefinedMetricType: "DynamoDBReadCapacityUtilization",
            },
        },
    });
    
    // Configure write capacity auto-scaling
    const writeTarget = new aws.appautoscaling.Target("writeTarget", {
        resourceId: pulumi.interpolate`table/${table.name}`,
        scalableDimension: "dynamodb:table:WriteCapacityUnits",
        serviceNamespace: "dynamodb",
        minCapacity: 5,
        maxCapacity: 100,
    });
    
    new aws.appautoscaling.Policy("writePolicy", {
        resourceId: writeTarget.resourceId,
        scalableDimension: writeTarget.scalableDimension,
        serviceNamespace: writeTarget.serviceNamespace,
        policyType: "TargetTrackingScaling",
        targetTrackingScalingPolicyConfiguration: {
            targetValue: 70.0,
            predefinedMetricSpecification: {
                predefinedMetricType: "DynamoDBWriteCapacityUtilization",
            },
        },
    });
    
    // Alternative: Use on-demand billing mode (no auto-scaling needed)
    // const table = new aws.dynamodb.Table("myTable", {
    //     billingMode: "PAY_PER_REQUEST", // On-demand mode handles scaling automatically
    //     attributes: [{ name: "id", type: "S" }],
    //     hashKey: "id",
    // });
    

    dynamodb-kms-encryption-enabled

    Severity: high · Enforcement: advisory

    Ensures DynamoDB tables have encryption enabled using KMS keys.

    • 2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
    Remediation
    Fix: Enable Customer-Managed KMS Encryption for DynamoDB Table

    Configure the DynamoDB table with a customer-managed KMS key for encryption at rest:

    import * as aws from "@pulumi/aws";
    
    // Create or reference a customer-managed KMS key
    const kmsKey = new aws.kms.Key("table-encryption-key", {
        description: "KMS key for DynamoDB table encryption",
        deletionWindowInDays: 10,
    });
    
    const table = new aws.dynamodb.Table("my-table", {
        // ... other configuration ...
        serverSideEncryption: {
            enabled: true,  // Enable encryption
            kmsKeyArn: kmsKey.arn,  // Use customer-managed KMS key (not AWS-managed)
        },
    });
    

    dynamodb-streams-enabled

    Severity: medium · Enforcement: advisory

    Enforces that all DynamoDB tables have Stream settings enabled to capture all changes

    • 9. Resource Availability — Define and enforce timeouts, quotas, and capacity limits to prevent resource exhaustion.
    Remediation
    Fix: Enable DynamoDB Streams

    Set the streamEnabled property to true on your DynamoDB table:

    const table = new aws.dynamodb.Table("my-table", {
        name: "my-table",
        attributes: [
            { name: "id", type: "S" },
        ],
        hashKey: "id",
        streamEnabled: true, // Enable DynamoDB Streams
        billingMode: "PAY_PER_REQUEST",
    });
    

    ebs-volume-encryption-required

    Severity: high · Enforcement: advisory

    Checks that EBS volumes are encrypted.

    • 2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
    Remediation
    Fix: Enable EBS Volume Encryption

    Set the encrypted property to true on your EBS volume resource.

    import * as aws from "@pulumi/aws";
    
    const volume = new aws.ebs.Volume("my-volume", {
        availabilityZone: "us-west-2a",
        size: 100,
        encrypted: true, // Enable encryption
    });
    

    ec2-instance-disallow-public-ip

    Severity: high · Enforcement: advisory

    Checks that EC2 instances do not have a public IP address.

    • 4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
    Remediation
    Fix: Disable Public IP Assignment
    const instance = new aws.ec2.Instance("app-server", {
        ami: "ami-12345678",
        instanceType: "t3.medium",
        associatePublicIpAddress: false,  // Disable public IP to prevent direct internet access
        subnetId: privateSubnet.id,
    });
    

    ec2-instance-disallow-unencrypted-block-device

    Severity: high · Enforcement: advisory

    Checks that EC2 instances do not have unencrypted block devices.

    • 2. Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
    Remediation
    Fix: Enable Encryption for EBS Block Devices

    Set the encrypted property to true for all EBS block devices attached to EC2 instances:

    const instance = new aws.ec2.Instance("my-instance", {
        ami: "ami-12345678",
        instanceType: "t3.micro",
        ebsBlockDevices: [{
            deviceName: "/dev/sdf",
            volumeSize: 20,
            encrypted: true, // Enable encryption for the EBS volume
        }],
    });
    

    ec2-instance-disallow-unencrypted-root-block-device

    Severity: high · Enforcement: advisory

    Checks that EC2 instances does not have unencrypted root volumes.

    • 2. Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
    Remediation
    Fix: Enable Encryption for Root Block Device

    Set the encrypted property to true in the rootBlockDevice configuration:

    const instance = new aws.ec2.Instance("my-instance", {
        instanceType: "t3.micro",
        ami: "ami-12345678",
        rootBlockDevice: {
            encrypted: true, // Enable encryption for root volume
        },
    });
    

    ec2-launch-configuration-disallow-unencrypted-block-device

    Severity: high · Enforcement: advisory

    Checks that EC2 Launch Configurations do not have unencrypted block devices.

    • 2. Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
    Remediation
    Fix: Enable EBS Block Device Encryption

    Set the encrypted property to true for all EBS block devices in the Launch Configuration:

    const launchConfig = new aws.ec2.LaunchConfiguration("my-launch-config", {
        imageId: "ami-12345678",
        instanceType: "t3.medium",
        ebsBlockDevices: [{
            deviceName: "/dev/sdf",
            volumeSize: 100,
            encrypted: true, // Enable encryption for all EBS block devices
        }],
    });
    

    ec2-launch-template-disallow-unencrypted-block-device

    Severity: high · Enforcement: advisory

    Checks that EC2 Launch Templates do not have unencrypted block device.

    • 2. Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
    Remediation
    Fix: Enable EBS Volume Encryption in Launch Template

    Set the encrypted property to “true” for all block device mappings in the Launch Template:

    const launchTemplate = new aws.ec2.LaunchTemplate("my-template", {
        imageId: "ami-12345678",
        instanceType: "t3.micro",
        blockDeviceMappings: [{
            deviceName: "/dev/sda1",
            ebs: {
                encrypted: "true", // Enable encryption for the EBS volume
                volumeSize: 20,
                volumeType: "gp3",
            },
        }],
    });
    

    elb-access-logging-enabled

    Severity: medium · Enforcement: advisory

    Check that ELB Load Balancers uses access logging.

    • 6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
    Remediation
    Fix: Enable Access Logging for ELB Load Balancer

    Configure the accessLogs property with an S3 bucket to store access logs:

    const loadBalancer = new aws.elb.LoadBalancer("my-lb", {
        availabilityZones: ["us-west-2a", "us-west-2b"],
        listeners: [{ /* ... */ }],
        accessLogs: {
            enabled: true,  // Enable access logging
            bucket: "my-elb-logs-bucket",  // S3 bucket for logs
            bucketPrefix: "my-app",  // Optional prefix for log files
        },
    });
    

    elb-cross-zone-load-balancing-enabled

    Severity: medium · Enforcement: advisory

    Classic Load Balancers must have cross-zone load balancing enabled

    • 7. High Availability — Deploy resources in redundant, fault-tolerant configurations to ensure service continuity.
    Remediation
    Fix: Enable cross-zone load balancing on Classic Load Balancer

    Set the crossZoneLoadBalancing property to true to distribute traffic evenly across all availability zones.

    const loadBalancer = new aws.elb.LoadBalancer("my-load-balancer", {
        availabilityZones: ["us-west-2a", "us-west-2b"],
        listeners: [{
            instancePort: 80,
            instanceProtocol: "http",
            lbPort: 80,
            lbProtocol: "http",
        }],
        crossZoneLoadBalancing: true, // Enable cross-zone load balancing
    });
    

    elb-disallow-unencrypted-traffic

    Severity: critical · Enforcement: advisory

    Check that ELB Load Balancers do not allow unencrypted (HTTP) traffic.

    • 3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.
    Remediation
    Fix: Configure HTTPS/SSL listeners instead of HTTP

    Update your ELB Load Balancer to use HTTPS or SSL protocol instead of HTTP:

    const loadBalancer = new aws.elb.LoadBalancer("my-lb", {
        listeners: [
            {
                instancePort: 443,
                instanceProtocol: "https",
                lbPort: 443,
                lbProtocol: "https", // Use "https" or "ssl" instead of "http"
                sslCertificateId: "arn:aws:iam::123456789012:server-certificate/my-cert", // Required for HTTPS/SSL
            },
        ],
    });
    

    environment-separation-tagging

    Severity: low · Enforcement: advisory

    Ensures that resources are tagged to distinguish between production and non-production environments

    • 5. Tagging — Enforce standardized resource tags for ownership, environment, and compliance tracking.
    Remediation
    Fix: Add Environment Tag to Resource

    Add an “Environment” tag to the resource with a valid environment value (e.g., production, development, staging, testing):

    const instance = new aws.ec2.Instance("web-server", {
        instanceType: "t3.micro",
        ami: "ami-12345678",
        tags: {
            "Environment": "production", // Add environment tag for proper separation
            "Name": "web-server",
        },
    });
    

    iam-group-policy-least-privilege

    Severity: high · Enforcement: advisory

    Ensures IAM group policies follow least privilege principles

    • 1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.
    Remediation
    Fix: Replace Wildcard Permissions with Specific Actions and Resources

    Replace wildcard actions and resources with explicit, scoped permissions:

    const groupPolicy = new aws.iam.GroupPolicy("my-group-policy", {
        group: myGroup.name,
        policy: JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Action: [
                    "cloudwatch:PutMetricData",  // Specify exact actions needed
                ],
                Resource: "*",  // Some AWS services require "*" for resource
                Condition: {
                    StringEquals: {
                        "cloudwatch:namespace": "MyApp",  // Use conditions to scope access
                    },
                },
            }],
        }),
    });
    

    iam-policy-least-privilege

    Severity: high · Enforcement: advisory

    Ensures IAM policies follow least privilege principles

    • 1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.
    Remediation
    Fix: Replace Wildcard Permissions with Specific Actions and Resources

    Replace wildcard actions and resources with explicit, scoped permissions that grant only the minimum required access:

    const policy = new aws.iam.Policy("my-policy", {
        policy: JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Action: [
                    "s3:GetObject",        // Specify exact actions needed
                    "s3:PutObject",
                ],
                Resource: [
                    "arn:aws:s3:::my-bucket/*",  // Scope to specific resources
                ],
            }],
        }),
    });
    

    iam-role-least-privilege

    Severity: high · Enforcement: advisory

    Ensures IAM roles follow least privilege principles

    • 1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.
    Remediation
    Fix: Replace Wildcard Permissions with Specific Actions and Resources

    Replace wildcard actions and resources in inline policies with explicit, scoped permissions:

    const role = new aws.iam.Role("my-role", {
        assumeRolePolicy: assumePolicyDoc,
        inlinePolicies: [{
            name: "my-inline-policy",
            policy: JSON.stringify({
                Version: "2012-10-17",
                Statement: [{
                    Effect: "Allow",
                    Action: [
                        "dynamodb:GetItem",    // Specify exact actions needed
                        "dynamodb:PutItem",
                    ],
                    Resource: [
                        "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable",  // Scope to specific resources
                    ],
                }],
            }),
        }],
    });
    

    iam-role-policy-least-privilege

    Severity: high · Enforcement: advisory

    Ensures IAM role policies follow least privilege principles

    • 1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.
    Remediation
    Fix: Replace Wildcard Permissions with Specific Actions and Resources

    Replace wildcard actions and resources with explicit, scoped permissions:

    const rolePolicy = new aws.iam.RolePolicy("my-role-policy", {
        role: myRole.id,
        policy: JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Action: [
                    "ec2:DescribeInstances",   // Specify exact actions needed
                    "ec2:StartInstances",
                    "ec2:StopInstances",
                ],
                Resource: [
                    "arn:aws:ec2:us-east-1:123456789012:instance/*",  // Scope to specific resources
                ],
            }],
        }),
    });
    

    iam-user-policy-least-privilege

    Severity: high · Enforcement: advisory

    Ensures IAM user policies follow least privilege principles

    • 1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.
    Remediation
    Fix: Replace Wildcard Permissions with Specific Actions and Resources

    Replace wildcard actions and resources with explicit, scoped permissions:

    const userPolicy = new aws.iam.UserPolicy("my-user-policy", {
        user: myUser.name,
        policy: JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Action: [
                    "s3:ListBucket",         // Specify exact actions needed
                ],
                Resource: [
                    "arn:aws:s3:::my-bucket",  // Scope to specific resources
                ],
            }],
        }),
    });
    

    kms-key-creation

    Severity: medium · Enforcement: advisory

    Validates KMS key creation with appropriate specifications and origins

    • 14. Key Management & Rotation — Manage encryption keys securely and enforce periodic key rotation to reduce the risk of compromise.
    Remediation
    Fix: Configure KMS Key with Proper Specifications

    Add a description, configure an appropriate deletion window (7-30 days), and ensure key specifications match your security requirements:

    const key = new aws.kms.Key("my-key", {
        description: "Key for encrypting application data", // Add clear description
        customerMasterKeySpec: "SYMMETRIC_DEFAULT", // Use approved key spec
        keyUsage: "ENCRYPT_DECRYPT", // Specify key usage
        deletionWindowInDays: 30, // Set deletion window between 7-30 days
        enableKeyRotation: true,
    });
    

    kms-key-deletion-lifecycle

    Severity: medium · Enforcement: advisory

    Validates KMS key deletion windows and lifecycle management

    • 14. Key Management & Rotation — Manage encryption keys securely and enforce periodic key rotation to reduce the risk of compromise.
    Remediation
    Fix: Configure KMS Key Deletion Window and Lifecycle Management

    Set a deletionWindowInDays between 7 and 30 days, add descriptive tags, and provide a clear description for proper lifecycle management:

    const kmsKey = new aws.kms.Key("my-key", {
        description: "Encryption key for application data",
        deletionWindowInDays: 30, // Set deletion window between 7-30 days
        tags: {
            Environment: "production",
            Owner: "security-team",
            Purpose: "data-encryption",
        },
    });
    

    kms-key-rotation-enabled

    Severity: medium · Enforcement: advisory

    Checks that KMS Keys have key rotation enabled.

    • 14. Key Management & Rotation — Manage encryption keys securely and enforce periodic key rotation to reduce the risk of compromise.
    Remediation
    Fix: Enable automatic key rotation for KMS keys

    Set the enableKeyRotation property to true on your KMS key resource to enable automatic annual rotation of the key material.

    const key = new aws.kms.Key("my-key", {
        description: "My KMS key",
        enableKeyRotation: true, // Enable automatic key rotation
    });
    

    lambda-concurrent-execution-limits-required

    Severity: medium · Enforcement: advisory

    Lambda functions must have concurrent execution limits configured to protect resource availability

    • 9. Resource Availability — Define and enforce timeouts, quotas, and capacity limits to prevent resource exhaustion.
    Remediation
    Fix: Configure Reserved Concurrent Executions

    Set the reservedConcurrentExecutions property on your Lambda function to limit concurrent executions and prevent resource exhaustion.

    import * as aws from "@pulumi/aws";
    
    const myFunction = new aws.lambda.Function("myFunction", {
        runtime: "nodejs18.x",
        handler: "index.handler",
        role: lambdaRole.arn,
        code: new pulumi.asset.AssetArchive({
            ".": new pulumi.asset.FileArchive("./function"),
        }),
        // Set reserved concurrent executions to limit resource consumption
        reservedConcurrentExecutions: 10,
    });
    

    Choose a value based on your function’s expected load and account limits. Setting this prevents the function from consuming all available concurrency and impacting other functions in your account.

    lambda-dead-letter-queue-required

    Severity: medium · Enforcement: advisory

    Lambda functions must have dead letter queues configured for error handling and incident response

    • 8. Require DLQ — Ensure all asynchronous messaging systems are configured with a dead-letter queue to handle failures.
    Remediation
    Fix: Configure Dead Letter Queue for Lambda Function

    Add a deadLetterConfig property to your Lambda function with a target SQS queue or SNS topic ARN to capture failed invocations for incident analysis.

    import * as aws from "@pulumi/aws";
    
    // Create a dead letter queue
    const dlq = new aws.sqs.Queue("my-function-dlq", {
        messageRetentionSeconds: 1209600, // 14 days
    });
    
    const lambdaFunction = new aws.lambda.Function("my-function", {
        runtime: "nodejs18.x",
        handler: "index.handler",
        role: lambdaRole.arn,
        code: new pulumi.asset.AssetArchive({
            ".": new pulumi.asset.FileArchive("./function"),
        }),
        // Add dead letter configuration to capture failed invocations
        deadLetterConfig: {
            targetArn: dlq.arn, // ARN of SQS queue or SNS topic
        },
    });
    

    lambda-function-documentation

    Severity: low · Enforcement: advisory

    Ensures all AWS Lambda functions have a documented description attribute

    • 12. Documentation — Maintain up-to-date documentation of architectures, configurations, policies, and procedures to ensure clarity, consistency, and auditability.
    Remediation
    Fix: Add Description to Lambda Function

    Add a meaningful description attribute to your Lambda function that is at least 10 characters long:

    const myFunction = new aws.lambda.Function("my-function", {
        code: new pulumi.asset.FileArchive("./function"),
        role: lambdaRole.arn,
        handler: "index.handler",
        runtime: "nodejs20.x",
        description: "Processes user authentication requests and validates credentials", // Add descriptive documentation
    });
    

    limit-lambda-execution-time

    Severity: low · Enforcement: advisory

    Ensures that AWS Lambda functions are configured to time out after a specified duration to prevent extended access

    • 9. Resource Availability — Define and enforce timeouts, quotas, and capacity limits to prevent resource exhaustion.
    Remediation
    Fix: Configure Lambda Function Timeout

    Set the timeout property to limit function execution time (default maximum: 300 seconds):

    const myFunction = new aws.lambda.Function("my-function", {
        runtime: "nodejs20.x",
        handler: "index.handler",
        role: lambdaRole.arn,
        code: new pulumi.asset.AssetArchive({
            ".": new pulumi.asset.FileArchive("./app"),
        }),
        timeout: 300, // Set timeout to prevent extended access (max 300 seconds by default)
    });
    

    neptune-clusterinstance-no-public-access

    Severity: high · Enforcement: advisory

    Checks that Neptune Cluster Instances public access is not enabled.

    • 4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
    Remediation
    Fix: Disable Public Access for Neptune Cluster Instance

    Set the publiclyAccessible property to false:

    const neptuneInstance = new aws.neptune.ClusterInstance("neptune-instance", {
        clusterIdentifier: neptuneCluster.id,
        instanceClass: "db.r5.large",
        engine: "neptune",
        publiclyAccessible: false,  // Disable public access
    });
    

    pubsub-least-privilege-iam

    Severity: medium · Enforcement: advisory

    Ensures IAM policies follow least privilege principles for Pub/Sub services (SNS, SQS, Kinesis)

    • 1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.
    Remediation
    Fix: Use Specific Pub/Sub IAM Actions and Resource ARNs

    Replace wildcard permissions with specific actions and resource ARNs for Pub/Sub services:

    const queuePolicy = new aws.iam.Policy("queue-policy", {
        policy: JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Action: [
                    "sqs:SendMessage",      // Use specific actions instead of sqs:*
                    "sqs:ReceiveMessage",
                    "sqs:DeleteMessage",
                ],
                Resource: "arn:aws:sqs:us-east-1:123456789012:my-queue", // Use specific ARN instead of *
            }],
        }),
    });
    

    rds-cluster-disallow-unencrypted-storage

    Severity: high · Enforcement: advisory

    Checks that RDS Clusters storage is encrypted.

    • 2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
    Remediation
    Fix: Enable RDS Cluster Storage Encryption

    Set the storageEncrypted property to true to enable encryption at rest for the RDS cluster:

    const cluster = new aws.rds.Cluster("my-cluster", {
        engine: "aurora-mysql",
        engineVersion: "8.0.mysql_aurora.3.02.0",
        masterUsername: "admin",
        masterPassword: dbPassword,
        storageEncrypted: true, // Enable storage encryption
        databaseName: "mydb",
        skipFinalSnapshot: true,
    });
    

    rds-cluster-instance-disallow-public-access

    Severity: high · Enforcement: advisory

    Checks that RDS Cluster Instances public access is not enabled.

    • 4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
    Remediation
    Fix: Disable Public Access for RDS Cluster Instance

    Set the publiclyAccessible property to false to ensure the RDS Cluster Instance is not accessible from the public internet:

    const clusterInstance = new aws.rds.ClusterInstance("my-cluster-instance", {
        clusterIdentifier: cluster.id,
        instanceClass: "db.r5.large",
        engine: "aurora-postgresql",
        publiclyAccessible: false, // Disable public access
    });
    

    rds-clusterinstance-ssl-encryption

    Severity: high · Enforcement: advisory

    Ensures RDS cluster instances have SSL/TLS encryption enabled through parameter group configuration

    • 3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.
    Remediation
    Fix: Configure SSL/TLS Encryption for Aurora Cluster Instance

    Configure a parameter group to enforce SSL/TLS connections for your Aurora cluster instance:

    const clusterParamGroup = new aws.rds.ParameterGroup("cluster-params", {
        family: "aurora-postgresql14",
        parameters: [
            {
                name: "rds.force_ssl",
                value: "1", // Enforce SSL/TLS for all connections
            },
        ],
    });
    
    const clusterInstance = new aws.rds.ClusterInstance("my-cluster-instance", {
        clusterIdentifier: cluster.id,
        instanceClass: "db.r5.large",
        engine: cluster.engine,
        dbParameterGroupName: clusterParamGroup.name, // Attach parameter group with SSL enforcement
    });
    

    rds-encryption-enabled

    Severity: high · Enforcement: advisory

    Checks that RDS instance storage is encrypted.

    • 2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
    Remediation
    Fix: Enable RDS Storage Encryption

    Enable the storageEncrypted property on your RDS instance:

    const myRdsInstance = new aws.rds.Instance("my-db", {
        engine: "postgres",
        instanceClass: "db.t3.micro",
        allocatedStorage: 20,
        storageEncrypted: true, // Enable encryption at rest
    });
    

    rds-instance-enable-backup-retention

    Severity: medium · Enforcement: advisory

    Checks that RDS Instances backup retention policy is enabled.

    • 13. Data Backup and Recovery — Regularly back up critical data and systems, store backups securely, and test recovery procedures to ensure timely restoration after failures or disasters.
    Remediation
    Fix: Enable automated backup retention for RDS instance

    Set the backupRetentionPeriod property to specify the number of days to retain automated backups (1-35 days):

    const db = new aws.rds.Instance("my-db", {
        engine: "postgres",
        instanceClass: "db.t3.micro",
        allocatedStorage: 20,
        backupRetentionPeriod: 7, // Enable automated backups with 7-day retention
    });
    

    rds-instance-high-availability

    Severity: medium · Enforcement: advisory

    Ensures RDS instances have Multi-AZ deployment enabled for high availability

    • 7. High Availability — Deploy resources in redundant, fault-tolerant configurations to ensure service continuity.
    Remediation
    Fix: Enable Multi-AZ Deployment for RDS Instance

    Set the multiAz property to true to enable automatic failover to a standby replica in a different Availability Zone:

    const dbInstance = new aws.rds.Instance("my-database", {
        allocatedStorage: 20,
        engine: "mysql",
        engineVersion: "8.0",
        instanceClass: "db.t3.micro",
        username: "admin",
        password: dbPassword,
        multiAz: true, // Enable Multi-AZ deployment for high availability
        skipFinalSnapshot: true,
    });
    

    rds-instance-ssl-encryption

    Severity: high · Enforcement: advisory

    Ensures RDS instances have SSL/TLS encryption enabled through parameter group configuration

    • 3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.
    Remediation
    Fix: Configure SSL/TLS Encryption for RDS Instance

    Configure a parameter group to enforce SSL/TLS connections for your RDS instance:

    const dbParamGroup = new aws.rds.ParameterGroup("db-params", {
        family: "mysql8.0",
        parameters: [
            {
                name: "require_secure_transport",
                value: "1", // Enforce SSL/TLS for all connections
            },
        ],
    });
    
    const db = new aws.rds.Instance("my-database", {
        engine: "mysql",
        instanceClass: "db.t3.micro",
        allocatedStorage: 20,
        parameterGroupName: dbParamGroup.name, // Attach parameter group with SSL enforcement
        username: "admin",
        password: dbPassword,
    });
    

    rds-private-subnet-validation

    Severity: critical · Enforcement: advisory

    Validates that RDS DB subnet groups contain only private subnets

    • 4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
    Remediation
    Fix: Deploy RDS DB Subnet Group in Private Subnets

    Ensure the DB subnet group only contains subnets that do not have routes to an Internet Gateway. Private subnets should route internet-bound traffic through a NAT Gateway instead:

    const privateSubnetGroup = new aws.rds.SubnetGroup("db-subnet-group", {
        subnetIds: [
            privateSubnet1.id,  // Private subnet without Internet Gateway route
            privateSubnet2.id,  // Private subnet without Internet Gateway route
        ],
        tags: {
            Name: "Private DB Subnet Group",
        },
    });
    
    const db = new aws.rds.Instance("database", {
        dbSubnetGroupName: privateSubnetGroup.name,
        publiclyAccessible: false,
        // ... other configuration
    });
    

    rds-ssl-encryption

    Severity: high · Enforcement: advisory

    Ensures RDS instances have SSL/TLS encryption enabled

    • 3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.
    Remediation
    Fix: Configure SSL/TLS Encryption for RDS Instance

    Set the caCertIdentifier and configure a parameter group to enforce SSL/TLS connections for your RDS instance:

    const dbParamGroup = new aws.rds.ParameterGroup("db-params", {
        family: "mysql8.0",
        parameters: [
            {
                name: "require_secure_transport",
                value: "1", // Enforce SSL/TLS for all connections
            },
        ],
    });
    
    const db = new aws.rds.Instance("my-database", {
        engine: "mysql",
        instanceClass: "db.t3.micro",
        allocatedStorage: 20,
        caCertIdentifier: "rds-ca-rsa2048-g1", // Specify CA certificate for SSL/TLS
        parameterGroupName: dbParamGroup.name, // Attach parameter group with SSL enforcement
        username: "admin",
        password: dbPassword,
    });
    

    resource-name-length

    Severity: high · Enforcement: mandatory

    Ensures AWS resource names do not exceed the service-specific character limits imposed by AWS

    • 9. Resource Availability — Define and enforce timeouts, quotas, and capacity limits to prevent resource exhaustion.
    Remediation
    Fix: Shorten the Resource Name

    AWS enforces hard character limits on resource names. The most constrained services are:

    ServicePropertyMax lengthNotes
    Elastic Beanstalk environmentname23 charsForms a DNS hostname
    ElastiCache clusterclusterId20 charsAlphanumeric + hyphens only
    Lambda functionname64 chars
    IAM role / username64 chars
    S3 bucketbucket63 charsMust be globally unique
    RDS instanceidentifier63 charsForms a DNS endpoint
    SQS queuename80 charsFIFO queues: 80 chars incl. .fifo
    EKS clustername100 chars
    CloudFormation stackname128 chars
    EC2 Auto Scaling groupname255 chars
    SNS topicname256 chars

    Shorten the name before running pulumi up. Do not rely on truncation helpers — truncated names can collide silently.

    resource-tagging

    Severity: low · Enforcement: advisory

    Ensures all AWS resources must include tags for proper change tracking

    • 5. Tagging — Enforce standardized resource tags for ownership, environment, and compliance tracking.
    Remediation
    Fix: Add Required Tags to AWS Resources

    Add a tags property with meaningful values to enable proper change tracking and documentation:

    const instance = new aws.ec2.Instance("my-instance", {
        ami: "ami-0c55b159cbfafe1f0",
        instanceType: "t3.micro",
        tags: {
            Environment: "production", // Add meaningful tags for change tracking
            Owner: "team-name",
            Application: "web-app",
        },
    });
    

    s3-bucket-access-logging

    Severity: medium · Enforcement: advisory

    Ensures each S3 bucket has access logging enabled

    • 6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
    Remediation
    Fix: Enable S3 Bucket Access Logging

    Create a BucketLogging resource with a target bucket and prefix to enable access logging:

    const myBucket = new aws.s3.Bucket("my-bucket", {
        // Bucket configuration
    });
    
    const logBucket = new aws.s3.Bucket("log-bucket", {
        // Configure log bucket settings
    });
    
    const bucketLogging = new aws.s3.BucketLogging("my-bucket-logging", {
        bucket: myBucket.id,
        targetBucket: logBucket.id, // Specify target bucket for logs
        targetPrefix: "logs/my-bucket/", // Specify prefix for organization
    });
    

    s3-bucket-encryption

    Severity: high · Enforcement: advisory

    S3 buckets must have server-side encryption configured using BucketServerSideEncryptionConfiguration resource

    • 2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
    Remediation
    Fix: Configure S3 Bucket Server-Side Encryption

    Create a separate BucketServerSideEncryptionConfigurationV2 resource for your S3 bucket with encryption rules. Use AES256 or KMS encryption:

    const bucket = new aws.s3.BucketV2("my-bucket", {
        bucket: "my-secure-bucket",
    });
    
    // Add encryption configuration resource
    const bucketEncryption = new aws.s3.BucketServerSideEncryptionConfiguration("my-bucket-encryption", {
        bucket: bucket.id,
        rules: [{
            applyServerSideEncryptionByDefault: {
                sseAlgorithm: "aws:kms", // Enable KMS encryption
                kmsMasterKeyId: kmsKey.arn, // Specify customer-managed key
            },
            bucketKeyEnabled: true, // Enable bucket key for cost optimization
        }],
    });
    

    s3-bucket-least-privilege

    Severity: critical · Enforcement: advisory

    Prevents overly permissive S3 bucket policies

    • 1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.
    Remediation
    Fix: Use Specific Actions, Resources, and Principals

    Replace wildcard (*) values in S3 bucket policy statements with specific, scoped permissions:

    const bucketPolicy = new aws.s3.BucketPolicy("policy", {
        bucket: bucket.id,
        policy: bucket.arn.apply(arn => JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Principal: {
                    AWS: "arn:aws:iam::123456789012:role/specific-role" // Specify exact principal ARN
                },
                Action: ["s3:GetObject", "s3:PutObject"], // Use specific S3 actions
                Resource: `${arn}/*` // Scope to specific bucket
            }]
        }))
    });
    

    s3-bucket-public-access-block

    Severity: critical · Enforcement: advisory

    Ensures each S3 bucket has a public access block with all settings enabled

    • 4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
    Remediation
    Fix: Enable All Public Access Block Settings

    Create a BucketPublicAccessBlock resource with all four settings set to true:

    const myBucket = new aws.s3.Bucket("my-bucket", {
        // Bucket configuration
    });
    
    const publicAccessBlock = new aws.s3.BucketPublicAccessBlock("my-bucket-public-access-block", {
        bucket: myBucket.id,
        blockPublicAcls: true,       // Blocks new public ACLs
        blockPublicPolicy: true,     // Blocks new public bucket policies
        ignorePublicAcls: true,      // Ignores existing public ACLs
        restrictPublicBuckets: true, // Restricts public access to buckets with public policies
    });
    

    s3-bucket-replication

    Enforcement: advisory

    Ensures S3 buckets have replication configured for enhanced availability

    • 13. Data Backup and Recovery — Regularly back up critical data and systems, store backups securely, and test recovery procedures to ensure timely restoration after failures or disasters.

    s3-bucket-versioning

    Severity: medium · Enforcement: advisory

    S3 buckets must have versioning enabled using BucketVersioning resource

    • 13. Data Backup and Recovery — Regularly back up critical data and systems, store backups securely, and test recovery procedures to ensure timely restoration after failures or disasters.
    Remediation
    Fix: Enable S3 Bucket Versioning

    Create a separate BucketVersioning resource with status set to “Enabled” for each S3 bucket:

    const bucket = new aws.s3.BucketV2("my-bucket", {
        bucket: "my-data-bucket",
    });
    
    const versioning = new aws.s3.BucketVersioning("my-bucket-versioning", {
        bucket: bucket.id,
        versioningConfiguration: {
            status: "Enabled", // Enable versioning for data integrity and recovery
        },
    });
    

    security-group-default-deny

    Severity: high · Enforcement: advisory

    Ensures Security Groups follow default deny with explicit allow principle

    • 11. Networking — Only allow required inbound and outbound traffic through network security groups, firewalls, or ACLs.
    Remediation
    Fix: Replace Unrestricted CIDR Blocks with Specific IP Ranges

    Replace 0.0.0.0/0 in ingress and egress rules with specific CIDR blocks or security group references to follow the default deny with explicit allow principle:

    const webSecurityGroup = new aws.ec2.SecurityGroup("web-sg", {
        vpcId: vpc.id,
        ingress: [{
            protocol: "tcp",
            fromPort: 443,
            toPort: 443,
            cidrBlocks: ["10.0.0.0/16"], // Use specific CIDR block instead of 0.0.0.0/0
        }],
        egress: [{
            protocol: "tcp",
            fromPort: 443,
            toPort: 443,
            cidrBlocks: ["10.0.0.0/16"], // Use specific CIDR block instead of 0.0.0.0/0
        }],
    });
    

    security-group-ssh-rdp

    Severity: critical · Enforcement: advisory

    Ensures security groups do not allow SSH/RDP from the internet

    • 11. Networking — Only allow required inbound and outbound traffic through network security groups, firewalls, or ACLs.
    Remediation
    Fix: Remove Public SSH and RDP Access

    Remove or restrict ingress rules that allow SSH (port 22) or RDP (port 3389) from 0.0.0.0/0. Use specific CIDR blocks for trusted networks or implement alternative secure access methods:

    const securityGroup = new aws.ec2.SecurityGroup("my-sg", {
        vpcId: vpc.id,
        ingress: [
            {
                fromPort: 22,
                toPort: 22,
                protocol: "tcp",
                cidrBlocks: ["10.0.0.0/16"], // Restrict to specific network, not 0.0.0.0/0
            },
            {
                fromPort: 3389,
                toPort: 3389,
                protocol: "tcp",
                cidrBlocks: ["10.0.0.0/16"], // Restrict to specific network, not 0.0.0.0/0
            },
        ],
    });
    

    security-group-strict

    Severity: high · Enforcement: advisory

    Ensures security groups follow strict firewall rules with default deny

    • 11. Networking — Only allow required inbound and outbound traffic through network security groups, firewalls, or ACLs.
    Remediation
    Fix: Configure Strict Security Group Rules

    Restrict ingress rules to specific CIDR blocks instead of allowing access from the entire internet (0.0.0.0/0). Use specific protocols and narrow port ranges:

    const securityGroup = new aws.ec2.SecurityGroup("my-sg", {
        vpcId: vpc.id,
        ingress: [
            {
                protocol: "tcp", // Use specific protocol, not "-1" (all)
                fromPort: 443,
                toPort: 443,
                cidrBlocks: ["10.0.0.0/8"], // Restrict to internal network, not "0.0.0.0/0"
            },
        ],
        egress: [
            {
                protocol: "-1",
                fromPort: 0,
                toPort: 0,
                cidrBlocks: ["0.0.0.0/0"],
            },
        ],
    });
    

    sqs-dead-letter-queue

    Severity: medium · Enforcement: advisory

    Ensures SQS queues have dead letter queue configuration

    • 8. Require DLQ — Ensure all asynchronous messaging systems are configured with a dead-letter queue to handle failures.
    Remediation
    Fix: Configure Dead Letter Queue for SQS Queue

    Set the redrivePolicy with a deadLetterTargetArn pointing to a DLQ and specify maxReceiveCount to capture failed messages:

    // Create a dead letter queue first
    const dlq = new aws.sqs.Queue("my-dlq", {
        messageRetentionSeconds: 1209600, // 14 days
    });
    
    // Configure the main queue with redrive policy
    const queue = new aws.sqs.Queue("my-queue", {
        redrivePolicy: pulumi.interpolate`{
            "deadLetterTargetArn": "${dlq.arn}",
            "maxReceiveCount": 3
        }`, // Configure DLQ to capture messages that fail processing
        messageRetentionSeconds: 345600,
    });
    

    vpc-flow-logs

    Severity: medium · Enforcement: advisory

    Ensures VPC flow logs use approved destinations for centralized monitoring

    • 6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
    Remediation
    Fix: Configure VPC Flow Logs with Approved Destination

    Configure VPC Flow Logs to use one of the approved log destinations specified in the policy configuration:

    const flowLog = new aws.ec2.FlowLog("vpc-flow-log", {
        vpcId: vpc.id,
        trafficType: "ALL",
        logDestination: "arn:aws:s3:::approved-logging-bucket", // Use approved destination
        logDestinationType: "s3",
    });
    

      The infrastructure as code platform for any cloud.