A repository of CDK resusable typescript code samples to provision AWS infrastructure with code. Some more info on CDK if you are new to it. cdk-home
import cdk = require("@aws-cdk/core");
import ec2 = require("@aws-cdk/aws-ec2");
export class PublicPrivateVpc extends ec2.Vpc {
constructor(scope: cdk.Construct, id: string, public readonly props: PublicPrivateStackProps) {
super(scope, id, {
maxAzs: props.maximumAzs,
cidr: props.vpcCidr,
subnetConfiguration: [
{
name: "Public",
subnetType: ec2.SubnetType.PUBLIC,
cidrMask: props.publicSubnetCidrMask,
},
{
name: "Private",
subnetType: ec2.SubnetType.PRIVATE,
cidrMask: props.privateSubnetCidrMask,
},
],
});
}
}
export interface PublicPrivateStackProps {
vpcCidr: string;
publicSubnetCidrMask: number;
privateSubnetCidrMask: number;
maximumAzs: number;
}
example config
{
vpcCidr: "10.101.0.0/16",
publicSubnetCidrMask: 24,
privateSubnetCidrMask: 24,
maximumAzs: 2,
}
import cdk = require("@aws-cdk/core");
import ec2 = require("@aws-cdk/aws-ec2");
import { PublicPrivateVpc } from "./public-private-vpc";
export class BastionHost extends cdk.Construct {
readonly linuxHost: ec2.BastionHostLinux;
constructor(scope: cdk.Construct, id: string, props: BastionHostProps) {
super(scope, id);
const securityGroupName = "BastionHostSg";
const securityGroup = new ec2.SecurityGroup(scope, securityGroupName, {
vpc: props.stackVpc,
allowAllOutbound: true,
securityGroupName: securityGroupName,
description: "Security group for bastion, no inbound open because we should access via AWS SSM",
});
this.linuxHost = new ec2.BastionHostLinux(scope, `${id}01`, {
vpc: props.stackVpc,
subnetSelection: props.stackVpc.selectSubnets({ subnetType: ec2.SubnetType.PUBLIC }),
instanceName: `${securityGroup.stack.stackName}/${id}`,
securityGroup: securityGroup,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.SMALL),
});
this.linuxHost.instance.instance.addPropertyOverride("KeyName", props.keypairName);
cdk.Tags.of(securityGroup).add("Name", `${securityGroup.stack.stackName}/${securityGroupName}`);
}
}
export interface BastionHostProps {
stackVpc: PublicPrivateVpc;
keypairName: string;
}
note, to access the bastion host using ssh over AWS SSM we need to add to our local ssh config below:
# SSH over Session Manager
host i-* mi-*
ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
and makes sure to ssh using -A and the instance id (not the public DNS or ip)
ssh -A ec2-user@i-xxxxxx
import cdk = require("@aws-cdk/core");
import ec2 = require("@aws-cdk/aws-ec2");
import elbv2 = require("@aws-cdk/aws-elasticloadbalancingv2");
import elbv2Targets = require("@aws-cdk/aws-elasticloadbalancingv2-targets");
import { PublicPrivateVpc } from "./public-private-vpc";
export class AppLoadBalancerInstanceType extends cdk.Construct {
readonly alb: elbv2.ApplicationLoadBalancer;
constructor(scope: cdk.Construct, id: string, props: AppLoadBalancerInstanceTypeProps) {
super(scope, id);
this.alb = new elbv2.ApplicationLoadBalancer(scope, `${id}01`, {
vpc: props.stackVpc,
vpcSubnets: props.stackVpc.selectSubnets({ subnetType: ec2.SubnetType.PUBLIC }),
internetFacing: true,
});
const listener = this.alb.addListener(`${id}-Listener`, {
port: 80,
});
listener.addTargets(`${id}-Targets`, {
port: 8080,
protocol: elbv2.ApplicationProtocol.HTTP,
targets: [new elbv2Targets.InstanceTarget(props.instance)],
});
listener.connections.allowDefaultPortFromAnyIpv4("Open to the world");
listener.connections.allowTo(
new ec2.Connections({
peer: ec2.Peer.ipv4(props.stackVpc.props.vpcCidr),
}),
ec2.Port.tcp(8080),
"Allow all egress 8080 traffic to be routed to the VPC"
);
const firstSecurityGroup = this.alb.connections.securityGroups[0];
cdk.Tags.of(firstSecurityGroup).add("Name", `${firstSecurityGroup.stack.stackName}/${id}Sg`);
}
}
export interface AppLoadBalancerInstanceTypeProps {
stackVpc: PublicPrivateVpc;
instance: ec2.Instance;
}
import cdk = require("@aws-cdk/core");
import ec2 = require("@aws-cdk/aws-ec2");
import iam = require("@aws-cdk/aws-iam");
import s3 = require("@aws-cdk/aws-s3");
import { PublicPrivateVpc } from "./public-private-vpc";
import { IamPolicies } from "./iam-policies";
export class AppServer extends cdk.Construct {
readonly ec2Instance: ec2.Instance;
constructor(scope: cdk.Construct, id: string, private props: AppServerProps) {
super(scope, id);
this.ec2Instance = new ec2.Instance(scope, `${id}01`, {
vpc: props.stackVpc,
keyName: props.keypairName,
vpcSubnets: props.stackVpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE }),
machineImage: props.ami,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM),
availabilityZone: props.stackVpc.availabilityZones[0],
role: this.createIamRole(scope),
securityGroup: this.createSecurityGroup(scope),
blockDevices: [
{
deviceName: "/dev/sda1",
volume: ec2.BlockDeviceVolume.ebs(500, {
deleteOnTermination: true,
}),
},
],
});
}
private createIamRole(scope: cdk.Construct): iam.Role {
const appServerRole = new iam.Role(scope, "AppServerRole", {
assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"),
});
const appServerPolicyDoc = iam.PolicyDocument.fromJson(
IamPolicies.AppServerS3PolicyJson(this.props.s3Bucket.bucketName)
);
appServerRole.addManagedPolicy(
new iam.ManagedPolicy(scope, "AppServerPolicy", {
document: appServerPolicyDoc,
})
);
return appServerRole;
}
private createSecurityGroup(scope: cdk.Construct): ec2.SecurityGroup {
const securityGroupName = "AppServer01Sg";
const securityGroup = new ec2.SecurityGroup(scope, securityGroupName, {
vpc: this.props.stackVpc,
securityGroupName: securityGroupName,
description: "Security Group for AppServer01",
allowAllOutbound: true,
});
const ingressPorts = [
ec2.Port.tcp(22), // SSH
ec2.Port.tcp(8080), // HTTP
];
for (const ingressPort of ingressPorts) {
// Only allow ingressPorts from within the VPC
securityGroup.addIngressRule(ec2.Peer.ipv4(this.props.stackVpc.props.vpcCidr), ingressPort);
}
cdk.Tags.of(securityGroup).add("Name", `${securityGroup.stack.stackName}/${securityGroupName}`);
return securityGroup;
}
}
export interface AppServerProps {
stackVpc: PublicPrivateVpc;
keypairName: string;
ami: ec2.IMachineImage;
s3Bucket: s3.Bucket;
}
and the IAM policy for the role
export const IamPolicies = {
AppServerS3PolicyJson: (bucketName: string) => {
return {
Version: "2012-10-17",
Statement: [
{
Sid: "VisualEditor0",
Effect: "Allow",
Action: [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ReplicateObject",
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:GetObjectVersion",
"s3:DeleteObjectVersion",
],
Resource: [`arn:aws:s3:::${bucketName}`, `arn:aws:s3:::${bucketName}/*`],
},
],
};
},
};