Skip to content

Instantly share code, notes, and snippets.

@micahmelling
Created August 26, 2021 02:25
Show Gist options
  • Save micahmelling/64e68c6c62eefca28a238d6c9c903953 to your computer and use it in GitHub Desktop.
Save micahmelling/64e68c6c62eefca28a238d6c9c903953 to your computer and use it in GitHub Desktop.
import mimetypes
import pulumi_aws as aws
import pulumi
from pulumi import FileAsset
from pulumi_aws import s3
def main(bucket_name, index_html_path, aliases, certificate_arn, domain_name, hosted_zone_id, ip_address):
"""
Creates a static, single-page website accessible via a Route 53 DNS and protected with SSL.
Example:
main(
bucket_name='my-s3-bucket',
index_html_path='index.html',
aliases=['python-package.mydomain.com'],
certificate_arn='arn:aws:acm:us-east-1:000000000000:certificate/oooooooooo', # found in certificate manager
domain_name='python-package.mydomain.com',
hosted_zone_id='ZZZZZZZZZZZZZZZZZZZZZZZ', # found in route 53
ip_address='0.0.0.0/32' # desired IP address to have access
)
:param bucket_name: name of the S3 bucket hosting the content.
:param index_html_path: path to index.html
:param aliases: CloudFront aliases, which must include domain_name
:param certificate_arn: ARN of the SSL cert, which must be in us-east-1
:param domain_name: domain name
:param hosted_zone_id: Route53 hosted zone ID
:param ip_address: IP address we want to have access
"""
web_bucket = s3.Bucket(bucket_name,
bucket=bucket_name,
website=s3.BucketWebsiteArgs(
index_document="index.html"
))
bucket_public_access_block = aws.s3.BucketPublicAccessBlock(f'{bucket_name}_public_access',
bucket=web_bucket.id,
block_public_acls=False,
block_public_policy=False)
mime_type, _ = mimetypes.guess_type(index_html_path)
obj = s3.BucketObject(
index_html_path,
bucket=web_bucket.id,
source=FileAsset(index_html_path),
content_type=mime_type
)
# can easily modify the ipset to accommodate multiple IP addresses
ipset = aws.waf.IpSet("ipset",
ip_set_descriptors=[aws.waf.IpSetIpSetDescriptorArgs(
type="IPV4",
value=ip_address,
)])
wafrule = aws.waf.Rule("wafrule",
metric_name="WAFRule",
predicates=[aws.waf.RulePredicateArgs(
data_id=ipset.id,
negated=False,
type="IPMatch",
)],
opts=pulumi.ResourceOptions(depends_on=[ipset]))
waf_acl = aws.waf.WebAcl("wafAcl",
metric_name="WebACL",
default_action=aws.waf.WebAclDefaultActionArgs(
type="BLOCK",
),
rules=[aws.waf.WebAclRuleArgs(
action=aws.waf.WebAclRuleActionArgs(
type="ALLOW",
),
priority=1,
rule_id=wafrule.id,
type="REGULAR",
)],
opts=pulumi.ResourceOptions(depends_on=[
ipset,
wafrule,
]))
oai = aws.cloudfront.OriginAccessIdentity(f"{bucket_name}_oai")
s3_distribution = aws.cloudfront.Distribution(
f'{bucket_name}_distribution',
origins=[aws.cloudfront.DistributionOriginArgs(
domain_name=web_bucket.bucket_regional_domain_name,
origin_id=f's3{bucket_name}_origin',
s3_origin_config=aws.cloudfront.DistributionOriginS3OriginConfigArgs(
origin_access_identity=oai.cloudfront_access_identity_path,
),
)],
enabled=True,
is_ipv6_enabled=False,
default_root_object="index.html",
aliases=aliases,
web_acl_id=waf_acl.id,
default_cache_behavior=aws.cloudfront.DistributionDefaultCacheBehaviorArgs(
allowed_methods=[
"DELETE",
"GET",
"HEAD",
"OPTIONS",
"PATCH",
"POST",
"PUT",
],
cached_methods=[
"GET",
"HEAD",
],
target_origin_id=f's3{bucket_name}_origin',
forwarded_values=aws.cloudfront.DistributionOrderedCacheBehaviorForwardedValuesArgs(
query_string=False,
headers=["Origin"],
cookies=aws.cloudfront.DistributionOrderedCacheBehaviorForwardedValuesCookiesArgs(
forward="none",
),
),
viewer_protocol_policy="redirect-to-https",
),
restrictions=aws.cloudfront.DistributionRestrictionsArgs(
geo_restriction=aws.cloudfront.DistributionRestrictionsGeoRestrictionArgs(
restriction_type="none",
),
),
viewer_certificate=aws.cloudfront.DistributionViewerCertificateArgs(
acm_certificate_arn=certificate_arn,
ssl_support_method='sni-only'
))
# you might have to wrap oai.iam_arn in an f-string on the first run and then re-run with the original way.
# pulumi is a bit finicky
source = aws.iam.get_policy_document(statements=[
aws.iam.GetPolicyDocumentStatementArgs(
actions=["s3:GetObject"],
resources=[f"arn:aws:s3:::{bucket_name}/*"],
principals=[
aws.iam.GetPolicyDocumentStatementPrincipalArgs(
type='AWS',
identifiers=[oai.iam_arn]
),
]
),
aws.iam.GetPolicyDocumentStatementArgs(
actions=["s3:GetObject"],
resources=[f"arn:aws:s3:::{bucket_name}/*"],
principals=[
aws.iam.GetPolicyDocumentStatementPrincipalArgs(
type='*',
identifiers=['*']
),
],
conditions=[
aws.iam.GetPolicyDocumentStatementConditionArgs(
test='IpAddress',
variable="aws:SourceIp",
values=[f'{ip_address}']
)
]
)
]
)
web_bucket_name = web_bucket.id
bucket_policy = s3.BucketPolicy(f"{bucket_name}_bucket-policy",
bucket=web_bucket_name,
policy=source.json)
route_53_record = aws.route53.Record(domain_name,
zone_id=hosted_zone_id,
name=domain_name,
type="A",
aliases=[aws.route53.RecordAliasArgs(
name=s3_distribution.domain_name,
zone_id=s3_distribution.hosted_zone_id,
evaluate_target_health=False,
)]
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment