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

multi-account.mddocs/guides/

Multi-Account AWS Setup

Guide for managing AWS infrastructure across multiple accounts using Pulumi.

Overview

Multi-account strategies improve security, isolation, and compliance by separating workloads across different AWS accounts.

Account Structure Patterns

Organizational Setup

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

// Create organizational units
const devOU = new aws.organizations.OrganizationalUnit("dev", {
    name: "Development",
    parentId: rootOrg.id,
});

const prodOU = new aws.organizations.OrganizationalUnit("prod", {
    name: "Production",
    parentId: rootOrg.id,
});

// Create member accounts
const devAccount = new aws.organizations.Account("dev-account", {
    name: "Development Account",
    email: "dev@example.com",
    parentId: devOU.id,
});

const prodAccount = new aws.organizations.Account("prod-account", {
    name: "Production Account",
    email: "prod@example.com",
    parentId: prodOU.id,
});

Cross-Account Access

Assume Role Pattern

// In target account: Create role to be assumed
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::${sourceAccountId}:root`,
            },
            Action: "sts:AssumeRole",
            Condition: {
                StringEquals: {
                    "sts:ExternalId": externalId,
                },
            },
        }],
    }),
});

// Attach permissions to the role
new aws.iam.RolePolicyAttachment("s3-access", {
    role: crossAccountRole.name,
    policyArn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess",
});

// In source account: Create policy to assume role
const assumeRolePolicy = new aws.iam.Policy("assume-role-policy", {
    policy: crossAccountRole.arn.apply(roleArn =>
        JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Action: "sts:AssumeRole",
                Resource: roleArn,
            }],
        })
    ),
});

Using Multiple Providers

// Configure providers for different accounts
const devProvider = new aws.Provider("dev", {
    region: "us-west-2",
    assumeRole: {
        roleArn: `arn:aws:iam::${devAccountId}:role/OrganizationAccountAccessRole`,
    },
});

const prodProvider = new aws.Provider("prod", {
    region: "us-west-2",
    assumeRole: {
        roleArn: `arn:aws:iam::${prodAccountId}:role/OrganizationAccountAccessRole`,
    },
});

// Create resources in different accounts
const devBucket = new aws.s3.Bucket("dev-bucket", {
    bucket: "my-dev-bucket",
}, { provider: devProvider });

const prodBucket = new aws.s3.Bucket("prod-bucket", {
    bucket: "my-prod-bucket",
}, { provider: prodProvider });

Cross-Account Resource Access

S3 Cross-Account Access

// In account A: Create bucket with policy
const bucket = new aws.s3.Bucket("shared-bucket", {
    bucket: "shared-data-bucket",
});

new aws.s3.BucketPolicy("cross-account-policy", {
    bucket: bucket.id,
    policy: bucket.arn.apply(arn =>
        JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Principal: {
                    AWS: `arn:aws:iam::${accountBId}:root`,
                },
                Action: [
                    "s3:GetObject",
                    "s3:ListBucket",
                ],
                Resource: [
                    arn,
                    `${arn}/*`,
                ],
            }],
        })
    ),
});

KMS Cross-Account Encryption

const kmsKey = new aws.kms.Key("cross-account-key", {
    description: "Cross-account KMS key",
    policy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [
            {
                Sid: "Enable IAM policies",
                Effect: "Allow",
                Principal: {
                    AWS: `arn:aws:iam::${currentAccountId}:root`,
                },
                Action: "kms:*",
                Resource: "*",
            },
            {
                Sid: "Allow other account",
                Effect: "Allow",
                Principal: {
                    AWS: `arn:aws:iam::${otherAccountId}:root`,
                },
                Action: [
                    "kms:Decrypt",
                    "kms:DescribeKey",
                ],
                Resource: "*",
            },
        ],
    }),
});

Centralized Services

Central Logging Account

// In logging account
const logBucket = new aws.s3.Bucket("central-logs", {
    bucket: "organization-central-logs",
});

new aws.s3.BucketPolicy("log-bucket-policy", {
    bucket: logBucket.id,
    policy: logBucket.arn.apply(arn =>
        JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Principal: {
                    Service: "cloudtrail.amazonaws.com",
                },
                Action: "s3:PutObject",
                Resource: `${arn}/*`,
                Condition: {
                    StringEquals: {
                        "s3:x-amz-acl": "bucket-owner-full-control",
                    },
                },
            }],
        })
    ),
});

// In member accounts: Configure CloudTrail
const trail = new aws.cloudtrail.Trail("org-trail", {
    s3BucketName: logBucket.id,
    isOrganizationTrail: true,
});

Network Hub Account

// Transit Gateway in hub account
const tgw = new aws.ec2transitgateway.TransitGateway("hub-tgw", {
    description: "Organization Transit Gateway",
    defaultRouteTableAssociation: "enable",
    defaultRouteTablePropagation: "enable",
});

// Share with organization
const share = new aws.ram.ResourceShare("tgw-share", {
    name: "transit-gateway-share",
    allowExternalPrincipals: false,
});

new aws.ram.ResourceAssociation("tgw-association", {
    resourceArn: tgw.arn,
    resourceShareArn: share.arn,
});

new aws.ram.PrincipalAssociation("org-association", {
    principal: organizationArn,
    resourceShareArn: share.arn,
});

Service Control Policies

const restrictRegionsPolicy = new aws.organizations.Policy("restrict-regions", {
    name: "RestrictRegions",
    type: "SERVICE_CONTROL_POLICY",
    content: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Effect: "Deny",
            Action: "*",
            Resource: "*",
            Condition: {
                StringNotEquals: {
                    "aws:RequestedRegion": [
                        "us-west-2",
                        "us-east-1",
                    ],
                },
            },
        }],
    }),
});

new aws.organizations.PolicyAttachment("dev-policy", {
    policyId: restrictRegionsPolicy.id,
    targetId: devOU.id,
});

Best Practices

  1. Separate Environments: Use different accounts for dev, staging, and production
  2. Centralize Logging: Route all logs to a dedicated logging account
  3. Network Isolation: Use a hub-and-spoke network architecture
  4. Billing Separation: Track costs per account/workload
  5. Security Boundaries: Enforce SCPs at the organizational level
  6. Automate Account Creation: Use Infrastructure as Code for new accounts

Related Documentation

  • IAM Best Practices
  • Security Guide
  • Organizations Service

Install with Tessl CLI

npx tessl i tessl/npm-pulumi--aws@7.16.0

docs

index.md

quickstart.md

README.md

tile.json