Home

Deploying a static site with the AWS CDK

Use the resource to add more detail. More to come.

index.ts

#!/usr/bin/env node import cdk = require('@aws-cdk/core'); import { StaticSite } from './static-site'; /** * This stack relies on getting the domain name from CDK context. * Use 'cdk synth -c domain=mystaticsite.com -c subdomain=www' * Or add the following to cdk.json: * { * "context": { * "domain": "mystaticsite.com", * "subdomain": "www" * } * } **/ class MyStaticSiteStack extends cdk.Stack { constructor(parent: cdk.App, name: string, props: cdk.StackProps) { super(parent, name, props); new StaticSite(this, 'StaticSite', { domainName: this.node.tryGetContext('domain'), siteSubDomain: this.node.tryGetContext('subdomain'), }); } } const app = new cdk.App(); new MyStaticSiteStack(app, 'MyStaticSite', { env: { // Stack must be in us-east-1, because the ACM certificate for a // global CloudFront distribution must be requested in us-east-1. region: 'us-east-1', }, }); app.synth();

S3 Stack

#!/usr/bin/env node import cloudfront = require('@aws-cdk/aws-cloudfront'); import route53 = require('@aws-cdk/aws-route53'); import s3 = require('@aws-cdk/aws-s3'); import s3deploy = require('@aws-cdk/aws-s3-deployment'); import acm = require('@aws-cdk/aws-certificatemanager'); import cdk = require('@aws-cdk/core'); import targets = require('@aws-cdk/aws-route53-targets/lib'); import { Construct } from '@aws-cdk/core'; export interface StaticSiteProps { domainName: string; siteSubDomain: string; } /** * Static site infrastructure, which deploys site content to an S3 bucket. * * The site redirects from HTTP to HTTPS, using a CloudFront distribution, * Route53 alias record, and ACM certificate. */ export class StaticSite extends Construct { constructor(parent: Construct, name: string, props: StaticSiteProps) { super(parent, name); const zone = route53.HostedZone.fromLookup(this, 'Zone', { domainName: props.domainName, }); const siteDomain = props.siteSubDomain + '.' + props.domainName; new cdk.CfnOutput(this, 'Site', { value: 'https://' + siteDomain }); // Content bucket const siteBucket = new s3.Bucket(this, 'SiteBucket', { bucketName: siteDomain, websiteIndexDocument: 'index.html', websiteErrorDocument: 'error.html', publicReadAccess: true, // The default removal policy is RETAIN, which means that cdk destroy will not attempt to delete // the new bucket, and it will remain in your account until manually deleted. By setting the policy to // DESTROY, cdk destroy will attempt to delete the bucket, but will error if the bucket is not empty. removalPolicy: cdk.RemovalPolicy.DESTROY, // NOT recommended for production code }); new cdk.CfnOutput(this, 'Bucket', { value: siteBucket.bucketName }); // TLS certificate const certificateArn = new acm.DnsValidatedCertificate( this, 'SiteCertificate', { domainName: siteDomain, hostedZone: zone, region: 'us-east-1', // Cloudfront only checks this region for certificates. }, ).certificateArn; new cdk.CfnOutput(this, 'Certificate', { value: certificateArn }); // CloudFront distribution that provides HTTPS const distribution = new cloudfront.CloudFrontWebDistribution( this, 'SiteDistribution', { aliasConfiguration: { acmCertRef: certificateArn, names: [siteDomain], sslMethod: cloudfront.SSLMethod.SNI, securityPolicy: cloudfront.SecurityPolicyProtocol.TLS_V1_1_2016, }, originConfigs: [ { s3OriginSource: { s3BucketSource: siteBucket, }, 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, }); // Deploy site contents to S3 bucket new s3deploy.BucketDeployment(this, 'DeployWithInvalidation', { sources: [s3deploy.Source.asset('./site-contents')], destinationBucket: siteBucket, distribution, distributionPaths: ['/*'], }); } }

Resources

  1. AWS Samples - statis site