Skip to content

Instantly share code, notes, and snippets.

@Pibotasnus
Last active January 25, 2022 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pibotasnus/954075c4f4a2a1576ba398fe75aa2cf7 to your computer and use it in GitHub Desktop.
Save Pibotasnus/954075c4f4a2a1576ba398fe75aa2cf7 to your computer and use it in GitHub Desktop.
import { Stack, StackProps, CfnOutput, RemovalPolicy } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as s3Deploy from 'aws-cdk-lib/aws-s3-deployment';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as route53 from 'aws-cdk-lib/aws-route53';
import { DnsValidatedCertificate } from 'aws-cdk-lib/aws-certificatemanager';
import { CloudFrontTarget } from 'aws-cdk-lib/aws-route53-targets';
interface IStackProps extends StackProps {
subDomain: string;
domainName: string;
}
class staticWebSiteStack extends Stack {
constructor(scope: Construct, id: string, props: IStackProps) {
super(scope, id, props);
new cfConstruct(this, id, props);
}
}
class cfConstruct extends Construct {
constructor(scope: Stack, id: string, props: IStackProps) {
super(scope, id);
const webSiteBucket = new s3.Bucket(this, 'myWebSiteBucket', {
publicReadAccess: false,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
websiteIndexDocument: 'index.html',
websiteErrorDocument: '404.html',
});
new CfnOutput(this, 'Bucket', { value: webSiteBucket.bucketName });
const zone = route53.HostedZone.fromLookup(this, 'domainNameZone', {
domainName: props.domainName,
});
// Create a certificate for our website and validate it
const certificateTLSArn = new DnsValidatedCertificate(this, 'SiteTLSCert', {
domainName: `${props.subDomain}.${props.domainName}`,
hostedZone: zone,
region: 'us-east-1',
}).certificateArn;
const cloudfrontOIA = new cloudfront.OriginAccessIdentity(
this,
'cloudfrontOia'
);
webSiteBucket.addToResourcePolicy(
new iam.PolicyStatement({
actions: ['s3:GetObject'],
resources: [webSiteBucket.arnForObjects('*')],
principals: [
new iam.CanonicalUserPrincipal(
cloudfrontOIA.cloudFrontOriginAccessIdentityS3CanonicalUserId
),
],
})
);
new CfnOutput(this, 'CertificateARNSite', { value: certificateTLSArn });
// Attach the previous certificate to the CloudFront viewer certificate to activate HTTPS
const viewerCertificate = cloudfront.ViewerCertificate.fromAcmCertificate(
{
certificateArn: certificateTLSArn,
env: { account: props.env!.account!, region: props.env!.region! },
node: this.node,
stack: scope,
metricDaysToExpiry: () =>
new cloudwatch.Metric({
namespace: 'TLS Viewer Certificate Validity',
metricName: 'TLS Viewer Certificate Expired',
}),
applyRemovalPolicy: () => RemovalPolicy.DESTROY,
},
{
sslMethod: cloudfront.SSLMethod.SNI,
securityPolicy: cloudfront.SecurityPolicyProtocol.TLS_V1_1_2016,
aliases: [`${props.subDomain}.${props.domainName}`],
}
);
// Configure the distribution to use the CloudFront viewer certificate
const distribution = new cloudfront.CloudFrontWebDistribution(
this,
'siteDist',
{
viewerCertificate,
originConfigs: [
{
s3OriginSource: {
s3BucketSource: webSiteBucket,
originAccessIdentity: cloudfrontOIA,
},
behaviors: [
{
isDefaultBehavior: true,
compress: true,
allowedMethods:
cloudfront.CloudFrontAllowedMethods.GET_HEAD_OPTIONS,
},
],
},
],
}
);
new CfnOutput(this, 'DistributionId', {
value: distribution.distributionId,
});
// Add an A record to redirect traffic to CloudFront distribution
new route53.ARecord(this, 'siteRecord', {
recordName: `${props.subDomain}.${props.domainName}`,
target: route53.RecordTarget.fromAlias(
new CloudFrontTarget(distribution)
),
zone: zone,
});
new s3Deploy.BucketDeployment(this, 'myWebsiteBucketDeployement', {
sources: [s3Deploy.Source.asset('../website-source/public')],
destinationBucket: webSiteBucket,
distribution: distribution,
distributionPaths: ['/*'],
});
}
}
export class InfraStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new staticWebSiteStack(this, 'staticWebsiteStack', {
subDomain: this.node.tryGetContext('subdomain'),
domainName: this.node.tryGetContext('domain'),
env: {
account: this.node.tryGetContext('accountId'),
region: this.node.tryGetContext('region'),
},
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment