1+ import json
2+ import mimetypes
3+ import os
4+
5+ from pulumi import export , FileAsset
6+ from pulumi_aws import s3 , route53 , acm , cloudfront
7+
8+ import pulumi
9+
10+ config = pulumi .Config ('proj1' ) # proj1 is project name defined in Pulumi.yaml
11+
12+ content_dir = config .require ('local_webdir' ) # www-staging or www-prod
13+ domain_name = config .require ('domain_name' ) # staging.devops4all.dev or www.devops4all.dev
14+ dns_zone_id = config .require ('dns_zone_id' )
15+
16+ web_bucket = s3 .Bucket ('s3-website-bucket' , website = {
17+ "index_document" : "index.html"
18+ })
19+
20+ for file in os .listdir (content_dir ):
21+ filepath = os .path .join (content_dir , file )
22+ mime_type , _ = mimetypes .guess_type (filepath )
23+ obj = s3 .BucketObject (file ,
24+ bucket = web_bucket .id ,
25+ source = FileAsset (filepath ),
26+ content_type = mime_type )
27+
28+ def public_read_policy_for_bucket (bucket_name ):
29+ return json .dumps ({
30+ "Version" : "2012-10-17" ,
31+ "Statement" : [{
32+ "Effect" : "Allow" ,
33+ "Principal" : "*" ,
34+ "Action" : [
35+ "s3:GetObject"
36+ ],
37+ "Resource" : [
38+ f"arn:aws:s3:::{ bucket_name } /*" ,
39+ ]
40+ }]
41+ })
42+
43+ web_bucket_id = web_bucket .id
44+ web_bucket_policy = s3 .BucketPolicy ("bucket-policy" ,
45+ bucket = web_bucket_id ,
46+ policy = web_bucket_id .apply (public_read_policy_for_bucket ))
47+
48+
49+ # Split a domain name into its subdomain and parent domain names.
50+ # e.g. "www.example.com" => "www", "example.com".
51+ def get_domain_and_subdomain (domain ):
52+ names = domain .split ("." )
53+ if len (names ) < 3 :
54+ return ('' , domain )
55+ subdomain = names [0 ]
56+ parent_domain = "." .join (names [1 :])
57+ return (subdomain , parent_domain )
58+
59+ (subdomain , parent_domain ) = get_domain_and_subdomain (domain_name )
60+ # zone = route53.Zone("route53_zone", name=parent_domain)
61+
62+ # create ACM certificate
63+ cert = acm .Certificate ('certificate' , domain_name = domain_name , validation_method = 'DNS' )
64+ domain_validation_options = cert .domain_validation_options [0 ]
65+
66+ # Create a DNS record to prove that we _own_ the domain we're requesting a certificate for.
67+ cert_validation_dns_record = route53 .Record (
68+ 'cert-validation-record' ,
69+ name = domain_validation_options ['resourceRecordName' ],
70+ zone_id = dns_zone_id ,
71+ type = domain_validation_options ['resourceRecordType' ],
72+ records = [domain_validation_options ['resourceRecordValue' ]],
73+ ttl = 600 )
74+
75+ # This is a _special_ resource that waits for ACM to complete validation via the DNS record
76+ # checking for a status of "ISSUED" on the certificate itself. No actual resources are
77+ # created (or updated or deleted).
78+ cert_validation_completion = acm .CertificateValidation ('cert-validation-completion' ,
79+ certificate_arn = cert .arn ,
80+ validation_record_fqdns = [cert_validation_dns_record .fqdn ])
81+
82+ cert_arn = cert_validation_completion .certificate_arn
83+
84+ # Create S3 bucket that will contain the CDN's request logs.
85+ log_bucket = s3 .Bucket ('cdn-log-bucket' , acl = 'private' )
86+
87+ # Create CloudFront distribution pointing to web S3 bucket
88+ cloudfront_distro = cloudfront .Distribution ( 'cloudfront-distro' ,
89+ enabled = True ,
90+ aliases = [ domain_name ],
91+ origins = [
92+ {
93+ 'originId' : web_bucket .arn ,
94+ 'domainName' : web_bucket .website_endpoint ,
95+ 'customOriginConfig' : {
96+ 'originProtocolPolicy' : "http-only" ,
97+ 'httpPort' : 80 ,
98+ 'httpsPort' : 443 ,
99+ 'originSslProtocols' : ["TLSv1.2" ],
100+ },
101+ },
102+ ],
103+ default_root_object = "index.html" ,
104+ default_cache_behavior = {
105+ 'targetOriginId' : web_bucket .arn ,
106+ 'viewerProtocolPolicy' : "redirect-to-https" ,
107+ 'allowedMethods' : ["GET" , "HEAD" , "OPTIONS" ],
108+ 'cachedMethods' : ["GET" , "HEAD" , "OPTIONS" ],
109+ 'forwardedValues' : {
110+ 'cookies' : { 'forward' : "none" },
111+ 'queryString' : False ,
112+ },
113+ 'minTtl' : 0 ,
114+ 'defaultTtl' : 600 ,
115+ 'maxTtl' : 600 ,
116+ },
117+ price_class = "PriceClass_100" ,
118+ custom_error_responses = [
119+ { 'errorCode' : 404 , 'responseCode' : 404 , 'responsePagePath' : "/404.html" },
120+ ],
121+ restrictions = {
122+ 'geoRestriction' : {
123+ 'restrictionType' : "none" ,
124+ },
125+ },
126+ viewer_certificate = {
127+ 'acmCertificateArn' : cert_arn ,
128+ 'sslSupportMethod' : "sni-only" ,
129+ },
130+ logging_config = {
131+ 'bucket' : log_bucket .bucket_domain_name ,
132+ 'includeCookies' : False ,
133+ 'prefix' : domain_name ,
134+ })
135+
136+
137+ # Create DNS record for the deployed site pointing to CloudFront DNS name
138+ site_dns_record = route53 .Record (
139+ 'site-dns-record' ,
140+ name = subdomain ,
141+ zone_id = dns_zone_id ,
142+ type = "A" ,
143+ aliases = [
144+ {
145+ 'name' : cloudfront_distro .domain_name ,
146+ 'zoneId' : cloudfront_distro .hosted_zone_id ,
147+ 'evaluateTargetHealth' : True
148+ }
149+ ])
150+
151+ #export('domain_validation_options', domain_validation_options)
152+ export ('web_bucket_id' , web_bucket .id )
153+ export ('log_bucket_id' , log_bucket .id )
154+ export ('website_url' , web_bucket .website_endpoint )
155+ export ('cloudfront_domain' , cloudfront_distro .domain_name )
0 commit comments