def create_distribution(self): t = self.template s3_origin = cloudfront.Origin( DomainName=self.origin_bucket_url, Id="s3", CustomOriginConfig=cloudfront.CustomOrigin( HTTPPort=80, HTTPSPort=443, OriginProtocolPolicy="http-only" ), ) default_behavior = cloudfront.DefaultCacheBehavior( AllowedMethods=["GET", "HEAD"], CachedMethods=["GET", "HEAD"], ViewerProtocolPolicy="redirect-to-https", ForwardedValues=cloudfront.ForwardedValues( QueryString=True, Cookies=cloudfront.Cookies( Forward="none" ) ), MinTTL=0, MaxTTL=31536000, DefaultTTL=86400, SmoothStreaming=False, TargetOriginId="s3", ) viewer_certificate = NoValue if self.certificate_arn: viewer_certificate = cloudfront.ViewerCertificate( AcmCertificateArn=self.certificate_arn, SslSupportMethod="sni-only", ) config = cloudfront.DistributionConfig( Aliases=self.aliases, DefaultCacheBehavior=default_behavior, Comment="%s" % self.origin_bucket_url, Enabled=True, PriceClass="PriceClass_All", ViewerCertificate=viewer_certificate, Origins=[s3_origin], ) self.distribution = t.add_resource( cloudfront.Distribution( "Distribution", DistributionConfig=config, ) ) self.add_output("DistributionId", self.distribution.Ref()) self.add_output( "DomainName", self.distribution.GetAtt("DomainName") )
def add_cache_behaviors(self, title, cf_cache_behavior_config): """ Create Cloudfront CacheBehavior objects and append to list of cache_behaviors :param title: Title of this Cloudfront Distribution :param cf_cache_behavior_config: list of CFCacheBehavior """ default_cache_behavior_count = 0 cache_behavior_count = 0 for number, cache_behavior in enumerate(cf_cache_behavior_config): forwarded_values = cloudfront.ForwardedValues( Cookies=cloudfront.Cookies( Forward=cache_behavior.forward_cookies), QueryString=cache_behavior.query_string) if cache_behavior.forwarded_headers is not None: forwarded_values.Headers = cache_behavior.forwarded_headers cf_cache_behavior_params = { 'AllowedMethods': cache_behavior.allowed_methods, 'CachedMethods': cache_behavior.cached_methods, 'Compress': False, 'TargetOriginId': cache_behavior.target_origin_id, 'ForwardedValues': forwarded_values, 'TrustedSigners': cache_behavior.trusted_signers, 'ViewerProtocolPolicy': cache_behavior.viewer_protocol_policy, 'MinTTL': cache_behavior.min_ttl, 'DefaultTTL': cache_behavior.default_ttl, 'MaxTTL': cache_behavior.max_ttl, 'SmoothStreaming': False } if cache_behavior.is_default: # Add default cache behavior self.default_cache_behavior = cloudfront.DefaultCacheBehavior( '{0}DefaultCacheBehavior'.format(title), **cf_cache_behavior_params) default_cache_behavior_count += 1 else: # Append additional cache behaviors to list cf_cache_behavior_params[ 'PathPattern'] = cache_behavior.path_pattern created_cache_behavior = cloudfront.CacheBehavior( '{0}CacheBehavior{1}'.format(title, number), **cf_cache_behavior_params) self.cache_behaviors.append(created_cache_behavior) cache_behavior_count += 1 # if there is at least one cache behavior, there must be exactly one default cache behavior if cache_behavior_count > 0 and default_cache_behavior_count != 1: raise CloudfrontConfigError( 'Error: cf_distribution_unit {0} must have exactly one default cache behavior.' .format(title))
def _cookies(cookies): if cookies: return cloudfront.Cookies(Forward='whitelist', WhitelistedNames=cookies) return cloudfront.Cookies(Forward='none')
def get_cloudfront_distribution_options( self, bucket: s3.Bucket, oai: cloudfront.CloudFrontOriginAccessIdentity, lambda_function_associations: List[ cloudfront.LambdaFunctionAssociation], ) -> Dict[str, Any]: """Retrieve the options for our CloudFront distribution. Args: bucket: The bucket resource oai: The origin access identity resource. lambda_function_associations: List of Lambda Function associations. Return: The CloudFront Distribution Options. """ if os.getenv("AWS_REGION") == "us-east-1": # use global endpoint for us-east-1 origin = Join(".", [bucket.ref(), "s3.amazonaws.com"]) else: # use reginal endpoint to avoid "temporary" redirect that can last over an hour # https://forums.aws.amazon.com/message.jspa?messageID=677452 origin = Join(".", [bucket.ref(), "s3", Region, "amazonaws.com"]) return { "Aliases": self.add_aliases(), "Origins": [ cloudfront.Origin( DomainName=origin, S3OriginConfig=cloudfront. S3OriginConfig(OriginAccessIdentity=Join( "", ["origin-access-identity/cloudfront/", oai.ref()])), Id="S3Origin", ) ], "DefaultCacheBehavior": cloudfront.DefaultCacheBehavior( AllowedMethods=["GET", "HEAD"], Compress=False, DefaultTTL="86400", ForwardedValues=cloudfront.ForwardedValues( Cookies=cloudfront.Cookies(Forward="none"), QueryString=False), LambdaFunctionAssociations=lambda_function_associations, TargetOriginId="S3Origin", ViewerProtocolPolicy="redirect-to-https", ), "DefaultRootObject": "index.html", "Logging": self.add_logging_bucket(), "PriceClass": self.variables["PriceClass"], "CustomErrorResponses": [ cloudfront.CustomErrorResponse( ErrorCode=response["ErrorCode"], ResponseCode=response["ResponseCode"], ResponsePagePath=response["ResponsePagePath"], ) for response in self.variables["custom_error_responses"] ], "Enabled": True, "WebACLId": self.add_web_acl(), "ViewerCertificate": self.add_acm_cert(), }
def create_template(self): """Create template (main function called by Stacker).""" template = self.template variables = self.get_variables() template.set_version('2010-09-09') template.set_description('Static Website - Bucket and Distribution') # Conditions template.add_condition( 'AcmCertSpecified', And(Not(Equals(variables['AcmCertificateArn'].ref, '')), Not(Equals(variables['AcmCertificateArn'].ref, 'undefined'))) ) template.add_condition( 'AliasesSpecified', And(Not(Equals(Select(0, variables['Aliases'].ref), '')), Not(Equals(Select(0, variables['Aliases'].ref), 'undefined'))) ) template.add_condition( 'CFLoggingEnabled', And(Not(Equals(variables['LogBucketName'].ref, '')), Not(Equals(variables['LogBucketName'].ref, 'undefined'))) ) template.add_condition( 'DirectoryIndexSpecified', And(Not(Equals(variables['RewriteDirectoryIndex'].ref, '')), Not(Equals(variables['RewriteDirectoryIndex'].ref, 'undefined'))) # noqa ) template.add_condition( 'WAFNameSpecified', And(Not(Equals(variables['WAFWebACL'].ref, '')), Not(Equals(variables['WAFWebACL'].ref, 'undefined'))) ) # Resources oai = template.add_resource( cloudfront.CloudFrontOriginAccessIdentity( 'OAI', CloudFrontOriginAccessIdentityConfig=cloudfront.CloudFrontOriginAccessIdentityConfig( # noqa pylint: disable=line-too-long Comment='CF access to website' ) ) ) bucket = template.add_resource( s3.Bucket( 'Bucket', AccessControl=s3.Private, LifecycleConfiguration=s3.LifecycleConfiguration( Rules=[ s3.LifecycleRule( NoncurrentVersionExpirationInDays=90, Status='Enabled' ) ] ), VersioningConfiguration=s3.VersioningConfiguration( Status='Enabled' ), WebsiteConfiguration=s3.WebsiteConfiguration( IndexDocument='index.html', ErrorDocument='error.html' ) ) ) template.add_output(Output( 'BucketName', Description='Name of website bucket', Value=bucket.ref() )) allowcfaccess = template.add_resource( s3.BucketPolicy( 'AllowCFAccess', Bucket=bucket.ref(), PolicyDocument=PolicyDocument( Version='2012-10-17', Statement=[ Statement( Action=[awacs.s3.GetObject], Effect=Allow, Principal=Principal( 'CanonicalUser', oai.get_att('S3CanonicalUserId') ), Resource=[ Join('', [bucket.get_att('Arn'), '/*']) ] ) ] ) ) ) cfdirectoryindexrewriterole = template.add_resource( iam.Role( 'CFDirectoryIndexRewriteRole', Condition='DirectoryIndexSpecified', AssumeRolePolicyDocument=PolicyDocument( Version='2012-10-17', Statement=[ Statement( Effect=Allow, Action=[awacs.sts.AssumeRole], Principal=Principal('Service', ['lambda.amazonaws.com', 'edgelambda.amazonaws.com']) ) ] ), ManagedPolicyArns=[ IAM_ARN_PREFIX + 'AWSLambdaBasicExecutionRole' ] ) ) cfdirectoryindexrewrite = template.add_resource( awslambda.Function( 'CFDirectoryIndexRewrite', Condition='DirectoryIndexSpecified', Code=awslambda.Code( ZipFile=Join( '', ["'use strict';\n", "exports.handler = (event, context, callback) => {\n", "\n", " // Extract the request from the CloudFront event that is sent to Lambda@Edge\n", # noqa pylint: disable=line-too-long " var request = event.Records[0].cf.request;\n", " // Extract the URI from the request\n", " var olduri = request.uri;\n", " // Match any '/' that occurs at the end of a URI. Replace it with a default index\n", # noqa pylint: disable=line-too-long " var newuri = olduri.replace(/\\/$/, '\\/", variables['RewriteDirectoryIndex'].ref, "');\n", # noqa " // Log the URI as received by CloudFront and the new URI to be used to fetch from origin\n", # noqa pylint: disable=line-too-long " console.log(\"Old URI: \" + olduri);\n", " console.log(\"New URI: \" + newuri);\n", " // Replace the received URI with the URI that includes the index page\n", # noqa pylint: disable=line-too-long " request.uri = newuri;\n", " // Return to CloudFront\n", " return callback(null, request);\n", "\n", "};\n"] ) ), Description='Rewrites CF directory HTTP requests to default page', # noqa Handler='index.handler', Role=cfdirectoryindexrewriterole.get_att('Arn'), Runtime='nodejs8.10' ) ) # Generating a unique resource name here for the Lambda version, so it # updates automatically if the lambda code changes code_hash = hashlib.md5( str(cfdirectoryindexrewrite.properties['Code'].properties['ZipFile'].to_dict()).encode() # noqa pylint: disable=line-too-long ).hexdigest() cfdirectoryindexrewritever = template.add_resource( awslambda.Version( 'CFDirectoryIndexRewriteVer' + code_hash, Condition='DirectoryIndexSpecified', FunctionName=cfdirectoryindexrewrite.ref() ) ) # If custom associations defined, use them if variables['lambda_function_associations']: lambda_function_associations = [ cloudfront.LambdaFunctionAssociation( EventType=x['type'], LambdaFunctionARN=x['arn'] ) for x in variables['lambda_function_associations'] ] else: # otherwise fallback to pure CFN condition lambda_function_associations = If( 'DirectoryIndexSpecified', [cloudfront.LambdaFunctionAssociation( EventType='origin-request', LambdaFunctionARN=cfdirectoryindexrewritever.ref() )], NoValue ) cfdistribution = template.add_resource( get_cf_distribution_class()( 'CFDistribution', DependsOn=allowcfaccess.title, DistributionConfig=get_cf_distro_conf_class()( Aliases=If( 'AliasesSpecified', variables['Aliases'].ref, NoValue ), Origins=[ get_cf_origin_class()( DomainName=Join( '.', [bucket.ref(), 's3.amazonaws.com']), S3OriginConfig=get_s3_origin_conf_class()( OriginAccessIdentity=Join( '', ['origin-access-identity/cloudfront/', oai.ref()]) ), Id='S3Origin' ) ], DefaultCacheBehavior=cloudfront.DefaultCacheBehavior( AllowedMethods=['GET', 'HEAD'], Compress=False, DefaultTTL='86400', ForwardedValues=cloudfront.ForwardedValues( Cookies=cloudfront.Cookies(Forward='none'), QueryString=False, ), LambdaFunctionAssociations=lambda_function_associations, # noqa TargetOriginId='S3Origin', ViewerProtocolPolicy='redirect-to-https' ), DefaultRootObject='index.html', Logging=If( 'CFLoggingEnabled', cloudfront.Logging( Bucket=Join('.', [variables['LogBucketName'].ref, 's3.amazonaws.com']) ), NoValue ), PriceClass=variables['PriceClass'].ref, Enabled=True, WebACLId=If( 'WAFNameSpecified', variables['WAFWebACL'].ref, NoValue ), ViewerCertificate=If( 'AcmCertSpecified', cloudfront.ViewerCertificate( AcmCertificateArn=variables['AcmCertificateArn'].ref, # noqa SslSupportMethod='sni-only' ), NoValue ) ) ) ) template.add_output(Output( 'CFDistributionId', Description='CloudFront distribution ID', Value=cfdistribution.ref() )) template.add_output( Output( 'CFDistributionDomainName', Description='CloudFront distribution domain name', Value=cfdistribution.get_att('DomainName') ) )
# has the Lambda-function associated. ], DefaultCacheBehavior=cloudfront.DefaultCacheBehavior( ViewerProtocolPolicy= 'redirect-to-https', # HTTPS required. Cookies need to be sent securely LambdaFunctionAssociations=[ cloudfront.LambdaFunctionAssociation( EventType='viewer-request', LambdaFunctionARN=Ref(param_authorizer_lae_arn)), ], # Rest of config as per your needs TargetOriginId='ExampleS3', ForwardedValues=cloudfront.ForwardedValues( QueryString=True, Cookies=cloudfront.Cookies( Forward= 'all', # Don't do this. Done here to validate cookie-removal logic ), ), ), ViewerCertificate=cloudfront.ViewerCertificate( AcmCertificateArn=Ref(acm_cert), SslSupportMethod='sni-only', ), ), Tags=GetAtt(cloudformation_tags, 'TagList'), )) hosted_zone_map = "HostedZoneMap" template.add_mapping(hosted_zone_map, cfnutils.mappings.r53_hosted_zone_id()) template.add_resource(
def get_cloudfront_distribution_options( self, # type: StaticSite bucket, # type: s3.Bucket oai, # type: cloudfront.CloudFrontOriginAccessIdentity # noqa pylint: disable=line-too-long lambda_function_associations # type: List[cloudfront.LambdaFunctionAssociation] # noqa pylint: disable=line-too-long # TODO remove after dropping python 2 ): # pylint: disable=bad-continuation # type: (...) -> Dict[str, Any] """Retrieve the options for our CloudFront distribution. Keyword Args: bucket (dict): The bucket resource oai (dict): The origin access identity resource lambda_function_associations (array): The lambda function association array Return: dict: The CloudFront Distribution Options """ variables = self.get_variables() return { 'Aliases': self.add_aliases(), 'Origins': [ cloudfront.Origin( DomainName=Join('.', [bucket.ref(), 's3.amazonaws.com']), S3OriginConfig=cloudfront. S3OriginConfig(OriginAccessIdentity=Join( '', ['origin-access-identity/cloudfront/', oai.ref()])), Id='S3Origin') ], 'DefaultCacheBehavior': cloudfront.DefaultCacheBehavior( AllowedMethods=['GET', 'HEAD'], Compress=False, DefaultTTL='86400', ForwardedValues=cloudfront.ForwardedValues( Cookies=cloudfront.Cookies(Forward='none'), QueryString=False, ), LambdaFunctionAssociations=lambda_function_associations, TargetOriginId='S3Origin', ViewerProtocolPolicy='redirect-to-https'), 'DefaultRootObject': 'index.html', 'Logging': self.add_logging_bucket(), 'PriceClass': variables['PriceClass'], 'CustomErrorResponses': [ cloudfront.CustomErrorResponse( ErrorCode=response['ErrorCode'], ResponseCode=response['ResponseCode'], ResponsePagePath=response['ResponsePagePath']) for response in variables['custom_error_responses'] ], 'Enabled': True, 'WebACLId': self.add_web_acl(), 'ViewerCertificate': self.add_acm_cert() }
Default='/media/*', Description='Specify which requests you want to route to the origin.', Type='String')) # endregion # region Resources distribution = template.add_resource( cloudfront.Distribution( 'Distribution', DistributionConfig=cloudfront.DistributionConfig( Aliases=[Ref(domain)], CacheBehaviors=[ cloudfront.CacheBehavior( Compress=True, ForwardedValues=cloudfront.ForwardedValues( Cookies=cloudfront.Cookies(Forward='none'), QueryString=False, ), PathPattern=Ref(media_pattern), TargetOriginId=Sub( '${domain}${path}', **{ 'domain': Ref(media_domain), 'path': Ref(media_path) }), ViewerProtocolPolicy='redirect-to-https', ) ], Comment=Sub('${AWS::StackName}'), DefaultCacheBehavior=cloudfront.DefaultCacheBehavior( Compress=True, ForwardedValues=cloudfront.ForwardedValues(
def cloudfront_distribution(self): if self.vars["AcmCertificateARN"]: viewer_certificate = cf.ViewerCertificate( SslSupportMethod="sni-only", MinimumProtocolVersion="TLSv1", AcmCertificateArn=self.vars["AcmCertificateARN"], ) url_prefix = 'https://' else: viewer_certificate = NoValue url_prefix = 'http://' t = self.template self.SiteCFDistribution = t.add_resource( cf.Distribution( "SiteCFDistribution", DistributionConfig=cf.DistributionConfig( Comment="S3 Distribution", Logging=cf.Logging( Prefix=self.vars["FQDNPublic"] + "/cloudfront_logs/", Bucket=self.vars["LogBucket"] + ".s3.amazonaws.com", IncludeCookies="false"), WebACLId=self.vars["WebACLId"], Origins=[ cf.Origin( S3OriginConfig=cf.S3Origin(OriginAccessIdentity=( "origin-access-identity/cloudfront/" + self.vars["OriginAccessIdentity"]), ), Id="myS3Origin", DomainName=GetAtt(self.SiteBucket, "DomainName"), OriginPath=self.vars["OriginPath"], ) ], DefaultRootObject=self.vars["DefaultRootObject"], PriceClass="PriceClass_100", Enabled="true", DefaultCacheBehavior=cf.DefaultCacheBehavior( ViewerProtocolPolicy="redirect-to-https", ForwardedValues=cf.ForwardedValues( Cookies=cf.Cookies(Forward="none"), QueryString="true"), TargetOriginId="myS3Origin", DefaultTTL=self.vars["DefaultTTL"], ), Aliases=[self.vars["FQDNPublic"]], ViewerCertificate=viewer_certificate, ), )) CloudFrontDistribution = t.add_output( Output( "CloudFrontDistribution", Description="Cloudfront distribution domainname in AWS", Value=GetAtt(self.SiteCFDistribution, "DomainName"), )) WebsiteURL = t.add_output( Output( "WebsiteURL", Description="Public URL of cloudfront hosted website", Value=url_prefix + self.vars["FQDNPublic"], ))
def __init__(self, title, key, **kwargs): super().__init__(title, **kwargs) name = self.title if 'AllowedMethods' in key: self.AllowedMethods = get_endvalue(f'{name}AllowedMethods') if 'CachedMethods' in key: self.CachedMethods = get_endvalue(f'{name}CachedMethods') # If not defined default to True if 'Compress' in key: self.Compress = get_endvalue(f'{name}Compress') else: self.Compress = True if 'DefaultTTL' in key: self.DefaultTTL = get_endvalue(f'{name}DefaultTTL') self.ForwardedValues = clf.ForwardedValues() # If not defined default to True if 'QueryString' in key: self.ForwardedValues.QueryString = get_endvalue( f'{name}QueryString') else: self.ForwardedValues.QueryString = True if 'CookiesForward' in key: self.ForwardedValues.Cookies = clf.Cookies( Forward=get_endvalue(f'{name}CookiesForward')) if 'CookiesWhitelistedNames' in key: self.ForwardedValues.Cookies.WhitelistedNames = (get_endvalue( f'{name}CookiesWhitelistedNames', condition=True)) # conditions c_CookiesForward = get_condition( f'{name}CookiesWhitelistedNames', 'equals', 'whitelist', f'{name}CookiesForward') add_obj(c_CookiesForward) # If not defined default to 'Host' if 'Headers' in key: self.ForwardedValues.Headers = get_endvalue(f'{name}Headers') else: self.ForwardedValues.Headers = ['Host'] if 'QueryStringCacheKeys' in key: self.ForwardedValues.QueryStringCacheKeys = get_endvalue( f'{name}QueryStringCacheKeys', condition=True) # conditions c_QueryString = get_condition(f'{name}QueryStringCacheKeys', 'equals', True, f'{name}QueryString') add_obj(c_QueryString) if 'TargetOriginId' in key: self.TargetOriginId = get_endvalue(f'{name}TargetOriginId') else: self.TargetOriginId = If( 'CloudFrontOriginAdHoc', Ref('RecordSetExternal'), Sub('${EnvRole}${RecordSetCloudFrontSuffix}.origin.%s' % cfg.HostedZoneNameEnv)) if 'MaxTTL' in key: self.MaxTTL = get_endvalue(f'{name}MaxTTL') if 'MinTTL' in key: self.MinTTL = get_endvalue(f'{name}MinTTL') if 'LambdaFunctionARN' in key: condname = f'{name}LambdaFunctionARN' eventType = 'origin-request' if 'LambdaEventType' in key: eventType = key['LambdaEventType'] # conditions add_obj(get_condition(condname, 'not_equals', 'None')) self.LambdaFunctionAssociations = [ If( condname, clf.LambdaFunctionAssociation( EventType=eventType, LambdaFunctionARN=get_endvalue(condname)), Ref('AWS::NoValue')) ] if 'ViewerProtocolPolicy' in key: self.ViewerProtocolPolicy = get_endvalue( f'{name}ViewerProtocolPolicy') else: self.ViewerProtocolPolicy = 'redirect-to-https'
def render_cloudfront(context, template, origin_hostname): if not context['cloudfront']['origins']: ensure( context['full_hostname'], "A public hostname is required to be pointed at by the Cloudfront CDN" ) allowed_cnames = context['cloudfront']['subdomains'] + context[ 'cloudfront']['subdomains-without-dns'] if context['cloudfront']['cookies']: cookies = cloudfront.Cookies( Forward='whitelist', WhitelistedNames=context['cloudfront']['cookies']) else: cookies = cloudfront.Cookies(Forward='none') if context['cloudfront']['origins']: origins = [ cloudfront.Origin(DomainName=o['hostname'], Id=o_id, CustomOriginConfig=cloudfront.CustomOrigin( HTTPSPort=443, OriginProtocolPolicy='https-only')) for o_id, o in context['cloudfront']['origins'].items() ] origin = origins[0].Id else: origin = CLOUDFRONT_TITLE + 'Origin' origins = [ cloudfront.Origin(DomainName=origin_hostname, Id=origin, CustomOriginConfig=cloudfront.CustomOrigin( HTTPSPort=443, OriginProtocolPolicy='https-only')) ] props = { 'Aliases': allowed_cnames, 'DefaultCacheBehavior': cloudfront.DefaultCacheBehavior( AllowedMethods=[ 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT' ], CachedMethods=['GET', 'HEAD'], Compress=context['cloudfront']['compress'], DefaultTTL=context['cloudfront']['default-ttl'], TargetOriginId=origin, ForwardedValues=cloudfront.ForwardedValues( Cookies=cookies, Headers=context['cloudfront'] ['headers'], # 'whitelisted' headers QueryString=True), ViewerProtocolPolicy='redirect-to-https', ), 'Enabled': True, 'HttpVersion': 'http2', 'Origins': origins, 'ViewerCertificate': cloudfront.ViewerCertificate( IamCertificateId=context['cloudfront']['certificate_id'], SslSupportMethod='sni-only') } def _cache_behavior(origin_id, pattern): return cloudfront.CacheBehavior( TargetOriginId=origin_id, DefaultTTL=context['cloudfront']['default-ttl'], ForwardedValues=cloudfront.ForwardedValues(QueryString=False), PathPattern=pattern, ViewerProtocolPolicy='allow-all', ) if context['cloudfront']['errors']: props['Origins'].append( cloudfront.Origin( DomainName=context['cloudfront']['errors']['domain'], # TODO: constant Id=CLOUDFRONT_ERROR_ORIGIN_ID, # no advantage in using cloudfront.S3Origin for public buckets CustomOriginConfig=cloudfront.CustomOrigin( HTTPSPort=443, OriginProtocolPolicy='https-only' if context['cloudfront']['errors']['protocol'] == 'https' else 'http-only'))) props['CacheBehaviors'] = [ _cache_behavior( CLOUDFRONT_ERROR_ORIGIN_ID, context['cloudfront']['errors']['pattern'], ) ] props['CustomErrorResponses'] = [ cloudfront.CustomErrorResponse(ErrorCode=code, ResponseCode=code, ResponsePagePath=page) for code, page in context['cloudfront']['errors']['codes'].items() ] if context['cloudfront']['origins']: props['CacheBehaviors'] = [ _cache_behavior(o_id, o['pattern']) for o_id, o in context['cloudfront']['origins'].items() if o['pattern'] ] template.add_resource( cloudfront.Distribution( CLOUDFRONT_TITLE, DistributionConfig=cloudfront.DistributionConfig(**props))) for dns in external_dns_cloudfront(context): template.add_resource(dns)
def create_template(self): """Create template (main function called by Stacker).""" template = self.template variables = self.get_variables() template.add_version('2010-09-09') template.add_description('Static Website - Bucket and Distribution') # Conditions template.add_condition( 'AcmCertSpecified', And(Not(Equals(variables['AcmCertificateArn'].ref, '')), Not(Equals(variables['AcmCertificateArn'].ref, 'undefined')))) template.add_condition( 'AliasesSpecified', And(Not(Equals(Select(0, variables['Aliases'].ref), '')), Not(Equals(Select(0, variables['Aliases'].ref), 'undefined')))) template.add_condition( 'CFLoggingEnabled', And(Not(Equals(variables['LogBucketName'].ref, '')), Not(Equals(variables['LogBucketName'].ref, 'undefined')))) template.add_condition( 'WAFNameSpecified', And(Not(Equals(variables['WAFWebACL'].ref, '')), Not(Equals(variables['WAFWebACL'].ref, 'undefined')))) # Resources oai = template.add_resource( cloudfront.CloudFrontOriginAccessIdentity( 'OAI', CloudFrontOriginAccessIdentityConfig=cloudfront. CloudFrontOriginAccessIdentityConfig( # noqa pylint: disable=line-too-long Comment='CF access to website'))) bucket = template.add_resource( s3.Bucket( 'Bucket', AccessControl=s3.Private, LifecycleConfiguration=s3.LifecycleConfiguration(Rules=[ s3.LifecycleRule(NoncurrentVersionExpirationInDays=90, Status='Enabled') ]), VersioningConfiguration=s3.VersioningConfiguration( Status='Enabled'), WebsiteConfiguration=s3.WebsiteConfiguration( IndexDocument='index.html', ErrorDocument='error.html'))) template.add_output( Output('BucketName', Description='Name of website bucket', Value=bucket.ref())) allowcfaccess = template.add_resource( s3.BucketPolicy( 'AllowCFAccess', Bucket=bucket.ref(), PolicyDocument=Policy( Version='2012-10-17', Statement=[ Statement( Action=[awacs.s3.GetObject], Effect=Allow, Principal=Principal( 'CanonicalUser', oai.get_att('S3CanonicalUserId')), Resource=[Join('', [bucket.get_att('Arn'), '/*'])]) ]))) cfdistribution = template.add_resource( cloudfront.Distribution( 'CFDistribution', DependsOn=allowcfaccess.title, DistributionConfig=cloudfront.DistributionConfig( Aliases=If('AliasesSpecified', variables['Aliases'].ref, NoValue), Origins=[ cloudfront.Origin( DomainName=Join( '.', [bucket.ref(), 's3.amazonaws.com']), S3OriginConfig=cloudfront.S3Origin( OriginAccessIdentity=Join( '', [ 'origin-access-identity/cloudfront/', oai.ref() ])), Id='S3Origin') ], DefaultCacheBehavior=cloudfront.DefaultCacheBehavior( AllowedMethods=['GET', 'HEAD'], Compress=False, DefaultTTL='86400', ForwardedValues=cloudfront.ForwardedValues( Cookies=cloudfront.Cookies(Forward='none'), QueryString=False, ), TargetOriginId='S3Origin', ViewerProtocolPolicy='redirect-to-https'), DefaultRootObject='index.html', Logging=If( 'CFLoggingEnabled', cloudfront.Logging(Bucket=Join('.', [ variables['LogBucketName'].ref, 's3.amazonaws.com' ])), NoValue), PriceClass=variables['PriceClass'].ref, Enabled=True, WebACLId=If('WAFNameSpecified', variables['WAFWebACL'].ref, NoValue), ViewerCertificate=If( 'AcmCertSpecified', cloudfront.ViewerCertificate( AcmCertificateArn=variables['AcmCertificateArn']. ref, # noqa SslSupportMethod='sni-only'), NoValue)))) template.add_output( Output('CFDistributionId', Description='CloudFront distribution ID', Value=cfdistribution.ref())) template.add_output( Output('CFDistributionDomainName', Description='CloudFront distribution domain name', Value=cfdistribution.get_att('DomainName')))
def get_cloudfront_distribution_options( self, bucket, # type: s3.Bucket oai, # type: cloudfront.CloudFrontOriginAccessIdentity lambda_function_associations, # type: List[cloudfront.LambdaFunctionAssociation] ): # type: (...) -> Dict[str, Any] """Retrieve the options for our CloudFront distribution. Args: bucket: The bucket resource oai: The origin access identity resource. lambda_function_associations: List of Lambda Function associations. Return: The CloudFront Distribution Options. """ variables = self.get_variables() return { "Aliases": self.add_aliases(), "Origins": [ cloudfront.Origin( DomainName=Join(".", [bucket.ref(), "s3.amazonaws.com"]), S3OriginConfig=cloudfront. S3OriginConfig(OriginAccessIdentity=Join( "", ["origin-access-identity/cloudfront/", oai.ref()])), Id="S3Origin", ) ], "DefaultCacheBehavior": cloudfront.DefaultCacheBehavior( AllowedMethods=["GET", "HEAD"], Compress=False, DefaultTTL="86400", ForwardedValues=cloudfront.ForwardedValues( Cookies=cloudfront.Cookies(Forward="none"), QueryString=False, ), LambdaFunctionAssociations=lambda_function_associations, TargetOriginId="S3Origin", ViewerProtocolPolicy="redirect-to-https", ), "DefaultRootObject": "index.html", "Logging": self.add_logging_bucket(), "PriceClass": variables["PriceClass"], "CustomErrorResponses": [ cloudfront.CustomErrorResponse( ErrorCode=response["ErrorCode"], ResponseCode=response["ResponseCode"], ResponsePagePath=response["ResponsePagePath"], ) for response in variables["custom_error_responses"] ], "Enabled": True, "WebACLId": self.add_web_acl(), "ViewerCertificate": self.add_acm_cert(), }
cloudfront.CacheBehavior( # The authorizer hijacks a set of URL-paths from your website. All paths are prefixed # with `/auth-<UUID>/`, so they are very unlikely to collide with your content. # Insert this as the first Behaviour. PathPattern=Join('', [ ImportValue( Sub('${' + authorizer_stack.title + '}-magic-path')), '/*', ]), ViewerProtocolPolicy='https-only', TargetOriginId='Authorizer', ForwardedValues=cloudfront.ForwardedValues( QueryString=True, Cookies=cloudfront.Cookies( Forward='all', # Needed to allow Set-Cookie:-headers ), ), MinTTL=0, DefaultTTL=0, MaxTTL=0, ) ], DefaultCacheBehavior=cloudfront.DefaultCacheBehavior( ViewerProtocolPolicy= 'redirect-to-https', # HTTPS required. Cookies need to be sent securely LambdaFunctionAssociations=[ cloudfront.LambdaFunctionAssociation( EventType='viewer-request', LambdaFunctionARN=Ref(param_authorizer_lae_arn)), ],