Git Product home page Git Product logo

terraform-website-s3-cloudfront-route53's Introduction

Terraform setup for S3 static site with CloudFront, Certificate Manager and Route53

This Git repository contains the required Terraform scripts to setup a static website, hosted out of an S3 bucket. The site is fronted by a CloudFront distribution, uses AWS Certificate Manager for HTTPS and allows for configuring the required DNS entries in Route53.

The scripts also take care of:

  • Preventing the origin bucket being indexed by search bots.
  • Redirect other domains to the main site with proper rewriting.
  • Access logging
  • Redirect HTTP to HTTPS

These scripts suite my needs, but all evolution in the form of pull requests are welcome! To make this process fluent, create an issue first describing what you want to contribute, then fork and create a branch with a clear name. Submit your work as a pull request.

Introduction

This repository is split in 4 parts, each of which can be used as a separate module in your own root script. The split is done because of the lack of conditional logic in Terraform 0.6.x. I leave the composition of the required setup to you, the user.

  • site-main: setup of the main S3 bucket with a CloudFront distribution
  • site-redirect: setup of the redirect S3 bucket with a CloudFront distribution
  • r53-cname: configuration of a Route53 CNAME record pointing to a CloudFront distribution
  • r53-alias: configuration of a Route53 ALIAS record pointing to a CloudFront distribution. Required for naked domain (APEX) setups.

With the above 4 modules, you can pick yourself what you need for setups like:

Given the ease of setting up SSL secured sites with AWS Certificate Manager, the above modules do not offer the option to set up non-SSL sites. But since AWS Certificate Manager requires manual intervention to complete the certificate setup, you must create your certificates first before using the modules below.

Note: AWS Certificate Manager supports multiple regions. To use CloudFront with ACM certificates, the certificates must be requested in region us-east-1.

Configuring Terraform

The different modules do not define variables for the AWS provider. For ease of use, the configuration is done implicitly by setting the following environment variables:

  • AWS_SECRET_ACCESS_KEY
  • AWS_ACCESS_KEY_ID
  • AWS_DEFAULT_REGION

These variables are inherited by any Terraform modules and prevents passing too much TF variables from parent to module. This info was found here.

Setting up the main site

Creating all the resources for an S3 based static website, with a CloudFront distribution and using the appropriate SSL certificates is as easy as using the site-main module and passing the appropriate variables:

module "site-main" {
   source = "github.com/skyscrapers/terraform-website-s3-cloudfront-route53//site-main"

   region = "eu-west-1"
   domain = "my.domain.com"
   bucket_name = "site_mydomain"
   duplicate-content-penalty-secret = "some-secret-password"
   deployer = "an-iam-username"
   acm-certificate-arn = "arn:aws:acm:us-east-1:<id>:certificate/<cert-id>"
   not-found-response-path = "/404.html"
}

Mention the double slash. This is to indicate to look into the subdirectory within the Github repository. See the Terraform Modules documentation for more info.

Inputs

  • region: the AWS region where the S3 bucket will be created. The source bucket can be created in any of the available regions. The default value is us-east-1.
  • domain: the domain name by which you want to make the website available on the Internet. While we are not at the point of setting up the DNS part, the CloudFront distribution needs to know for which domain it needs to accept requests.
  • bucket_name: the name of the bucket to create for the S3 based static website.
  • duplicate-content-penalty-secret: Value that will be used in a custom header for a CloudFront distribution to gain access to the origin S3 bucket. If you make an S3 bucket available as the source for a CloudFront distribution, you have the risk of search bots to index both this source bucket and the distribution. Google punishes you for this as you can read in this article. We need to protect access to the source bucket. There are 2 options to prevent this: using an Origin Access User between CloudFront distribution and the source S3 bucket, or using custom headers between the distribution and the bucket. The use of an Origin Access User prescribes accessing the source bucket in REST mode which results in bucket redirects not being followed. As a result, this module will use the custom header option.
  • acm-certificate-arn: the id of an certificate in AWS Certificate Manager. As this certificate will be used on a CloudFront distribution, Amazon's documentation states the certificate must be generated in the us-east-1 region.
  • deployer: (Optional) the name of an existing IAM user that will be used to push contents to the S3 bucket. This user will get a role policy attached to it, configured to have read/write access to the bucket that will be created.
  • default-root-object: (Optional) default root object to be served by CloudFront. Defaults to index.html, but can be e.x. v1.0.0/index.html for versioned applications.
  • not-found-response-path: response path for the file that should be served on 404. Default to /404.html, but can be e.x. /index.html for single page applications.
  • not-found-response-code: response code when serving a 404 page. Defaults to 200.
  • trusted_signers: (Optional) List of AWS account IDs that are allowed to create signed URLs for this distribution. May contain self to indicate the account where the distribution is created in.
  • tags: (Optional) Additional key/value pairs to set as tags.
  • forward-query-string: (Optional) Forward the query string to the origin. Default value = false
  • price_class: (Optional) The price class that corresponds with the maximum price that you want to pay for CloudFront service. Read pricing page for more details. Options: PriceClass_100 | PriceClass_200 | PriceClass_All. Default value = PriceClass_200
  • ipv6: (Optional) Enable IPv6 support on CloudFront distribution. Default value = false
  • minimum_client_tls_protocol_version: (Optional) Set the minimum protocol version of the CloudFront certificate. Read the docs on Supported Protocols and Ciphers for supported values. Default value = TLSv1

Outputs

  • website_cdn_hostname: the Amazon generated Cloudfront domain name. You can already test accessing your website content by this hostname. This hostname is needed later on to create a CNAME record in Route53.
  • website_cdn_zone_id: the Hosted Zone ID of the Cloudfront distribution. This zone ID is needed later on to create a Route53 ALIAS record.
  • website_bucket_id: The website bucket id
  • website_bucket_arn: The website bucket arn
  • website_cdn_id: The CDN ID of the Cloudfront distribution.
  • website_cdn_arn: The ARN of the CDN

Setting up the redirect site

module "site-redirect" {
   source = "github.com/skyscrapers/terraform-website-s3-cloudfront-route53//site-redirect"
   region = "eu-west-1"
   domain = "my.domain.com"
   target = "domain.com"
   duplicate-content-penalty-secret = "some-secret-password"
   deployer = "an-iam-username"
   acm-certificate-arn = "arn:aws:acm:us-east-1:<id>:certificate/<cert-id>"
}

Inputs

  • tags: (Optional) Additional key/value pairs to set as tags.
  • default_root_object: (Optional) The object that you want CloudFront to return (for example, index.html) when an end user requests the root URL. Default value = index.html
  • price_class: (Optional) The price class that corresponds with the maximum price that you want to pay for CloudFront service. Read pricing page for more details. Options: PriceClass_100 | PriceClass_200 | PriceClass_All. Default value = PriceClass_200
  • ipv6: (Optional) Enable IPv6 support on CloudFront distribution. Default value = false
  • minimum_client_tls_protocol_version: (Optional) Set the minimum protocol version of the CloudFront certificate. Read the docs on Supported Protocols and Ciphers for supported values. Default value = TLSv1

Outputs

  • website_cdn_hostname: the Amazon generated Cloudfront domain name. You can already test accessing your website content by this hostname. This hostname is needed later on to create a CNAME record in Route53.
  • website_cdn_zone_id: the Hosted Zone ID of the Cloudfront distribution. This zone ID is needed later on to create a Route53 ALIAS record.

Setting up the Route 53 CNAME

Whether it is a main site or a redirect site, a CNAME DNS record is needed for your site to be accessed on a non-root domain.

module "dns-cname" {
   source = "github.com/skyscrapers/terraform-website-s3-cloudfront-route53//r53-cname"

   domain = "my.domain.com"
   target = "${module.site-main.website_cdn_hostname}"
   route53_zone_id = "<r53-zone-id>"
}

Inputs

  • domain: the domain name you want to use to access your static website. This should match the domain name used in setting up either a main or a redirect site.
  • target: the domain name of the CloudFront distribution to which the domain name should point. You usually pass the website_cdn_hostname output variable from the main or redirect site here.
  • route53_zone_id: the Route53 Zone ID where the CNAME entry must be created.

Setting up the Route 53 ALIAS

Whether it is a main site or a redirect site, an ALIAS DNS record is needed for your site to be accessed on a root domain.

module "dns-alias" {
   source = "github.com/skyscrapers/terraform-website-s3-cloudfront-route53//r53-alias"

   domain = "domain.com"
   target = "${module.site-main.website_cdn_hostname}"
   cdn_hosted_zone_id = "${module.site-main.website_cdn_zone_id}"
   route53_zone_id = "<r53-zone-id>"
}

Inputs

  • domain: the domain name you want to use to access your static website. This should match the domain name used in setting up either a main or a redirect site.
  • target: the domain name of the CloudFront distribution to which the domain name should point. You usually pass the website_cdn_hostname output variable from the main or redirect site here.
  • cdn_hosted_zone_id: the Hosted Zone ID of the CloudFront distribution. You usually pass the website_cdn_zone_id output variable from the main or redirect site here.
  • route53_zone_id: the Route53 Zone ID where the CNAME entry must be created.

Users

If you are using the modules in this Git repository to set up your static site and you want some visibility, add your site and info below and submit a pull request:

Enjoy!

terraform-website-s3-cloudfront-route53's People

Contributors

arjitj2 avatar bhgames avatar dchesterton avatar diosmosis avatar gaell avatar iuriaranda avatar jakubczarniecki avatar jerr0328 avatar madebydavid avatar mattiasgees avatar mojeto avatar moondancer83 avatar podviaznikov avatar ringods avatar samclinckspoor avatar shavo007 avatar simonrondelez avatar sword42 avatar undecidedapollo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

terraform-website-s3-cloudfront-route53's Issues

CNAME vs ALIAS

Is there any case you're aware of where you'd prefer a Route53 CNAME over A-ALIAS? ALIAS records save the client from additional DNS lookups

duplicate IAM policies

Both site-main and site-redirect try to create an aws_iam_policy resource with the name "site.${replace("${var.domain}",".","-")}.deployer". Since the domain variable will generally be the same for the main and redirect buckets, the result is an error from Terraform/AWS like:

* aws_iam_policy.site_deployer_policy: Error creating IAM policy site.my-domain-com.deployer: EntityAlreadyExists: A policy called site.my-domain-com.deployer already exists. Duplicate names are not allowed.
        status code: 409, request id: 38816639-7dd1-11e6-9f1a-6d0a94ff2f5f

Seems like adding a -main and -redirect or something to the policy name would be an easy enough fix. Alternatively, since the deployer_role_policy.json file that they each use is identical, perhaps just a refactor so it only tries to create it once?

"required field is not set" error

Hi there,
I'm running into an error like this when I try to use your module:

2020/03/21 16:27:37 [ERROR] module.site-main: eval: *terraform.EvalDiff, err: "origin.0.domain_name": required field is not set
2020/03/21 16:27:37 [ERROR] module.site-main: eval: *terraform.EvalSequence, err: "origin.0.domain_name": required field is not set
2020/03/21 16:27:37 [TRACE] [walkRefresh] Exiting eval tree: module.site-main.aws_cloudfront_distribution.website_cdn
2020/03/21 16:27:37 [TRACE] vertex "module.site-main.aws_cloudfront_distribution.website_cdn": visit complete
2020/03/21 16:27:37 [TRACE] vertex "module.site-main.aws_cloudfront_distribution.website_cdn": dynamic subgraph encountered errors
2020/03/21 16:27:37 [TRACE] vertex "module.site-main.aws_cloudfront_distribution.website_cdn": visit complete
2020/03/21 16:27:37 [TRACE] dag/walk: upstream of "module.site-main.output.website_cdn_hostname" errored, so skipping
2020/03/21 16:27:37 [TRACE] dag/walk: upstream of "module.site-main.output.website_cdn_arn" errored, so skipping
2020/03/21 16:27:37 [TRACE] dag/walk: upstream of "module.site-main.output.website_cdn_zone_id" errored, so skipping
2020/03/21 16:27:37 [TRACE] dag/walk: upstream of "module.site-main.output.website_cdn_id" errored, so skipping
2020/03/21 16:27:37 [TRACE] dag/walk: upstream of "provider.aws (close)" errored, so skipping
2020/03/21 16:27:37 [TRACE] dag/walk: upstream of "root" errored, so skipping
Error: "origin.0.domain_name": required field is not set

My implementation looks like this:

module "site-main" {
  source                           = "github.com/skyscrapers/terraform-website-s3-cloudfront-route53//site-main"
  region                           = "eu-central-1"
  domain                           = var.domain
  bucket_name                      = "prefix-${var.environment}-suffix"
  duplicate-content-penalty-secret = var.duplicate-content-penalty-secret
  deployer                         = var.deployer
  acm-certificate-arn              = aws_acm_certificate.cert.arn
  not-found-response-path          = "/404.html"
}

hashicorp AWS 4.2.0 provider error

Describe the problem
When making terraform init - it will automatically download aws 4.2.0 - which results in error when try to terraform plan or apply.
If we use aws 3.7.0 everything works as expected

To Reproduce
Steps to reproduce the behavior:

  1. Create main.tf according to readme
  2. terraform init
  3. terraform apply
  4. See error

Expected behavior
everything should work, or we should specify 3.7.0 as supported aws provider version.

Screenshots
image

Your Environment
Ubuntu 21.04

remove hard coded tags

The following tags are hard coded (in variables.tf):
tags.Project
tags.Environment
tags.Name

It would be good to remove the hard coding so that we can have a clean set of custom values.

Allow 'deployer' to be a role or group, not just a single user

I want to let multiple users be the 'deployer' to my s3 bucket, e.g. me, my colleagues, + the user from a CI app like Jenkins or something.

Normally to facilitate this I'd have a 'deployer' role (not user), and then authorize any of the above users assume that role so that they can deploy.

As this is coded now, there can only be one user. https://github.com/ringods/terraform-website-s3-cloudfront-route53/blob/d2e06/site-redirect/main.tf#L78

Agree it's a good idea to have this module support making a role instead? It could output the role arn, and users of this module could authorize users to assume that role as needed. https://www.terraform.io/docs/providers/aws/r/iam_role_policy_attachment.html

Alternatively, the modules could just output the policy JSON, and then users of this module could use that to send into IAM however makes sense.

Question on site-redirect

If my main site is https://example.com and redirecting the www version to the naked domain; do i need two security certificates?

I only have one currently which is for main site.
Example: example.com

In site redirect module i reused the same certificate.

So when I redirect from www.example.com browser reports certificate issue.

Thanks,
Shane.

site-redirect module error

Hi,

Just ran through the modules and got an error running the module above

"target field is needed"

But the readme doesnt outline this.

Example:

module "site-redirect" {
   source = "github.com/ringods/terraform-website-s3-cloudfront-route53//site-redirect"

   region = "${var.region}"
   domain = "www.xxx.com"
   duplicate-content-penalty-secret = "some-secret-password"
   deployer = "xxx"
   acm-certificate-arn = "arn:aws:acm:us-east-1:118195946080:certificate/0a124b1d-6988-4d46-8f50-c7a8a567a0f9"
   target = "${var.domain}"
}

Also, Im using an aws profile.

When i run the modules i have to run as

AWS_PROFILE=xxx terraform plan

does your modules support profile from aws provider?

provider "aws" {
  region     = "${var.region}"
  profile   = "thsmiko"
}

Awesome modules btw! ;-)

Make "deployer" optional

Happy to make the PR myself, but wondering if you'd agree to "deployer" being optional for site-main?

Some of us already have managed IAM roles with superuser access, or some of us (bad i know) just use the root user on our own AWS accounts.

So, for these cases we don't need this IAM Role Policy Attachment.

Ability to define list of domain aliases (especially for wildcard subdomain handling)

Hi,

currently it's not possible to utilize the functionality of multiple CNAMEs for the cloudfront distribution. In my case I'm trying to register a wildcard subdomain.

Due to the missing type in the variable declaration we're not able to pass a list, although the terraform resource allows a list: https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#aliases

In addition to that: if we just provide a wildcard-domain, e.g. *.test.example.com, the default tag handling creates invalid tags (asterisks are not allowed as it seems).

Best

Make CloudFront price_class attribute configurable

Hi,
I have a feature request

What:

Expose CloudFront price_class attribute as an optional module variable.

Why

So I can define where on the world a website is fast accessible.

Let me know if you like this feature.
I'm willing to create a PR.

Cheers and thank you for a great module.

Multiple profiles

I have 2 aws profiles: prod and dev. Default is dev.
Even if i set profile in provider like that:

provider "aws" {
  region = "ap-northeast-2"
  profile = "prod"
}

i don't know why, but it creates cloudfront distribution on my prod profile, but other resources will be deployed on my default profile.

I added profile variable and now it works correctly!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.