CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-pulumi--aws

A Pulumi package for creating and managing Amazon Web Services (AWS) cloud resources with infrastructure-as-code.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

overview.mddocs/security/

AWS Security Services Overview

Guide to selecting and using AWS security services with Pulumi.

Service Categories

Identity & Access Management

Control who can do what - Authentication and authorization

  • IAM - Users, roles, policies, permissions
  • IAM Identity Center (SSO) - Centralized access management
  • Cognito - User authentication for applications
  • Directory Service - Managed Active Directory

Encryption & Key Management

Protect data at rest and in transit

  • KMS - Key Management Service, encryption keys
  • Secrets Manager - Store and rotate secrets
  • Systems Manager Parameter Store - Configuration and secrets
  • ACM - Certificate Manager, SSL/TLS certificates

Threat Detection & Monitoring

Identify security threats

  • GuardDuty - Intelligent threat detection
  • Security Hub - Centralized security findings
  • Inspector - Vulnerability assessment
  • Macie - Sensitive data discovery
  • Detective - Security investigation

Network Security

Protect network boundaries

  • WAF - Web Application Firewall
  • Shield - DDoS protection
  • Network Firewall - Advanced network filtering
  • Firewall Manager - Centralized firewall management

Compliance & Governance

Audit and compliance

  • CloudTrail - API activity logging
  • Config - Resource configuration history
  • Audit Manager - Compliance auditing
  • Artifact - Compliance reports

Decision Tree

Choose Your Security Services

Start: What do you need to secure?

┌─ Access control
│  ├─ AWS resource access → IAM (users, roles, policies)
│  ├─ Application users → Cognito (user pools)
│  ├─ Cross-account access → IAM roles + trust policies
│  ├─ Temporary credentials → STS (via IAM roles)
│  └─ Centralized SSO → IAM Identity Center
│
┌─ Data encryption
│  ├─ Encryption keys → KMS
│  ├─ Application secrets → Secrets Manager
│  ├─ Configuration values → Parameter Store
│  ├─ SSL/TLS certificates → ACM
│  └─ Client-side encryption → KMS + client libraries
│
┌─ Threat detection
│  ├─ General threats → GuardDuty (always enable)
│  ├─ Centralized view → Security Hub
│  ├─ Vulnerabilities → Inspector
│  ├─ Sensitive data → Macie
│  └─ Investigation → Detective
│
┌─ Network protection
│  ├─ Web application → WAF (with CloudFront or ALB)
│  ├─ DDoS protection → Shield (Standard free, Advanced paid)
│  ├─ Network filtering → Network Firewall
│  └─ Centralized management → Firewall Manager
│
└─ Audit & compliance
   ├─ API logging → CloudTrail (always enable)
   ├─ Resource compliance → Config
   ├─ Compliance reports → Audit Manager
   └─ Security standards → Security Hub standards

Service Selection Guide

Use IAM When

  • Always - IAM is the foundation of AWS security
  • Controlling access to AWS resources
  • Creating service roles for AWS services
  • Implementing least-privilege access
  • Managing user and application credentials

IAM best practices:

  1. Never use root account - Create IAM users/roles
  2. Enable MFA - Multi-factor authentication for users
  3. Use roles, not users, for applications - Temporary credentials
  4. Apply least privilege - Grant minimal permissions
  5. Use AWS managed policies - When appropriate
  6. Rotate credentials regularly - Use access key rotation
  7. Monitor with CloudTrail - Track all API calls

Key concepts:

  • Users - Individual identities (people, applications)
  • Groups - Collections of users with shared permissions
  • Roles - Temporary credentials for services/applications
  • Policies - JSON documents defining permissions
  • Trust policies - Who can assume a role

Use KMS When

  • Encrypting data at rest (S3, EBS, RDS, etc.)
  • Creating and managing encryption keys
  • Implementing key rotation
  • Compliance requirements for key management
  • Need audit trail for key usage

Key types:

  • Customer managed keys - Full control, rotation, policies
  • AWS managed keys - Automatic management (aws/service-name)
  • AWS owned keys - Used by AWS services, invisible to you

Common use cases:

  • Encrypt S3 buckets (SSE-KMS)
  • Encrypt EBS volumes
  • Encrypt RDS databases
  • Encrypt Secrets Manager secrets
  • Application-level encryption (Encryption SDK)

Cost considerations:

  • $1/month per customer managed key
  • $0.03 per 10,000 API requests
  • AWS managed keys: Free (but requests still billed)

Use Secrets Manager When

  • Storing database credentials
  • Storing API keys, passwords, tokens
  • Need automatic rotation of secrets
  • Want integration with RDS, DocumentDB, Redshift
  • Require cross-account secret access

Secrets Manager vs Parameter Store:

Secrets Manager:

  • Automatic rotation support
  • RDS/DocumentDB integration
  • Cross-account access
  • More expensive ($0.40/secret/month + $0.05/10K API calls)
  • Best for: Database credentials, secrets requiring rotation

Parameter Store:

  • No automatic rotation (manual via Lambda)
  • Simpler secrets and configuration
  • Standard: Free (10K params, 4KB size, 1000 TPS)
  • Advanced: $0.05/param/month (larger, higher throughput)
  • Best for: Configuration values, simple secrets

Use ACM When

  • Need SSL/TLS certificates for AWS services
  • Public certificates for domains
  • Private PKI for internal services
  • Want automatic certificate renewal
  • Free certificates for AWS services

ACM features:

  • Free public certificates
  • Automatic renewal
  • Integration with CloudFront, ALB, API Gateway
  • Wildcard certificates supported
  • Private CA for internal PKI

Validation methods:

  • DNS validation - Recommended, automatic renewal
  • Email validation - Manual process, less automated

Use GuardDuty When

  • Always - Enable in all accounts and regions
  • Need intelligent threat detection
  • Want to identify compromised resources
  • Monitoring for malicious activity
  • Detecting cryptocurrency mining

GuardDuty analyzes:

  • VPC Flow Logs
  • CloudTrail events
  • DNS logs
  • Kubernetes audit logs (EKS)
  • S3 data events (optional)

Finding types:

  • Backdoor activity
  • Bitcoin mining
  • Credential compromise
  • Reconnaissance activity
  • Suspicious network traffic
  • Unusual API calls

Cost:

  • Pay for data analyzed (CloudTrail, VPC Flow Logs, DNS)
  • 30-day free trial
  • Typically $3-10/month per account

Use Security Hub When

  • Centralized security view across accounts
  • Aggregating findings from multiple services
  • Compliance checking (CIS, PCI-DSS, etc.)
  • Security posture management
  • Automated remediation

Security Hub aggregates:

  • GuardDuty findings
  • Inspector findings
  • Macie findings
  • Firewall Manager findings
  • IAM Access Analyzer findings
  • Third-party security tools

Security standards:

  • AWS Foundational Security Best Practices
  • CIS AWS Foundations Benchmark
  • PCI DSS
  • NIST frameworks

Use WAF When

  • Protecting web applications (CloudFront, ALB, API Gateway)
  • Blocking SQL injection, XSS attacks
  • Rate limiting requests
  • Geo-blocking traffic
  • Bot management

WAF rules:

  • AWS Managed Rules - Pre-configured rule sets (recommended)
  • Custom rules - IP sets, geo matching, rate limiting
  • Marketplace rules - Third-party managed rules

Common rule groups:

  • Core rule set (OWASP Top 10)
  • SQL injection protection
  • Known bad inputs
  • Linux/Windows OS protections
  • IP reputation list
  • Anonymous IP list
  • Rate-based rules

Use CloudTrail When

  • Always - Enable in all accounts and regions
  • Need audit trail of API calls
  • Compliance requirements
  • Security investigations
  • Troubleshooting access issues

CloudTrail features:

  • Management events (API calls)
  • Data events (S3 object access, Lambda invocations)
  • Insights events (unusual activity)
  • Multi-region trails
  • Organization trails
  • Integration with CloudWatch Logs

Best practices:

  1. Enable in all regions
  2. Enable log file validation
  3. Encrypt logs with KMS
  4. Store logs in dedicated S3 bucket
  5. Enable versioning on S3 bucket
  6. Set up CloudWatch alarms for critical events

Common Patterns

Pattern 1: Least-Privilege IAM Role

Service Role with Minimal Permissions

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

// IAM role for Lambda function
const lambdaRole = new aws.iam.Role("lambda-role", {
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Effect: "Allow",
            Principal: { Service: "lambda.amazonaws.com" },
            Action: "sts:AssumeRole",
        }],
    }),
    tags: { Purpose: "Lambda execution" },
});

// Attach basic Lambda execution policy
const lambdaBasicPolicy = new aws.iam.RolePolicyAttachment("lambda-basic", {
    role: lambdaRole.name,
    policyArn: "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
});

// Custom inline policy for DynamoDB access
const dynamoPolicy = new aws.iam.RolePolicy("dynamo-access", {
    role: lambdaRole.name,
    policy: pulumi.interpolate`{
        "Version": "2012-10-17",
        "Statement": [{
            "Effect": "Allow",
            "Action": [
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:Query"
            ],
            "Resource": "${table.arn}"
        }]
    }`,
});

// KMS decrypt permission
const kmsPolicy = new aws.iam.RolePolicy("kms-access", {
    role: lambdaRole.name,
    policy: pulumi.interpolate`{
        "Version": "2012-10-17",
        "Statement": [{
            "Effect": "Allow",
            "Action": ["kms:Decrypt"],
            "Resource": "${kmsKey.arn}"
        }]
    }`,
});

export const roleArn = lambdaRole.arn;

Use when: Creating service roles, Lambda functions, ECS tasks

Pattern 2: Secure Secrets Management

Secrets Manager + KMS + Rotation

// KMS key for encrypting secrets
const secretsKey = new aws.kms.Key("secrets-key", {
    description: "KMS key for Secrets Manager",
    enableKeyRotation: true,
    policy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [
            {
                Sid: "Enable IAM policies",
                Effect: "Allow",
                Principal: { AWS: pulumi.interpolate`arn:aws:iam::${accountId}:root` },
                Action: "kms:*",
                Resource: "*",
            },
            {
                Sid: "Allow Secrets Manager",
                Effect: "Allow",
                Principal: { Service: "secretsmanager.amazonaws.com" },
                Action: ["kms:Decrypt", "kms:GenerateDataKey"],
                Resource: "*",
            },
        ],
    }),
    tags: { Purpose: "Secrets encryption" },
});

const secretsKeyAlias = new aws.kms.Alias("secrets-key-alias", {
    name: "alias/secrets-key",
    targetKeyId: secretsKey.id,
});

// Secret for database credentials
const dbSecret = new aws.secretsmanager.Secret("db-credentials", {
    description: "Database credentials with auto-rotation",
    kmsKeyId: secretsKey.id,
    recoveryWindowInDays: 7,
    tags: { Purpose: "RDS credentials" },
});

// Secret version
const dbSecretVersion = new aws.secretsmanager.SecretVersion("db-creds-version", {
    secretId: dbSecret.id,
    secretString: JSON.stringify({
        username: "admin",
        password: randomPassword.result,
        engine: "postgres",
        host: dbInstance.endpoint,
        port: 5432,
        dbname: "myapp",
    }),
});

// Rotation Lambda role
const rotationRole = new aws.iam.Role("rotation-role", {
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Effect: "Allow",
            Principal: { Service: "lambda.amazonaws.com" },
            Action: "sts:AssumeRole",
        }],
    }),
});

// Rotation function (provided by AWS)
const rotationLambda = new aws.lambda.Function("rotation", {
    runtime: "python3.11",
    handler: "lambda_function.lambda_handler",
    role: rotationRole.arn,
    code: new pulumi.asset.FileArchive("./rotation-lambda"), // AWS provided
    environment: {
        variables: {
            SECRETS_MANAGER_ENDPOINT: pulumi.interpolate`https://secretsmanager.${region}.amazonaws.com`,
        },
    },
});

// Enable automatic rotation
const rotation = new aws.secretsmanager.SecretRotation("db-rotation", {
    secretId: dbSecret.id,
    rotationLambdaArn: rotationLambda.arn,
    rotationRules: {
        automaticallyAfterDays: 30,
    },
});

// Lambda permission for Secrets Manager
const rotationPermission = new aws.lambda.Permission("rotation-permission", {
    action: "lambda:InvokeFunction",
    function: rotationLambda.name,
    principal: "secretsmanager.amazonaws.com",
});

export const secretArn = dbSecret.arn;

Use when: Storing database credentials, API keys, secure configuration

Pattern 3: Defense in Depth

Multi-Layer Security (WAF + GuardDuty + Security Hub)

// Enable GuardDuty
const guardduty = new aws.guardduty.Detector("main", {
    enable: true,
    findingPublishingFrequency: "FIFTEEN_MINUTES",
    datasources: {
        s3Logs: { enable: true },
        kubernetes: { auditLogs: { enable: true } },
    },
    tags: { Name: "GuardDuty detector" },
});

// Enable Security Hub
const securityHub = new aws.securityhub.Account("main", {
    enableDefaultStandards: true,
    controlFindingGenerator: "SECURITY_CONTROL",
});

// Enable security standards
const cisStandard = new aws.securityhub.StandardsSubscription("cis", {
    standardsArn: pulumi.interpolate`arn:aws:securityhub:${region}::standards/cis-aws-foundations-benchmark/v/1.4.0`,
});

const foundationalStandard = new aws.securityhub.StandardsSubscription("foundational", {
    standardsArn: pulumi.interpolate`arn:aws:securityhub:${region}::standards/aws-foundational-security-best-practices/v/1.0.0`,
});

// CloudTrail for audit logging
const trailBucket = new aws.s3.BucketV2("cloudtrail-logs", {
    bucket: "cloudtrail-logs-" + accountId,
});

const trailBucketPolicy = new aws.s3.BucketPolicy("cloudtrail-policy", {
    bucket: trailBucket.id,
    policy: pulumi.all([trailBucket.arn, accountId, region]).apply(([arn, account, reg]) =>
        JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Sid: "AWSCloudTrailAclCheck",
                Effect: "Allow",
                Principal: { Service: "cloudtrail.amazonaws.com" },
                Action: "s3:GetBucketAcl",
                Resource: arn,
            }, {
                Sid: "AWSCloudTrailWrite",
                Effect: "Allow",
                Principal: { Service: "cloudtrail.amazonaws.com" },
                Action: "s3:PutObject",
                Resource: `${arn}/*`,
                Condition: {
                    StringEquals: { "s3:x-amz-acl": "bucket-owner-full-control" },
                },
            }],
        })
    ),
});

const trail = new aws.cloudtrail.Trail("main-trail", {
    s3BucketName: trailBucket.id,
    includeGlobalServiceEvents: true,
    isMultiRegionTrail: true,
    enableLogFileValidation: true,
    kmsKeyId: kmsKey.id,
    eventSelectors: [{
        readWriteType: "All",
        includeManagementEvents: true,
    }],
    tags: { Name: "Organization trail" },
});

// WAF for CloudFront/ALB
const wafAcl = new aws.wafv2.WebAcl("web-acl", {
    scope: "REGIONAL", // Use "CLOUDFRONT" for CloudFront
    defaultAction: { allow: {} },
    rules: [
        {
            name: "AWSManagedRulesCommonRuleSet",
            priority: 1,
            overrideAction: { none: {} },
            statement: {
                managedRuleGroupStatement: {
                    vendorName: "AWS",
                    name: "AWSManagedRulesCommonRuleSet",
                },
            },
            visibilityConfig: {
                cloudwatchMetricsEnabled: true,
                metricName: "CommonRuleSet",
                sampledRequestsEnabled: true,
            },
        },
        {
            name: "RateLimitRule",
            priority: 2,
            action: { block: {} },
            statement: {
                rateBasedStatement: {
                    limit: 2000,
                    aggregateKeyType: "IP",
                },
            },
            visibilityConfig: {
                cloudwatchMetricsEnabled: true,
                metricName: "RateLimit",
                sampledRequestsEnabled: true,
            },
        },
    ],
    visibilityConfig: {
        cloudwatchMetricsEnabled: true,
        metricName: "WebACL",
        sampledRequestsEnabled: true,
    },
});

// Associate WAF with ALB
const wafAssociation = new aws.wafv2.WebAclAssociation("alb-waf", {
    resourceArn: alb.arn,
    webAclArn: wafAcl.arn,
});

export const guardDutyId = guardduty.id;
export const securityHubArn = securityHub.arn;
export const wafAclArn = wafAcl.arn;

Use when: Production environments, compliance requirements, high-security needs

Pattern 4: Cross-Account Access

IAM Roles with Trust Relationships

// Role in account A to be assumed by account B
const crossAccountRole = new aws.iam.Role("cross-account-role", {
    name: "CrossAccountAccessRole",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Effect: "Allow",
            Principal: {
                AWS: `arn:aws:iam::${accountBId}:root`, // Account B
            },
            Action: "sts:AssumeRole",
            Condition: {
                StringEquals: {
                    "sts:ExternalId": "unique-external-id-12345",
                },
            },
        }],
    }),
    tags: { Purpose: "Cross-account S3 access" },
});

// Policy granting S3 read access
const s3ReadPolicy = new aws.iam.RolePolicy("s3-read", {
    role: crossAccountRole.name,
    policy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Effect: "Allow",
            Action: [
                "s3:GetObject",
                "s3:ListBucket",
            ],
            Resource: [
                bucket.arn,
                pulumi.interpolate`${bucket.arn}/*`,
            ],
        }],
    }),
});

// In Account B, create user/role with permission to assume the role
const assumeRolePolicy = new aws.iam.Policy("assume-cross-account", {
    policy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Effect: "Allow",
            Action: "sts:AssumeRole",
            Resource: crossAccountRole.arn,
        }],
    }),
});

export const roleArn = crossAccountRole.arn;

// Usage example (in Account B):
// aws sts assume-role \
//   --role-arn arn:aws:iam::ACCOUNT_A_ID:role/CrossAccountAccessRole \
//   --role-session-name CrossAccountSession \
//   --external-id unique-external-id-12345

Use when: Multi-account architectures, centralized logging, shared resources

Pattern 5: Data Encryption at Rest

KMS Encryption for Multiple Services

// Customer managed KMS key
const dataKey = new aws.kms.Key("data-encryption-key", {
    description: "Key for encrypting data at rest",
    enableKeyRotation: true, // Automatic annual rotation
    policy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [
            {
                Sid: "Enable IAM policies",
                Effect: "Allow",
                Principal: { AWS: pulumi.interpolate`arn:aws:iam::${accountId}:root` },
                Action: "kms:*",
                Resource: "*",
            },
            {
                Sid: "Allow services to use key",
                Effect: "Allow",
                Principal: { Service: [
                    "s3.amazonaws.com",
                    "rds.amazonaws.com",
                    "dynamodb.amazonaws.com",
                    "logs.amazonaws.com",
                ] },
                Action: [
                    "kms:Decrypt",
                    "kms:GenerateDataKey",
                    "kms:CreateGrant",
                ],
                Resource: "*",
            },
        ],
    }),
    tags: { Purpose: "Data encryption" },
});

const keyAlias = new aws.kms.Alias("data-key-alias", {
    name: "alias/data-encryption",
    targetKeyId: dataKey.id,
});

// S3 bucket with KMS encryption
const bucket = new aws.s3.BucketV2("encrypted-bucket", {
    bucket: "encrypted-data-bucket",
});

const bucketEncryption = new aws.s3.BucketServerSideEncryptionConfigurationV2("encryption", {
    bucket: bucket.id,
    rules: [{
        applyServerSideEncryptionByDefault: {
            sseAlgorithm: "aws:kms",
            kmsMasterKeyId: dataKey.id,
        },
        bucketKeyEnabled: true, // Reduce KMS costs
    }],
});

// EBS volume with KMS encryption
const volume = new aws.ebs.Volume("encrypted-volume", {
    availabilityZone: "us-east-1a",
    size: 100,
    type: "gp3",
    encrypted: true,
    kmsKeyId: dataKey.id,
    tags: { Name: "Encrypted volume" },
});

// RDS with KMS encryption
const db = new aws.rds.Instance("encrypted-db", {
    engine: "postgres",
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
    storageEncrypted: true,
    kmsKeyId: dataKey.id,
    username: "admin",
    password: dbPassword.result,
});

// DynamoDB with KMS encryption
const table = new aws.dynamodb.Table("encrypted-table", {
    attributes: [{ name: "id", type: "S" }],
    hashKey: "id",
    billingMode: "PAY_PER_REQUEST",
    serverSideEncryption: {
        enabled: true,
        kmsKeyArn: dataKey.arn,
    },
});

export const kmsKeyId = dataKey.id;
export const kmsKeyArn = dataKey.arn;

Use when: Compliance requirements, protecting sensitive data, enterprise security

Security Architecture Guidelines

Defense in Depth

Implement multiple layers of security:

  1. Network Layer

    • VPC with private subnets
    • Security groups (allow-list)
    • Network ACLs (deny-list)
    • WAF for web applications
  2. Identity Layer

    • IAM roles with least privilege
    • MFA for human users
    • Regular credential rotation
    • No hard-coded credentials
  3. Data Layer

    • Encryption at rest (KMS)
    • Encryption in transit (TLS)
    • Regular backups
    • Data classification
  4. Monitoring Layer

    • CloudTrail enabled
    • GuardDuty enabled
    • Security Hub enabled
    • CloudWatch alarms

Zero Trust Principles

  1. Verify explicitly (authenticate and authorize)
  2. Use least-privilege access
  3. Assume breach (limit blast radius)

Cost Optimization

Free Tier / Always Free

  • IAM - Free
  • CloudTrail - First trail free per region
  • Shield Standard - Free DDoS protection
  • ACM - Free public certificates
  • Security Hub - 30-day free trial

Cost Reduction Strategies

  1. Use AWS managed policies - Free, well-tested
  2. Parameter Store vs Secrets Manager - Standard Parameter Store is free
  3. GuardDuty - Monitor costs, disable unnecessary data sources
  4. WAF - Use managed rule groups, they're more cost-effective
  5. KMS - Use bucket keys with S3 to reduce API calls

Quick Links

Core Services

  • IAM - Identity & Access
  • KMS - Key Management
  • Secrets Manager
  • ACM - Certificate Manager
  • WAF - Web Application Firewall
  • GuardDuty - Threat Detection
  • Security Hub

Related Services

Guides

See Also

Install with Tessl CLI

npx tessl i tessl/npm-pulumi--aws

docs

index.md

quickstart.md

README.md

tile.json