Skip to content

Instantly share code, notes, and snippets.

@medelman17
Created April 24, 2021 01:27
Show Gist options
  • Save medelman17/959ed7bd11d60b191a54c385ac686703 to your computer and use it in GitHub Desktop.
Save medelman17/959ed7bd11d60b191a54c385ac686703 to your computer and use it in GitHub Desktop.
import * as fs from 'fs';
import * as path from 'path';
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda-nodejs';
import * as kms from '@aws-cdk/aws-kms';
import * as secretsmanager from '@aws-cdk/aws-secretsmanager';
import * as acm from '@aws-cdk/aws-certificatemanager';
import * as alias from '@aws-cdk/aws-route53-targets';
import * as iam from '@aws-cdk/aws-iam';
import * as api from '@aws-cdk/aws-apigatewayv2';
import * as route53 from '@aws-cdk/aws-route53';
import { Runtime, Tracing } from '@aws-cdk/aws-lambda';
import { LambdaProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';
export class CdkInfraStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Get existing parent hosted zone for `amplify.edelman.sh` by lookup
const parentZone = route53.HostedZone.fromHostedZoneAttributes(
this,
'ParentZone',
{
zoneName: 'amplify.edelman.sh',
hostedZoneId: 'Z10312421HWSZ73AHQQQU',
}
);
// Create new child hosted zone for `gateway.amplify.edelman.sh`
const childZone = new route53.PublicHostedZone(this, 'ChildZone', {
zoneName: 'gateway.amplify.edelman.sh',
});
// Create new custom domain with api gateway; import existing SSL certificate from
// certificate manager
const domainName = new api.DomainName(this, 'ApiCustomDomain', {
domainName: childZone.zoneName,
certificate: acm.Certificate.fromCertificateArn(
this,
'Cert',
getCertificateArn()
),
});
// Create dns target/record in child zone for api gateway domain
new route53.ARecord(this, 'AliasRecord', {
zone: childZone,
target: route53.RecordTarget.fromAlias(
new alias.ApiGatewayv2DomainProperties(
domainName.regionalDomainName,
domainName.regionalHostedZoneId
)
),
});
// Create zone delegation record in parent zone to forward requests to child
const delegationRecord = new route53.ZoneDelegationRecord(
this,
'DelegationRecord',
{
zone: parentZone,
recordName: childZone.zoneName,
nameServers: childZone.hostedZoneNameServers!,
}
);
// Add delegationRecord dependency on childZone to ensure NS are present
delegationRecord.node.addDependency(childZone);
// Store GitHub private key for app using secrets manager; encrypt with kms key
const encryptionKey = new kms.Key(this, 'GitHubSecretEncryptionKey');
const secret = new secretsmanager.Secret(this, 'GitHubSecret', {
encryptionKey,
generateSecretString: {
secretStringTemplate: JSON.stringify({ key: getGitHubPrivateKey() }),
generateStringKey: 'password',
},
});
// Create execution role for lambda api handlers
const lambdaExecutionRole = new iam.Role(this, `FuncExecutionRole`, {
roleName: 'FuncExecutionRole',
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName(
'service-role/AWSLambdaBasicExecutionRole'
),
],
inlinePolicies: {
customApiFunctionPolicy: new iam.PolicyDocument({
statements: getPolicyStatementsForLambdaExecRole(),
}),
},
});
// Add permission to read GitHub secret to lambda execution role
secret.grantRead(lambdaExecutionRole);
// Create lambda api handler for GitHub app webhook
const webhookHandler = new lambda.NodejsFunction(
this,
'GitHubWebhookHandler',
{
handler: 'handler',
runtime: Runtime.NODEJS_14_X,
role: lambdaExecutionRole,
entry: path.join(__dirname, '../../docs-assistant-lambda/src/index.ts'),
bundling: {
externalModules: ['aws-sdk'],
tsconfig: path.join(__dirname, '../../docs-assistant/tsconfig.json'),
nodeModules: ['probot', 'probot-commands'],
},
depsLockFilePath: path.join(__dirname, '../../../yarn.lock'),
tracing: Tracing.ACTIVE,
environment: {
APP_ID: '112080',
PRIVATE_KEY: secret.secretValueFromJson('key').toString(),
WEBHOOK_SECRET: 'development',
NODE_ENV: 'production',
LOG_LEVEL: 'debug',
SECRET_ID: secret.secretArn,
STAGE: 'development',
SENTRY_DSN:
'https://1a08fe1ec4244d4ea82eefff01bb8ebb@o553757.ingest.sentry.io/5733906',
},
}
);
// Create lambda proxy integration for HttpApi
const gitHubWebhookIntegration = new LambdaProxyIntegration({
handler: webhookHandler,
});
// Create actual HttpApi
const httpApi = new api.HttpApi(this, 'HttpApi');
// Create development stage for HttpApi & map custom domain
const developmentStage = new api.HttpStage(this, 'DevStage', {
httpApi,
stageName: 'dev',
autoDeploy: true,
domainMapping: { domainName, mappingKey: 'dev' },
});
// Add route for GitHub webhook handler & attach integration
httpApi.addRoutes({
path: `/docs-assistant/webhook`,
methods: [api.HttpMethod.POST, api.HttpMethod.PATCH],
integration: gitHubWebhookIntegration,
});
// Output relevant data, endpoint urls, etc.
new cdk.CfnOutput(this, 'HttpApiBaseEndpoint', {
value: httpApi.apiEndpoint,
});
new cdk.CfnOutput(this, 'HttpApiDevStageEndpoint', {
value: developmentStage.url,
});
}
}
function getPolicyStatementsForLambdaExecRole() {
return [
new iam.PolicyStatement({
actions: [
'ec2:DescribeNetworkInterfaces',
'ec2:CreateNetworkInterface',
'ec2:DeleteNetworkInterface',
'ec2:DescribeInstances',
'ec2:AttachNetworkInterface',
'secretsmanager:GetSecretValue',
],
resources: ['*'],
}),
];
}
function getGitHubPrivateKey() {
return fs
.readFileSync(
path.join(
__dirname,
'../../docs-assistant/amplify-docs-assistant.2021-04-23.private-key.pem'
)
)
.toString();
}
function getCertificateArn() {
return 'arn:aws:acm:us-east-1:728134986050:certificate/3d8d5457-b4e8-4cee-860f-a1dda2f50427';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment