This file contains 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 { 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