Skip to content

Instantly share code, notes, and snippets.

@gdpotter
Created July 1, 2020 12:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gdpotter/dabe84e753ebf5ad17b315e667ede621 to your computer and use it in GitHub Desktop.
Save gdpotter/dabe84e753ebf5ad17b315e667ede621 to your computer and use it in GitHub Desktop.
S3/CloudFront CodePipeline Static Site CDK Stack
import {App, Duration, SecretValue, Stack, StackProps} from "@aws-cdk/core";
import {Bucket} from "@aws-cdk/aws-s3";
import {CloudFrontWebDistribution, OriginAccessIdentity, PriceClass} from '@aws-cdk/aws-cloudfront'
import {PolicyStatement} from "@aws-cdk/aws-iam";
import {BuildSpec, LinuxBuildImage, PipelineProject} from "@aws-cdk/aws-codebuild";
import {Artifact, Pipeline} from "@aws-cdk/aws-codepipeline";
import {
CacheControl,
CodeBuildAction,
GitHubSourceAction,
GitHubTrigger,
S3DeployAction
} from "@aws-cdk/aws-codepipeline-actions";
import {ARecord, HostedZone, RecordTarget} from "@aws-cdk/aws-route53";
import {DnsValidatedCertificate} from "@aws-cdk/aws-certificatemanager";
import {CloudFrontTarget} from "@aws-cdk/aws-route53-targets";
export class ReactSampleStack extends Stack {
constructor(app: App, id: string, props?: StackProps) {
super(app, id, props);
const webappBucket = new Bucket(this, 'Bucket', {
bucketName: 'react-sample-web'
});
const cloudFrontOAI = new OriginAccessIdentity(this, 'OAI', {
comment: 'OAI for react sample webapp.',
});
const cloudfrontS3Access = new PolicyStatement();
cloudfrontS3Access.addActions('s3:GetBucket*');
cloudfrontS3Access.addActions('s3:GetObject*');
cloudfrontS3Access.addActions('s3:List*');
cloudfrontS3Access.addResources(webappBucket.bucketArn);
cloudfrontS3Access.addResources(`${webappBucket.bucketArn}/*`);
cloudfrontS3Access.addCanonicalUserPrincipal(
cloudFrontOAI.cloudFrontOriginAccessIdentityS3CanonicalUserId
);
webappBucket.addToResourcePolicy(cloudfrontS3Access);
const hostedZone = HostedZone.fromLookup(this, 'HostedZone', {
domainName: 'gdpotter.com',
privateZone: false
});
const certificate = new DnsValidatedCertificate(this, 'Certificate', {
domainName: 'react-test.gdpotter.com',
hostedZone
});
const distribution = new CloudFrontWebDistribution(this, 'Cloudfront', {
originConfigs: [
{
s3OriginSource: {
s3BucketSource: webappBucket,
originAccessIdentity: cloudFrontOAI
},
behaviors: [
{isDefaultBehavior: true}
]
}
],
errorConfigurations: [
{
errorCode: 404,
responseCode: 200,
responsePagePath: '/index.html',
errorCachingMinTtl: 0
}
],
priceClass: PriceClass.PRICE_CLASS_100,
aliasConfiguration: {
acmCertRef: certificate.certificateArn,
names: ['react-test.gdpotter.com']
}
});
new ARecord(this, 'Alias', {
zone: hostedZone,
recordName: 'react-test',
target: RecordTarget.fromAlias(new CloudFrontTarget(distribution))
});
const sourceOutput = new Artifact();
const buildHtmlOutput = new Artifact('base');
const buildStaticOutput = new Artifact('static');
new Pipeline(this, 'Pipeline', {
stages: [
{
stageName: 'Source',
actions: [
new GitHubSourceAction({
actionName: 'Checkout',
owner: 'gdpotter',
repo: 'webpack-react-sample',
oauthToken: SecretValue.secretsManager('gdpotter-github'),
output: sourceOutput,
trigger: GitHubTrigger.WEBHOOK,
})
]
},
{
stageName: 'Build',
actions: [
new CodeBuildAction({
actionName: 'Webapp',
project: new PipelineProject(this, 'Build', {
projectName: 'ReactSample',
buildSpec: BuildSpec.fromObject({
version: '0.2',
phases: {
install: {
commands: [
'npm install'
]
},
build: {
commands: 'npm run build'
}
},
artifacts: {
'secondary-artifacts': {
[buildHtmlOutput.artifactName as string]: {
'base-directory': 'dist',
files: [
'*'
]
},
[buildStaticOutput.artifactName as string]: {
'base-directory': 'dist',
files: [
'static/**/*'
]
}
}
}
}),
environment: {
buildImage: LinuxBuildImage.STANDARD_4_0,
}
}),
input: sourceOutput,
outputs: [buildStaticOutput, buildHtmlOutput]
})
]
},
{
stageName: 'Deploy',
actions: [
new S3DeployAction({
actionName: 'Static-Assets',
input: buildStaticOutput,
bucket: webappBucket,
cacheControl: [CacheControl.setPublic(), CacheControl.maxAge(Duration.days(1))],
runOrder: 1
}),
new S3DeployAction({
actionName: 'HTML-Assets',
input: buildHtmlOutput,
bucket: webappBucket,
cacheControl: [CacheControl.noCache()],
runOrder: 2
})
]
}
]
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment