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
Comprehensive guide for implementing AWS security best practices with Pulumi.
Always grant the minimum permissions necessary.
const lambdaRole = new aws.iam.Role("lambda-role", {
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: { Service: "lambda.amazonaws.com" },
}],
}),
});
// Specific permissions only
new aws.iam.RolePolicy("lambda-policy", {
role: lambdaRole.id,
policy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Action: ["dynamodb:GetItem", "dynamodb:PutItem"],
Resource: table.arn,
}],
}),
});// S3 bucket with encryption
const bucket = new aws.s3.Bucket("secure-bucket", {
bucket: "secure-data-bucket",
serverSideEncryptionConfiguration: {
rule: {
applyServerSideEncryptionByDefault: {
sseAlgorithm: "aws:kms",
kmsMasterKeyId: kmsKey.id,
},
bucketKeyEnabled: true,
},
},
});
// RDS with encryption
const db = new aws.rds.Instance("secure-db", {
instanceClass: "db.t3.micro",
allocatedStorage: 20,
engine: "postgres",
storageEncrypted: true,
kmsKeyId: kmsKey.arn,
});
// EBS volume with encryption
const volume = new aws.ebs.Volume("secure-volume", {
availabilityZone: "us-west-2a",
size: 100,
encrypted: true,
kmsKeyId: kmsKey.id,
});// ALB with HTTPS only
const alb = new aws.lb.LoadBalancer("secure-alb", {
loadBalancerType: "application",
subnets: subnetIds,
});
const listener = new aws.lb.Listener("https", {
loadBalancerArn: alb.arn,
port: 443,
protocol: "HTTPS",
certificateArn: certificate.arn,
defaultActions: [{
type: "forward",
targetGroupArn: targetGroup.arn,
}],
});
// Redirect HTTP to HTTPS
const httpListener = new aws.lb.Listener("http-redirect", {
loadBalancerArn: alb.arn,
port: 80,
protocol: "HTTP",
defaultActions: [{
type: "redirect",
redirect: {
port: "443",
protocol: "HTTPS",
statusCode: "HTTP_301",
},
}],
});const vpc = new aws.ec2.Vpc("secure-vpc", {
cidrBlock: "10.0.0.0/16",
enableDnsHostnames: true,
enableDnsSupport: true,
tags: { Name: "secure-vpc" },
});
// Private subnets for resources
const privateSubnet = new aws.ec2.Subnet("private", {
vpcId: vpc.id,
cidrBlock: "10.0.1.0/24",
availabilityZone: "us-west-2a",
mapPublicIpOnLaunch: false,
});
// Public subnets only for load balancers
const publicSubnet = new aws.ec2.Subnet("public", {
vpcId: vpc.id,
cidrBlock: "10.0.101.0/24",
availabilityZone: "us-west-2a",
mapPublicIpOnLaunch: true,
});// Restrictive security group
const appSg = new aws.ec2.SecurityGroup("app-sg", {
vpcId: vpc.id,
description: "Application security group",
ingress: [{
protocol: "tcp",
fromPort: 443,
toPort: 443,
cidrBlocks: ["10.0.0.0/16"], // Only from VPC
}],
egress: [{
protocol: "-1",
fromPort: 0,
toPort: 0,
cidrBlocks: ["0.0.0.0/0"],
}],
});const nacl = new aws.ec2.NetworkAcl("private-nacl", {
vpcId: vpc.id,
ingress: [{
protocol: "tcp",
ruleNo: 100,
action: "allow",
cidrBlock: "10.0.0.0/16",
fromPort: 443,
toPort: 443,
}],
egress: [{
protocol: "-1",
ruleNo: 100,
action: "allow",
cidrBlock: "0.0.0.0/0",
fromPort: 0,
toPort: 0,
}],
});const dbSecret = new aws.secretsmanager.Secret("db-secret", {
name: "database-credentials",
description: "Database credentials",
});
const secretVersion = new aws.secretsmanager.SecretVersion("db-secret-version", {
secretId: dbSecret.id,
secretString: pulumi.jsonStringify({
username: "admin",
password: dbPassword, // From Pulumi config
}),
});
// Automatic rotation
const rotationLambda = new aws.lambda.Function("rotation", {
code: new pulumi.asset.AssetArchive({
".": new pulumi.asset.FileArchive("./rotation-lambda"),
}),
handler: "index.handler",
runtime: "python3.11",
role: rotationRole.arn,
});
new aws.secretsmanager.SecretRotation("rotation", {
secretId: dbSecret.id,
rotationLambdaArn: rotationLambda.arn,
rotationRules: {
automaticallyAfterDays: 30,
},
});const trailBucket = new aws.s3.Bucket("cloudtrail-logs", {
bucket: "org-cloudtrail-logs",
versioning: { enabled: true },
});
const trail = new aws.cloudtrail.Trail("audit", {
s3BucketName: trailBucket.id,
includeGlobalServiceEvents: true,
isMultiRegionTrail: true,
enableLogFileValidation: true,
eventSelectors: [{
readWriteType: "All",
includeManagementEvents: true,
}],
});const flowLogRole = new aws.iam.Role("flow-log-role", {
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: { Service: "vpc-flow-logs.amazonaws.com" },
}],
}),
});
const logGroup = new aws.cloudwatch.LogGroup("vpc-flow-logs", {
retentionInDays: 90,
});
const flowLog = new aws.ec2.FlowLog("vpc-flow", {
vpcId: vpc.id,
trafficType: "ALL",
logDestinationType: "cloud-watch-logs",
logDestination: logGroup.arn,
iamRoleArn: flowLogRole.arn,
});const detector = new aws.guardduty.Detector("main", {
enable: true,
findingPublishingFrequency: "FIFTEEN_MINUTES",
});
// Send findings to SNS
const findingsTopic = new aws.sns.Topic("guardduty-findings");
new aws.cloudwatch.EventRule("guardduty-findings", {
eventPattern: JSON.stringify({
source: ["aws.guardduty"],
detailType: ["GuardDuty Finding"],
}),
});const configRecorder = new aws.cfg.Recorder("recorder", {
roleArn: configRole.arn,
recordingGroup: {
allSupported: true,
includeGlobalResourceTypes: true,
},
});
// Check for encrypted volumes
const encryptedVolumesRule = new aws.cfg.Rule("encrypted-volumes", {
source: {
owner: "AWS",
sourceIdentifier: "ENCRYPTED_VOLUMES",
},
});// Enforce tagging strategy
const resourceTags = {
Environment: "production",
Project: "my-app",
CostCenter: "engineering",
ManagedBy: "pulumi",
};
const bucket = new aws.s3.Bucket("tagged-bucket", {
tags: resourceTags,
});Install with Tessl CLI
npx tessl i tessl/npm-pulumi--aws@7.16.0