Last active
October 23, 2021 06:55
-
-
Save bkniffler/960467cde8a090266b9dbf830ef887e3 to your computer and use it in GitHub Desktop.
Hasura ECS cdk typescript example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ec2 = require('@aws-cdk/aws-ec2'); | |
import ecs = require('@aws-cdk/aws-ecs'); | |
import targets = require('@aws-cdk/aws-route53-targets'); | |
import route53 = require('@aws-cdk/aws-route53'); | |
import acm = require('@aws-cdk/aws-certificatemanager'); | |
import secrets = require('@aws-cdk/aws-secretsmanager'); | |
import elb2 = require('@aws-cdk/aws-elasticloadbalancingv2'); | |
import cdk = require('@aws-cdk/core'); | |
export interface StaticSiteProps extends cdk.StackProps { | |
domainName: string; | |
dbSecret: string; | |
dbHost: string; | |
vpc: ec2.IVpc; | |
dbName: string; | |
dbUser: string; | |
subdomain: string; | |
} | |
export class HasuraStack extends cdk.Stack { | |
graphqlUri: string; | |
graphqlDns: string; | |
hasuraAdminSecret: secrets.Secret; | |
jwtSecret: secrets.Secret; | |
path: string; | |
constructor(app: cdk.App, id: string, props: StaticSiteProps) { | |
super(app, id, props); | |
const jwtSecret = new secrets.Secret(this, 'Jwt', { | |
generateSecretString: { | |
includeSpace: false, | |
passwordLength: 32, | |
excludePunctuation: true | |
} | |
}); | |
const adminSecret = new secrets.Secret(this, 'HasuraAdminSecret', { | |
generateSecretString: { | |
includeSpace: false, | |
passwordLength: 32, | |
excludePunctuation: true | |
} | |
}); | |
const taskDefinition = new ecs.TaskDefinition(this, 'TaskDefinition', { | |
compatibility: ecs.Compatibility.EC2 | |
}); | |
const containerDefinition = new ecs.ContainerDefinition( | |
this, | |
'ContainerDefinition', | |
{ | |
logging: new ecs.AwsLogDriver({ | |
streamPrefix: 'ecs' | |
}), | |
image: ecs.ContainerImage.fromRegistry('hasura/graphql-engine:v1.1.0'), | |
secrets: { | |
HASURA_GRAPHQL_ADMIN_SECRET: ecs.Secret.fromSecretsManager( | |
adminSecret | |
) | |
}, | |
environment: { | |
HASURA_GRAPHQL_JWT_SECRET: `{"type": "HS256", "key": "${jwtSecret.secretValue.toString()}"}` | |
}, | |
entryPoint: ['graphql-engine'], | |
command: [ | |
`--host=${props.dbHost}`, | |
'--port=5432', | |
`--user=${props.dbUser}`, | |
`--dbname=${props.dbName}`, | |
`--password=${props.dbSecret}`, | |
'serve' | |
// '--enable-console' | |
// '--admin-secret=$(ADMIN_SECRET)' | |
], | |
taskDefinition, | |
memoryReservationMiB: 512 | |
// memoryLimitMiB: 1024 | |
} | |
); | |
containerDefinition.addPortMappings({ | |
containerPort: 8080, | |
hostPort: 8080 | |
}); | |
const cluster = new ecs.Cluster(this, 'EcsCluster', { | |
vpc: props.vpc | |
}); | |
cluster.addCapacity('Capacity', { | |
instanceType: new ec2.InstanceType('t2.micro'), | |
minCapacity: 0, | |
desiredCapacity: 1, | |
maxCapacity: 1 | |
}); | |
const fargate = new ecs.Ec2Service(this, 'FargateService', { | |
taskDefinition, | |
// vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC }, | |
// securityGroup: securityGroup2, | |
// assignPublicIp: true, | |
cluster | |
}); | |
const zone = route53.HostedZone.fromLookup(this, 'Zone', { | |
domainName: props.domainName | |
}); | |
const loadBalancer = new elb2.ApplicationLoadBalancer(this, 'LB', { | |
vpc: props.vpc, | |
internetFacing: true | |
}); | |
// loadBalancer.addTarget(autoScalingGroup); | |
const domainName = `${props.subdomain}.${props.domainName}`; | |
console.log(domainName); | |
const certficate = new acm.DnsValidatedCertificate( | |
this, | |
'DatabaseCertificate', | |
{ | |
domainName, | |
hostedZone: zone | |
} | |
); | |
setupHttpsRedirect(loadBalancer); | |
forwardSslPort(loadBalancer, certficate, fargate); | |
new route53.ARecord(this, 'DatabaseAliasRecord', { | |
recordName: domainName, | |
target: route53.AddressRecordTarget.fromAlias( | |
new targets.LoadBalancerTarget(loadBalancer) | |
), | |
zone | |
}); | |
const path = '/v1/graphql'; | |
this.graphqlUri = `https://${domainName}${path}`; | |
this.graphqlDns = domainName; | |
this.hasuraAdminSecret = adminSecret; | |
this.jwtSecret = jwtSecret; | |
this.path = path; | |
} | |
} | |
function setupHttpsRedirect(loadBalancer: elb2.ApplicationLoadBalancer) { | |
const listener = loadBalancer.addListener('Listener', { | |
port: 80, | |
open: true | |
}); | |
listener.addRedirectResponse('RedirectHTTPS', { | |
protocol: elb2.Protocol.HTTPS, | |
statusCode: 'HTTP_301', | |
port: '443', | |
host: '#{host}', | |
path: '/#{path}', | |
query: '#{query}' | |
}); | |
} | |
function forwardSslPort( | |
loadBalancer: elb2.ApplicationLoadBalancer, | |
certficate: acm.DnsValidatedCertificate, | |
target: elb2.IApplicationLoadBalancerTarget | |
) { | |
const listener2 = loadBalancer.addListener('Listener2', { | |
port: 443, | |
open: true, | |
certificateArns: [certficate.certificateArn] | |
}); | |
listener2.addTargets('ECS2', { | |
port: 8080, | |
healthCheck: { path: '/healthz' }, | |
targets: [target] | |
}); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.... | |
const pgStack = new PostgreStack(app, `PostgreStack`, { | |
env, | |
dbName: 'somedb', | |
dbUser: 'someuser' | |
}); | |
new HasuraStack(app, `HasuraStack`, { | |
env, | |
vpc: pgStack.vpc, | |
dbHost: pgStack.dbHost, | |
dbSecret: pgStack.dbSecret, | |
dbName: pgStack.dbName, | |
dbUser: pgStack.dbUser, | |
domainName: 'somedomain.com', | |
subdomain: 'graphql' | |
}); | |
.... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ec2 = require('@aws-cdk/aws-ec2'); | |
import rds = require('@aws-cdk/aws-rds'); | |
import secrets = require('@aws-cdk/aws-secretsmanager'); | |
import cdk = require('@aws-cdk/core'); | |
import { CfnOutput } from '@aws-cdk/core'; | |
export interface Props extends cdk.StackProps { | |
dbName: string; | |
dbUser: string; | |
} | |
export class PostgreStack extends cdk.Stack { | |
dbHost: string; | |
dbSecret: string; | |
dbName: string; | |
dbUser: string; | |
vpc: ec2.Vpc; | |
constructor(app: cdk.App, id: string, props: Props) { | |
super(app, id, props); | |
const secret = new secrets.Secret(this, 'DatabaseSecret', { | |
generateSecretString: { | |
includeSpace: false, | |
passwordLength: 32, | |
excludePunctuation: true | |
} | |
}); | |
const vpc = new ec2.Vpc(this, 'Vpc2', { | |
maxAzs: 2, | |
natGateways: 0, | |
cidr: '192.168.0.0/16', | |
subnetConfiguration: [ | |
{ subnetType: ec2.SubnetType.PUBLIC, name: 'Public' } | |
] | |
}); | |
const securityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', { | |
allowAllOutbound: true, | |
vpc | |
}); | |
securityGroup.addIngressRule( | |
ec2.Peer.ipv4(vpc.vpcCidrBlock), | |
ec2.Port.tcp(5432), | |
'PostgreSQL' | |
); | |
const database = new rds.DatabaseInstance(this, 'PostgreSQL', { | |
engine: rds.DatabaseInstanceEngine.POSTGRES, | |
masterUsername: props.dbUser, | |
masterUserPassword: secret.secretValue, | |
instanceClass: ec2.InstanceType.of( | |
ec2.InstanceClass.T2, | |
ec2.InstanceSize.MICRO | |
), | |
securityGroups: [securityGroup], | |
removalPolicy: cdk.RemovalPolicy.DESTROY, | |
deletionProtection: false, | |
databaseName: props.dbName, | |
multiAz: false, | |
vpcPlacement: { subnetType: ec2.SubnetType.PUBLIC }, | |
vpc | |
}); | |
new CfnOutput(this, 'DbAd', { | |
description: 'DB Ad', | |
value: database.dbInstanceEndpointAddress | |
}); | |
this.vpc = vpc; | |
this.dbHost = database.dbInstanceEndpointAddress; | |
this.dbName = props.dbName; | |
this.dbSecret = secret.secretValue.toString(); | |
this.dbUser = props.dbUser; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment