A Pulumi package for creating and managing Amazon Web Services (AWS) cloud resources with infrastructure-as-code.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Guide for managing AWS infrastructure across multiple accounts using Pulumi.
Multi-account strategies improve security, isolation, and compliance by separating workloads across different AWS accounts.
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,
});// 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,
}],
})
),
});// 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 });// 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}/*`,
],
}],
})
),
});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: "*",
},
],
}),
});// 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,
});// 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,
});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,
});Install with Tessl CLI
npx tessl i tessl/npm-pulumi--aws@7.16.0