Skip to content

Instantly share code, notes, and snippets.

@fourgates
Last active November 25, 2020 23:36
Show Gist options
  • Save fourgates/855aad3c722f623da895548e8cd6a8b5 to your computer and use it in GitHub Desktop.
Save fourgates/855aad3c722f623da895548e8cd6a8b5 to your computer and use it in GitHub Desktop.
AWS CDK - S3 / Cloudfront + Domain + SSL & ECS (ecr, ecs-pattern [elb, taskdef, service, cluster])
import * as cdk from '@aws-cdk/core';
import * as dynamodb from '@aws-cdk/aws-dynamodb';
import * as s3 from '@aws-cdk/aws-s3';
import * as s3Deploy from '@aws-cdk/aws-s3-deployment';
import * as cloudfront from '@aws-cdk/aws-cloudfront';
import * as route53 from '@aws-cdk/aws-route53';
import * as acm from '@aws-cdk/aws-certificatemanager';
import * as targets from '@aws-cdk/aws-route53-targets/lib';
import { OriginAccessIdentity } from '@aws-cdk/aws-cloudfront';
import * as ecr from '@aws-cdk/aws-ecr';
import * as ecsPatterns from '@aws-cdk/aws-ecs-patterns'
import * as ecs from '@aws-cdk/aws-ecs'
import * as ec2 from '@aws-cdk/aws-ec2'
import * as elb2 from '@aws-cdk/aws-elasticloadbalancingv2'
import { StaticSite, StaticSiteProps } from './static-website/static-webiste';
export class CdkStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// 0. we need a NoSQL db to persist data
// create dynamoDb table to store data
const table = new dynamodb.Table(this, 'Questions', {
partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
sortKey: {name: 'sk', type: dynamodb.AttributeType.STRING},
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
tableName: 'Questions'
});
new cdk.CfnOutput(this, 'Dynamo', { value: table.tableName });
// 1. we need an angular app to fun the FE and connect to an app api
// - S3 Bucket, Cloudfront Dist, Route 53 Alias
// create S3 + CloudFront to host the FE
const siteSubDomain = "app";
const domainName = "yourdomain.com";
const zone = route53.HostedZone.fromLookup(this, 'Zone', { domainName: domainName });
const siteDomain = siteSubDomain + '.' + domainName;
new cdk.CfnOutput(this, 'Site', { value: 'https://' + siteDomain });
// TLS certificate
const wildCartCert = new acm.DnsValidatedCertificate(this, 'SiteCertificate', {
domainName: "*.yourdomain.com",
hostedZone: zone,
region: 'us-east-1', // Cloudfront only checks this region for certificates.
});
const certificateArn = wildCartCert.certificateArn;
new cdk.CfnOutput(this, 'Certificate', { value: certificateArn });
// S3
const bucket = new s3.Bucket(this, "MathDynastyAppBucket", {
publicReadAccess: false,
removalPolicy: cdk.RemovalPolicy.DESTROY,
websiteIndexDocument: "index.html",
bucketName: "math-dynasty-app-bucket"
});
new cdk.CfnOutput(this, 'Bucket', { value: bucket.bucketName });
const oia = new OriginAccessIdentity(this, 'OIA', {
comment: "Created by CDK"
});
bucket.grantRead(oia);
// Cloudfront
const distribution = new cloudfront.CloudFrontWebDistribution(this, "CDKMDAAPPDistribution", {
aliasConfiguration: {
acmCertRef: certificateArn,
names: [ siteDomain ],
sslMethod: cloudfront.SSLMethod.SNI,
securityPolicy: cloudfront.SecurityPolicyProtocol.TLS_V1_1_2016
},
originConfigs: [
{
s3OriginSource: {
s3BucketSource: bucket,
originAccessIdentity: oia
},
behaviors: [{isDefaultBehavior: true}]
},
]
});
new cdk.CfnOutput(this, 'DistributionId', { value: distribution.distributionId });
// Route53 alias record for the CloudFront distribution
new route53.ARecord(this, 'SiteAliasRecord', {
recordName: siteDomain,
target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
zone
});
// Deployment - deploy angular app
const src = new s3Deploy.BucketDeployment(this, "DeployMDAWithInvalidation", {
sources: [s3Deploy.Source.asset("./build")],
destinationBucket: bucket,
distribution,
distributionPaths: ['/*'],
});
// 2. we need several ECS Repo
// angular FE, express BE, cdk
// ECR
const appDev = new ecr.Repository(this, 'md-app-dev', {
imageScanOnPush: true,
repositoryName: 'md-app-dev'
});
new cdk.CfnOutput(this, 'App Repo', { value: appDev.repositoryUri });
const appApi = new ecr.Repository(this, 'md-app-api-dev', {
imageScanOnPush: true,
repositoryName: 'md-app-api-dev'
});
new cdk.CfnOutput(this, 'App-API Repo', { value: appApi.repositoryUri });
const appCdk = new ecr.Repository(this, 'md-app-cdk', {
imageScanOnPush: true,
repositoryName: 'md-app-cdk'
});
new cdk.CfnOutput(this, 'App-CDK Repo', { value: appCdk.repositoryUri });
const prodVpc = new ec2.Vpc(this, 'VPC');
// TODO create a prod security group
const prodCluster = new ecs.Cluster(this, 'Cluster', {
vpc: prodVpc,
clusterName: 'prod',
});
const prodLoadBalancedFargateService = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'ProdService', {
cluster: prodCluster,
certificate: wildCartCert,
desiredCount: 1,
assignPublicIp: true,
protocol: elb2.ApplicationProtocol.HTTPS,
redirectHTTP: true,
memoryLimitMiB: 1024,
cpu: 512,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
containerName: 'app-api',
containerPort: 80,
enableLogging: true,
family: 'prod-app-api',
logDriver: ecs.LogDriver.awsLogs({
streamPrefix: "prod-app-api",
})
},
domainName: 'app-api.mathdynasty.com',
domainZone: zone,
serviceName: 'app-api'
});
prodLoadBalancedFargateService.targetGroup.configureHealthCheck({
path: "/custom-health-path",
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment