def render_s3(context, template): for bucket_name in context['s3']: props = { 'DeletionPolicy': context['s3'][bucket_name]['deletion-policy'].capitalize(), 'Tags': s3.Tags(**aws.generic_tags(context, name=False)), } bucket_title = _sanitize_title(bucket_name) + "Bucket" if context['s3'][bucket_name]['cors']: # generic configuration for allowing read-only access props['CorsConfiguration'] = s3.CorsConfiguration(CorsRules=[ s3.CorsRules(AllowedHeaders=['*'], AllowedMethods=['GET', 'HEAD'], AllowedOrigins=['*']) ]) if context['s3'][bucket_name]['website-configuration']: index_document = context['s3'][bucket_name][ 'website-configuration'].get('index-document', 'index.html') props['WebsiteConfiguration'] = s3.WebsiteConfiguration( IndexDocument=index_document) _add_bucket_policy(template, bucket_title, bucket_name) if context['s3'][bucket_name]['public']: _add_bucket_policy(template, bucket_title, bucket_name) props['AccessControl'] = s3.PublicRead if context['s3'][bucket_name]['encryption']: props['BucketEncryption'] = _bucket_kms_encryption( context['s3'][bucket_name]['encryption']) template.add_resource( s3.Bucket(bucket_title, BucketName=bucket_name, **props))
def add_bucket(self, name, access_control, static_site, route53, public_hosted_zone): """ Helper method creates a directory service resource @param name [string] Fully qualified name for the bucket (corp.example.com) @param access_control [string] type of access control for the bucket @param static_site [boolean] should the bucket host a static site @param route53 [boolean] create a route53 entry? """ if route53: self.add_dns_alias(name, "s3-website-us-east-1.amazonaws.com", "Z3AQBSTGFYJSTF", public_hosted_zone) if access_control == "PublicRead": policy = s3.BucketPolicy(name.replace('.', '') + "BucketPolicy", Bucket=name, PolicyDocument={ "Statement": [{ "Sid": "PublicReadForGetBucketObjects", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::%s/*" % name }] }) self.add_resource(policy) bucket = s3.Bucket( name.replace('.', '') + "Bucket", BucketName=name, AccessControl=access_control, ) if static_site: web_config = s3.WebsiteConfiguration(IndexDocument='index.html') bucket.properties['WebsiteConfiguration'] = web_config return self.add_resource(bucket)
def render_s3(context, template): for bucket_name in context['s3']: props = { 'DeletionPolicy': context['s3'][bucket_name]['deletion-policy'].capitalize() } bucket_title = _sanitize_title(bucket_name) + "Bucket" if context['s3'][bucket_name]['cors']: # generic configuration for allowing read-only access props['CorsConfiguration'] = s3.CorsConfiguration(CorsRules=[ s3.CorsRules(AllowedHeaders=['*'], AllowedMethods=['GET', 'HEAD'], AllowedOrigins=['*']) ]) if context['s3'][bucket_name]['website-configuration']: index_document = context['s3'][bucket_name][ 'website-configuration'].get('index-document', 'index.html') props['WebsiteConfiguration'] = s3.WebsiteConfiguration( IndexDocument=index_document) template.add_resource( s3.BucketPolicy("%sPolicy" % bucket_title, Bucket=bucket_name, PolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Sid": "AddPerm", "Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::%s/*" % bucket_name] }] })) template.add_resource( s3.Bucket(bucket_title, BucketName=bucket_name, **props))
def S3_Buckets(key): global bucket_name for n, v in getattr(cfg, key).items(): resname = f"{key}{n}" name = n bucket_name = getattr(cfg, f"{key}Name{n}") ## Policy Read PolicyReadConditions = [] PolicyReadPrincipal = [] for m, w in v["AccountsRead"].items(): accountread_name = f"{resname}AccountsRead{m}" # conditions add_obj(get_condition(accountread_name, "not_equals", "none")) PolicyReadConditions.append(Condition(accountread_name)) PolicyReadPrincipal.append( If( accountread_name, get_subvalue("arn:aws:iam::${1M}:root", accountread_name), Ref("AWS::NoValue"), )) # conditions if PolicyReadConditions: c_PolicyRead = { f"{resname}PolicyRead": Or(Equals("1", "0"), Equals("1", "0"), *PolicyReadConditions) } else: c_PolicyRead = {f"{resname}PolicyRead": Equals("True", "False")} ## Policy Write PolicyWriteConditions = [] PolicyWritePrincipal = [] for m, w in v["AccountsWrite"].items(): accountwrite_name = f"{resname}AccountsWrite{m}" # conditions add_obj(get_condition(accountwrite_name, "not_equals", "none")) PolicyWriteConditions.append(Condition(accountwrite_name)) PolicyWritePrincipal.append( If( accountwrite_name, get_subvalue("arn:aws:iam::${1M}:root", accountwrite_name), Ref("AWS::NoValue"), )) # conditions if PolicyWriteConditions: c_PolicyWrite = { f"{resname}PolicyWrite": Or(Equals("1", "0"), Equals("1", "0"), *PolicyWriteConditions) } else: c_PolicyWrite = {f"{resname}PolicyWrite": Equals("True", "False")} ## Policy Delete PolicyDeleteConditions = [] PolicyDeletePrincipal = [] for m, w in v["AccountsDelete"].items(): accountwrite_name = f"{resname}AccountsDelete{m}" # conditions add_obj(get_condition(accountwrite_name, "not_equals", "none")) PolicyDeleteConditions.append(Condition(accountwrite_name)) PolicyDeletePrincipal.append( If( accountwrite_name, get_subvalue("arn:aws:iam::${1M}:root", accountwrite_name), Ref("AWS::NoValue"), )) # conditions if PolicyDeleteConditions: c_PolicyDelete = { f"{resname}PolicyDelete": Or(Equals("1", "0"), Equals("1", "0"), *PolicyDeleteConditions) } else: c_PolicyDelete = { f"{resname}PolicyDelete": Equals("True", "False") } c_Create = get_condition(resname, "equals", "yes", f"{resname}Create") c_Versioning = get_condition(f"{resname}Versioning", "equals", "Enabled") c_Cors = get_condition(f"{resname}Cors", "equals", "yes") c_Replica = { f"{resname}Replica": And( Condition(resname), get_condition("", "equals", "yes", f"{resname}ReplicationEnabled"), ) } c_ReplicaPolicyStatement = get_condition( f"{resname}PolicyStatementReplicaPrincipal", "not_equals", "none") add_obj([ c_PolicyRead, c_PolicyWrite, c_PolicyDelete, c_Create, c_Versioning, c_Cors, c_Replica, c_ReplicaPolicyStatement, ]) # resources r_Bucket = S3Bucket(resname, key=v) Replica_Rules = [] for m, w in v["Replication"]["ConfigurationRules"].items(): replica_name = f"{resname}ReplicationConfigurationRules{m}" # parameters p_replicabucket = Parameter( f"{replica_name}DestinationBucket", Description= "Replica Destination Bucket - empty for default based on Env/Roles/Region", ) add_obj(p_replicabucket) # conditions add_obj([ get_condition(f"{replica_name}DestinationBucket", "not_equals", "none") ]) # resources rule = s3.ReplicationConfigurationRules(replica_name, Status="Enabled") auto_get_props(rule) Replica_Rules.append( If(f"{replica_name}DestinationBucket", rule, Ref("AWS::NoValue"))) if Replica_Rules: ReplicationConfiguration = s3.ReplicationConfiguration( "", Role=GetAtt(f"Role{resname}Replica", "Arn"), Rules=Replica_Rules) r_Bucket.ReplicationConfiguration = If(f"{resname}Replica", ReplicationConfiguration, Ref("AWS::NoValue")) PolicyStatementReplicaResources = [] for m, w in v["PolicyStatementReplica"]["Resource"].items(): polstatname = f"{resname}PolicyStatementReplicaResource{m}" # conditions add_obj(get_condition(f"{polstatname}Prefix", "not_equals", "none")) PolicyStatementReplicaResources.append( If( f"{polstatname}Prefix", get_subvalue("arn:aws:s3:::%s/${1M}*" % bucket_name, f"{polstatname}Prefix"), Ref("AWS::NoValue"), )) BucketPolicyStatement = [] r_Policy = S3BucketPolicy( f"BucketPolicy{name}", key=v, Condition=resname, Bucket=Ref(resname), ) r_Policy.PolicyDocument["Statement"] = BucketPolicyStatement BucketPolicyStatement.extend([ # At least one statement must be always present, create a simple one with no conditions S3BucketPolicyStatementBase(resname), S3BucketPolicyStatementReplica(resname, PolicyStatementReplicaResources), S3BucketPolicyStatementRead(resname, PolicyReadPrincipal), S3BucketPolicyStatementWrite(resname, PolicyWritePrincipal), S3BucketPolicyStatementDelete(resname, PolicyDeletePrincipal), ]) r_IAMPolicyReplica = IAMPolicyBucketReplica( f"IAMPolicyReplicaBucket{name}", bucket=resname, bucket_name=bucket_name, mapname=f"{resname}ReplicationConfigurationRules", key=v["Replication"]["ConfigurationRules"], ) try: bucket_policies = getattr(cfg, "BucketPolicy") except Exception: pass else: for policy_name, policy_value in bucket_policies.items(): BucketPolicyStatement.append(get_dictvalue(policy_value)) if "WebsiteConfiguration" in v: r_Bucket.WebsiteConfiguration = s3.WebsiteConfiguration( f"{resname}WebsiteConfiguration") auto_get_props(r_Bucket.WebsiteConfiguration) if "PolicyStatement" in v: FixedStatements = [] for fsn, fsv in v["PolicyStatement"].items(): FixedStatement = IAMPolicyStatement(fsv) FixedStatement["Principal"] = {"AWS": eval(fsv["Principal"])} FixedStatement["Sid"] = fsv["Sid"] FixedStatements.append(FixedStatement) BucketPolicyStatement.extend(FixedStatements) if "PolicyStatementExGetObjectPrincipal" in v: BucketPolicyStatement.append( S3BucketPolicyStatementAllowGetObject( resname, get_endvalue( f"{resname}PolicyStatementExGetObjectPrincipal"), "AllowGetObjectExPrincipal", )) PolicyCloudFrontOriginAccessIdentityPrincipal = [] if "CloudFrontOriginAccessIdentity" in v: identityname = v["CloudFrontOriginAccessIdentity"] identityresname = f"CloudFrontOriginAccessIdentity{identityname}" PolicyCloudFrontOriginAccessIdentityPrincipal.append( Sub("arn:aws:iam::cloudfront:user/" "CloudFront Origin Access Identity ${%s}" % identityresname)) for ixn, ixv in v["CloudFrontOriginAccessIdentityExtra"].items(): ixname = f"{resname}CloudFrontOriginAccessIdentityExtra{ixn}" # conditions add_obj(get_condition(ixname, "not_equals", "none")) PolicyCloudFrontOriginAccessIdentityPrincipal.append( If( ixname, get_subvalue( "arn:aws:iam::cloudfront:user/" "CloudFront Origin Access Identity ${1M}", ixname, ), Ref("AWS::NoValue"), )) # conditions identitycondname = f"{resname}CloudFrontOriginAccessIdentity" c_identity = { identitycondname: And( Condition(resname), get_condition("", "not_equals", "none", identitycondname), ) } add_obj(c_identity) # resources BucketPolicyStatement.append( S3BucketPolicyStatementAllowGetObject( resname, PolicyCloudFrontOriginAccessIdentityPrincipal, "AllowCFAccess", )) r_OriginAccessIdentity = CFOriginAccessIdentity( identityresname, comment=identityname, Condition=identitycondname) add_obj(r_OriginAccessIdentity) # outputs o_OriginAccessIdentity = Output(identityresname, Value=Ref(identityresname), Condition=identitycondname) add_obj(o_OriginAccessIdentity) add_obj([r_Bucket, r_Policy, r_IAMPolicyReplica]) # outputs o_Bucket = Output(resname) outvalue = If(resname, Ref(resname), Sub(bucket_name)) if "OutputValueRegion" in v: condname = f"{resname}OutputValueRegion" # conditions add_obj(get_condition(condname, "not_equals", "AWSRegion")) outvalue = If( condname, Sub( "${Region}-%s" % bucket_name.replace("${AWS::Region}-", "", 1), **{"Region": get_endvalue(condname)}, ), outvalue, ) o_Bucket.Export = Export(resname) else: o_Bucket.Condition = resname o_Bucket.Value = outvalue add_obj([o_Bucket])
from troposphere import Template, Output, GetAtt import troposphere.s3 as s3 t = Template() s3bucket = t.add_resource(s3.Bucket( 'S3Bucket', AccessControl=s3.PublicRead, WebsiteConfiguration=s3.WebsiteConfiguration( IndexDocument='index.html', ErrorDocument='error.html' ) )) t.add_output( Output( 'WebsiteURL', Value=GetAtt(s3bucket, 'WebsiteURL'), Description='URL for website hosted on S3' ) ) print(t.to_yaml())
)) error_page = template.add_parameter( Parameter( 'WebsitePageError', Type=c.STRING, Default='error.html', Description='Error page for your site', )) root_bucket = template.add_resource( s3.Bucket('RootBucket', AccessControl=s3.PublicRead, BucketName=Ref(domain), WebsiteConfiguration=s3.WebsiteConfiguration( IndexDocument=Ref(index_page), ErrorDocument=Ref(error_page), ))) root_bucket_arn = Join('', ['arn:aws:s3:::', Ref(root_bucket), '/*']) template.add_resource( s3.BucketPolicy('RootBucketPolicy', Bucket=Ref(root_bucket), PolicyDocument={ 'Statement': [{ 'Action': ['s3:GetObject'], 'Effect': 'Allow', 'Resource': root_bucket_arn, 'Principal': '*', }] }))
def generate_cf_template(): """ Returns an entire CloudFormation stack by using troposphere to construct each piece """ # Header of CloudFormation template t = Template() t.add_version("2010-09-09") t.add_description("Lambda Chat AWS Resources") # Paramters description = "should match [0-9]+-[a-z0-9]+.apps.googleusercontent.com" google_oauth_client_id = t.add_parameter(Parameter( "GoogleOAuthClientID", AllowedPattern="[0-9]+-[a-z0-9]+.apps.googleusercontent.com", Type="String", Description="The Client ID of your Google project", ConstraintDescription=description )) website_s3_bucket_name = t.add_parameter(Parameter( "WebsiteS3BucketName", AllowedPattern="[a-zA-Z0-9\-]*", Type="String", Description="Name of S3 bucket to store the website in", ConstraintDescription="can contain only alphanumeric characters and dashes.", )) # The SNS topic the website will publish chat messages to website_sns_topic = t.add_resource(sns.Topic( 'WebsiteSnsTopic', TopicName='lambda-chat', DisplayName='Lambda Chat' )) t.add_output(Output( "WebsiteSnsTopic", Description="sns_topic_arn", Value=Ref(website_sns_topic), )) # The IAM Role and Policy the website will assume to publish to SNS website_role = t.add_resource(iam.Role( "WebsiteRole", Path="/", AssumeRolePolicyDocument=Policy( Statement=[ Statement( Effect=Allow, Action=[Action("sts", "AssumeRoleWithWebIdentity")], Principal=Principal("Federated", "accounts.google.com"), Condition=Condition( StringEquals( "accounts.google.com:aud", Ref(google_oauth_client_id) ) ), ), ], ), )) t.add_resource(iam.PolicyType( "WebsitePolicy", PolicyName="lambda-chat-website-policy", Roles=[Ref(website_role)], PolicyDocument=Policy( Version="2012-10-17", Statement=[ Statement( Effect=Allow, Action=[Action("sns", "Publish")], Resource=[ Ref(website_sns_topic) ], ), ], ) )) t.add_output(Output( "WebsiteRole", Description="website_iam_role_arn", Value=GetAtt(website_role, "Arn"), )) website_bucket = t.add_resource(s3.Bucket( 'WebsiteS3Bucket', BucketName=Ref(website_s3_bucket_name), WebsiteConfiguration=s3.WebsiteConfiguration( ErrorDocument="error.html", IndexDocument="index.html" ) )) t.add_output(Output( "S3Bucket", Description="s3_bucket", Value=Ref(website_bucket), )) t.add_resource(s3.BucketPolicy( 'WebsiteS3BucketPolicy', Bucket=Ref(website_bucket), PolicyDocument={ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicAccess", "Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], "Resource": [{ "Fn::Join": [ "", [ "arn:aws:s3:::", { "Ref": "WebsiteS3Bucket", }, "/*" ] ] }] } ] } )) return t
Type='String', Default='error.html', Description='The name of the error document for the website.')) # # Resource # bucket = t.add_resource( s3.Bucket( 'Bucket', BucketName=Join('.', [Ref(param_domain_name), Ref(param_hosted_domain)]), WebsiteConfiguration=s3.WebsiteConfiguration( IndexDocument=Ref(param_index_doc), ErrorDocument=Ref(param_error_doc), ), )) bucket_policy = t.add_resource( s3.BucketPolicy( 'BucketPolicy', Bucket=Ref(bucket), PolicyDocument={ 'Version': '2012-10-17', 'Statement': [{ 'Sid': 'PublicReadGetObject', 'Effect': 'Allow', 'Principal': '*', 'Action': ['s3:GetObject'],
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( '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())) 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, ), LambdaFunctionAssociations=If( 'DirectoryIndexSpecified', [ cloudfront.LambdaFunctionAssociation( EventType='origin-request', LambdaFunctionARN=cfdirectoryindexrewritever .ref() # noqa ) ], NoValue), 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 _build_template(self, template): t = template s3b = t.add_resource(s3.Bucket(self.name)) if self.public_read: s3b.AccessControl = s3.PublicRead t.add_resource( s3.BucketPolicy('{}BucketPolicy'.format(self.name), Bucket=Ref(s3b), PolicyDocument={ "Statement": [{ "Action": ["s3:GetObject"], "Effect": "Allow", "Resource": Join('', ["arn:aws:s3:::", Ref(s3b), "/*"]), "Principal": "*" }] })) versioning = "Suspended" if self.versioning: versioning = "Enabled" s3b.VersioningConfiguration = s3.VersioningConfiguration( Status=versioning) if self.website_mode: s3b.WebsiteConfiguration = s3.WebsiteConfiguration( **self.website_config) if self.cors_enabled is True \ and len(self.cors_rules) <= 0: self.add_cors_rule("CorsAll", ['*'], ['GET', 'POST', 'PUT'], ['*'], 3000) if len(self.cors_rules) > 0: cors = s3.CorsConfiguration(CorsRules=self.cors_rules) s3b.CorsConfiguration = cors if len(self.lifecycle_rules) > 0: s3b.LifecycleConfiguration = s3.LifecycleConfiguration(Rules=[]) for lcr in self.lifecycle_rules: s3b.LifecycleConfiguration.Rules.append(lcr) t.add_output([ Output("{}BucketName".format(self.name), Value=Ref(s3b), Description="{} Bucket Name".format(self.name)), Output("{}BucketUrl".format(self.name), Value=GetAtt(s3b, "DomainName"), Description="{} Bucket Name".format(self.name)), Output('{}WebsiteUrl'.format(self.name), Value=GetAtt(s3b, 'WebsiteURL')) ]) return s3b
def __init__(self, key): global bucket_name for n, v in getattr(cfg, key).items(): resname = f'{key}{n}' name = n if not ('Enabled' in v and v['Enabled'] is True): continue bucket_name = getattr(cfg, resname) # parameters p_ReplicaDstRegion = Parameter(f'{resname}ReplicaDstRegion') p_ReplicaDstRegion.Description = ( 'Region to Replicate Bucket - None to disable - ' 'empty for default based on env/role') p_ReplicaDstRegion.AllowedValues = ['', 'None'] + cfg.regions add_obj(p_ReplicaDstRegion) PolicyReadConditions = [] PolicyReadPrincipal = [] for m, w in v['AccountsRead'].items(): accountread_name = f'{resname}AccountsRead{m}' # conditions add_obj(get_condition(accountread_name, 'not_equals', 'None')) PolicyReadConditions.append(Condition(accountread_name)) PolicyReadPrincipal.append( If( accountread_name, get_subvalue('arn:aws:iam::${1M}:root', accountread_name), Ref('AWS::NoValue'))) # conditions if PolicyReadConditions: c_PolicyRead = { f'{resname}PolicyRead': Or(Equals('1', '0'), Equals('1', '0'), *PolicyReadConditions) } else: c_PolicyRead = { f'{resname}PolicyRead': Equals('True', 'False') } PolicyWriteConditions = [] PolicyWritePrincipal = [] for m, w in v['AccountsWrite'].items(): accountwrite_name = f'{resname}AccountsWrite{m}' # conditions add_obj(get_condition(accountwrite_name, 'not_equals', 'None')) PolicyWriteConditions.append(Condition(accountwrite_name)) PolicyWritePrincipal.append( If( accountwrite_name, get_subvalue('arn:aws:iam::${1M}:root', accountwrite_name), Ref('AWS::NoValue'))) # conditions if PolicyWriteConditions: c_PolicyWrite = { f'{resname}PolicyWrite': Or(Equals('1', '0'), Equals('1', '0'), *PolicyWriteConditions) } else: c_PolicyWrite = { f'{resname}PolicyWrite': Equals('True', 'False') } c_Create = get_condition(resname, 'not_equals', 'None', f'{resname}Create') c_Versioning = get_condition(f'{resname}Versioning', 'not_equals', 'None') c_Cors = get_condition(f'{resname}Cors', 'not_equals', 'None') c_ReplicaSrcAccount = get_condition(f'{resname}ReplicaSrcAccount', 'not_equals', 'None') c_ReplicaDstOwner = get_condition(f'{resname}ReplicaDstOwner', 'not_equals', 'None') # c_AccountRO = get_condition( # f'{resname}AccountRO', 'not_equals', 'None') c_Replica = { f'{resname}Replica': And( Condition(resname), get_condition('', 'not_equals', 'None', f'{resname}ReplicaDstRegion')) } add_obj([ c_PolicyRead, c_PolicyWrite, c_Create, c_Versioning, c_Cors, c_ReplicaSrcAccount, c_ReplicaDstOwner, c_Replica, ]) # resources BucketPolicyStatement = [] r_Bucket = S3Bucket(resname, key=v) r_Policy = S3BucketPolicy(f'BucketPolicy{name}', key=v) r_Policy.Condition = resname r_Policy.Bucket = Sub(bucket_name) r_Policy.PolicyDocument['Statement'] = BucketPolicyStatement # At least one statement must be always present, # create a simple one with no conditions BucketPolicyStatement.extend(S3BucketPolicyStatementBase(resname)) BucketPolicyStatement.extend( S3BucketPolicyStatementReplica(resname, v)) r_Role = IAMRoleBucketReplica(f'Role{resname}Replica') BucketPolicyStatement.extend( S3BucketPolicyStatementRead(resname, PolicyReadPrincipal)) BucketPolicyStatement.extend( S3BucketPolicyStatementWrite(resname, PolicyWritePrincipal)) r_IAMPolicyReplica = IAMPolicyBucketReplica( f'IAMPolicyReplicaBucket{name}', bucket=resname, bucket_name=bucket_name, key=v) try: lambda_arn = eval( v['NotificationConfiguration']['LambdaConfigurations'] ['Trigger']['Function']) except: pass else: if 'Fn::GettAtt' in lambda_arn.data: permname = '%s%s' % ( lambda_arn.data['Fn::GettAtt'].replace( 'Lambda', 'LambdaPermission'), resname) else: permname = 'LambdaPermission' r_LambdaPermission = LambdaPermissionS3(permname, key=lambda_arn, source=resname) add_obj(r_LambdaPermission) BucketPolicyStatement.extend( S3BucketPolicyStatementSes(resname)) if 'WebsiteConfiguration' in v: r_Bucket.WebsiteConfiguration = s3.WebsiteConfiguration( f'{resname}WebsiteConfiguration') auto_get_props(r_Bucket.WebsiteConfiguration, v['WebsiteConfiguration'], recurse=True) if 'PolicyStatement' in v: FixedStatements = [] for fsn, fsv in v['PolicyStatement'].items(): FixedStatement = IAMPolicyStatement(fsv) FixedStatement['Principal'] = { 'AWS': eval(fsv['Principal']) } FixedStatement['Sid'] = fsv['Sid'] FixedStatements.append(FixedStatement) BucketPolicyStatement.extend(FixedStatements) PolicyCloudFrontOriginAccessIdentityPrincipal = [] if 'CloudFrontOriginAccessIdentity' in v: identityname = v['CloudFrontOriginAccessIdentity'] identityresname = ( f'CloudFrontOriginAccessIdentity{identityname}') PolicyCloudFrontOriginAccessIdentityPrincipal.append( Sub('arn:aws:iam::cloudfront:user/' 'CloudFront Origin Access Identity ${%s}' % identityresname)) for ixn, ixv in ( v['CloudFrontOriginAccessIdentityExtra'].items()): ixname = ( f'{resname}CloudFrontOriginAccessIdentityExtra{ixn}') # conditions add_obj(get_condition(ixname, 'not_equals', 'None')) PolicyCloudFrontOriginAccessIdentityPrincipal.append( If( ixname, get_subvalue( 'arn:aws:iam::cloudfront:user/' 'CloudFront Origin Access Identity ${1M}', ixname), Ref('AWS::NoValue'))) # conditions identitycondname = f'{resname}CloudFrontOriginAccessIdentity' c_identity = get_condition(identitycondname, 'not_equals', 'None') add_obj(c_identity) # resources BucketPolicyStatement.extend( S3BucketPolicyStatementCFOriginAccessIdentity( resname, PolicyCloudFrontOriginAccessIdentityPrincipal)) r_OriginAccessIdentity = CFOriginAccessIdentity( identityresname, comment=identityname) r_OriginAccessIdentity.Condition = identitycondname add_obj([ r_OriginAccessIdentity, ]) # outputs o_OriginAccessIdentity = Output(identityresname) o_OriginAccessIdentity.Value = Ref(identityresname) o_OriginAccessIdentity.Condition = identitycondname add_obj(o_OriginAccessIdentity) add_obj([ r_Bucket, r_Policy, # r_PolicyReplica, # r_PolicyRO, r_IAMPolicyReplica, r_Role ]) # outputs outvaluebase = Sub(bucket_name) if 'OutputValueRegion' in v: condname = f'{resname}OutputValueRegion' # conditions add_obj(get_condition(condname, 'not_equals', 'AWSRegion')) outvaluebase = If( condname, Sub( '${Region}-%s' % bucket_name.replace('${AWS::Region}-', '', 1), **{'Region': get_endvalue(condname)}), outvaluebase) o_Bucket = Output(resname) o_Bucket.Value = If(resname, Ref(resname), outvaluebase) if resname == 'BucketAppRepository': o_Bucket.Export = Export(resname) add_obj([ o_Bucket, ])
bucket_name = template.add_parameter( Parameter( "WebsiteBucketName", Description="Name for the website asset bucket", Type="String", ), ) website_bucket = template.add_resource( s3.Bucket( "WebsiteBucket", BucketName=Ref(bucket_name), AccessControl="PublicRead", VersioningConfiguration=s3.VersioningConfiguration(Status="Enabled"), DeletionPolicy="Retain", WebsiteConfiguration=s3.WebsiteConfiguration( IndexDocument="index.html", ), )) website_bucket_policy = template.add_resource( s3.BucketPolicy( "WebsiteBucketPolicy", PolicyDocument=dict(Statement=dict( Effect="Allow", Principal="*", Action=["s3:GetObject"], Resource=Join( "", ["arn:aws:s3:::", Ref(website_bucket), "/*"]), ), ), Bucket=Ref(website_bucket), ))
'zone_name': 'trentnielsen.me', 'redirect_target': 'resume.trentnielsen.me', 'alt_sub': 'www', }, } for src_domain, domain_info in redirect_domains.items(): bucketResourceName = "{}0{}0Bucket".format(app_group_ansi, src_domain.replace('.', '0')) redirectBucket = t.add_resource( Bucket(bucketResourceName, BucketName='{}'.format(src_domain), Tags=DefaultTags + Tags(Component='{}'.format(src_domain)), WebsiteConfiguration=(s3.WebsiteConfiguration( IndexDocument='index.html')))) # Set some cdn based values and defaults dns_domain = domain_info['zone_name'] cdn_domain = '{}'.format(src_domain) max_ttl = 31536000, default_ttl = 86400, # If an alt_sub domain is not specified use empty string if domain_info['alt_sub'] != '': alternate_name = '{}.{}'.format(domain_info['alt_sub'], src_domain) else: alternate_name = '' # Provision certificate for CDN cdnCertificate = t.add_resource(
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')))