Skip to content

terraform-aws-arc-cloudfront

Known Vulnerabilities

Overview

SourceFuse AWS Reference Architecture (ARC) Terraform module for managing Cloudfront, S3, Route53 and ACM.

Usage

To see a full example, check out the main.tf file in the example folder.

module "tags" {
  source  = "sourcefuse/arc-tags/aws"
  version = "1.2.3"

  environment = "dev"
  project     = "test"

  extra_tags = {
    RepoName = "terraform-aws-refarch-cloudfront"
  }
}

module "cloudfront" {
  source  = "sourcefuse/arc-cloudfront/aws"
  version = "4.0.1"

  origins = [{
    origin_type   = "custom",
    origin_id     = "cloudfront-arc",
    domain_name   = "tst.wpengine.com",
    bucket_name   = "",
    create_bucket = false,
    custom_origin_config = {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "match-viewer"
      origin_ssl_protocols   = ["TLSv1"]
    }

    }
  ]
  //source = "git::https://github.com/sourcefuse/terraform-aws-refarch-cloudfront?ref=2.0.2"

  namespace              = "test"
  description            = "This is a test Cloudfront distribution"
  route53_root_domain    = "sfrefarch.com" // Used to fetch the Hosted Zone
  create_route53_records = var.create_route53_records
  aliases                = ["cf.sfrefarch.com", "www.cf.sfrefarch.com", "test.sfrefarch.com", "*.sfrefarch.com", "test1.sfrefarch.com"]
  enable_logging         = var.enable_logging // Create a new S3 bucket for storing Cloudfront logs

  default_cache_behavior = {
    origin_id              = "cloudfront-arc",
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    compress               = false
    viewer_protocol_policy = "redirect-to-https"

    use_aws_managed_cache_policy          = true
    cache_policy_name                     = "CachingOptimized"
    use_aws_managed_origin_request_policy = true
    origin_request_policy_name            = "CORS-S3Origin" // It can be custom or aws managed policy name , if custom origin_request_policies variable key should match
    lambda_function_association = [{
      event_type   = "viewer-request"
      lambda_arn   = aws_lambda_function.this.qualified_arn
      include_body = true
    }]

  }

  cache_behaviors = [
    {
      origin_id              = "cloudfront-arc",
      path_pattern           = "/content/immutable/*"
      allowed_methods        = ["GET", "HEAD"]
      cached_methods         = ["GET", "HEAD"]
      compress               = false
      viewer_protocol_policy = "redirect-to-https"

      use_aws_managed_cache_policy          = false
      cache_policy_name                     = "cache-policy-1" // Note: This has to match cache_polices mentioned below
      use_aws_managed_origin_request_policy = false
      origin_request_policy_name            = "origin-req-policy-1" // Note: This has to match origin_request_policies mentioned below

      function_association = [
        {
          event_type   = "viewer-request"
          function_arn = aws_cloudfront_function.this.arn
        }
      ]
    }
  ]

  viewer_certificate = {
    cloudfront_default_certificate = false // false :  It will create ACM certificate with details provided in acm_details
    minimum_protocol_version       = "TLSv1.2_2018"
    ssl_support_method             = "sni-only"
  }

  acm_details = {
    domain_name               = "*.sfrefarch.com",
    subject_alternative_names = ["www.cf.sfrefarch.com"]
  }

  cache_policies = {
    "cache-policy-1" = {
      default_ttl = 86400,
      max_ttl     = 31536000,
      min_ttl     = 0,
      cookies_config = {
        cookie_behavior = "none",
        items           = []
      },
      headers_config = {
        header_behavior = "whitelist",
        items           = ["Authorization", "Origin", "Accept", "Access-Control-Request-Method", "Access-Control-Request-Headers", "Referer"]
      },
      query_string_behavior = {
        header_behavior = "none",
        items           = []
      },
      query_strings_config = {
        query_string_behavior = "none",
        items                 = []
      }
  } }


  origin_request_policies = {
    "origin-req-policy-1" = {
      cookies_config = {
        cookie_behavior = "none",
        items           = []
      },
      headers_config = {
        header_behavior = "whitelist",
        items = ["Accept", "Accept-Charset", "Accept-Datetime", "Accept-Language",
          "Access-Control-Request-Method", "Access-Control-Request-Headers", "CloudFront-Forwarded-Proto", "CloudFront-Is-Android-Viewer",
        "CloudFront-Is-Desktop-Viewer", "CloudFront-Is-IOS-Viewer"]
      },
      query_strings_config = {
        query_string_behavior = "none",
        items                 = []
      }
  } }

  custom_error_responses = [{
    error_caching_min_ttl = 10,
    error_code            = "404", // should be unique
    response_code         = "404",
    response_page_path    = "/custom_404.html"
  }]

  s3_kms_details = {
    s3_bucket_encryption_type = "SSE-S3", //Encryption for S3 bucket , options : `SSE-S3` , `SSE-KMS`
    kms_key_administrators    = [],
    kms_key_users             = [], // Note :- Add users/roles who wanted to read/write to S3 bucket
    kms_key_arn               = null
  }

  tags = module.tags.tags

}

Requirements

Name Version
terraform >= 1.3.0, < 2.0.0
aws ~> 4.0

Providers

Name Version
aws 4.67.0
aws.acm 4.67.0

Modules

Name Source Version
kms ./modules/kms n/a
s3_bucket git::https://github.com/cloudposse/terraform-aws-s3-bucket 3.1.2
s3_bucket_logs git::https://github.com/cloudposse/terraform-aws-s3-bucket 3.1.2

Resources

Name Type
aws_acm_certificate.this resource
aws_acm_certificate_validation.this resource
aws_cloudfront_cache_policy.this resource
aws_cloudfront_distribution.this resource
aws_cloudfront_origin_access_control.s3 resource
aws_cloudfront_origin_request_policy.this resource
aws_cloudfront_response_headers_policy.this resource
aws_route53_record.root_domain resource
aws_route53_record.this resource
aws_s3_bucket_policy.cdn_bucket_policy resource
aws_caller_identity.this data source
aws_partition.this data source
aws_route53_zone.this data source
aws_s3_bucket.origin data source

Inputs

Name Description Type Default Required
acm_details Details required for creating certificate
eg. {
domain_name = "test.com",
subject_alternative_names = ["www.test.com"]
}
object({
domain_name = string,
subject_alternative_names = list(string),
})
{
"domain_name": "",
"subject_alternative_names": []
}
no
aliases Fully qualified domain name for site being hosted list(string) n/a yes
cache_behaviors Set the cache behaviors for the distribution , Note:- You cannot use an origin request policy in a cache behavior without a cache policy.
list(object({
origin_id = string // should be same as what is given in origins
path_pattern = string
allowed_methods = list(string)
cached_methods = list(string)
response_headers_policy_name = optional(string, null)
use_aws_managed_response_headers_policy = optional(bool, false)
function_association = optional(list(object({ // Specific event to trigger this function. Valid values: viewer-request or viewer-response.
event_type = string,
function_arn = string
})))
lambda_function_association = optional(list(object({ // A config block that triggers a lambda function with specific actions (maximum 4).
event_type = string,
lambda_arn = string,
include_body = bool // When set to true it exposes the request body to the lambda function.
})))
use_aws_managed_cache_policy = bool,
cache_policy_name = string, // It can be custom or aws managed policy name , if custom cache_policies variable key should match
use_aws_managed_origin_request_policy = optional(bool),
origin_request_policy_name = optional(string), // It can be custom or aws managed policy name , if custom origin_request_policies variable key should match
compress = bool,
viewer_protocol_policy = string
}))
[] no
cache_policies Cache policies,
eg. {
"cache-policy-1" = {
default_ttl = 86400,
max_ttl = 31536000,
min_ttl = 0,
cookies_config = {
cookie_behavior = "none",
items = []
},
headers_config = {
header_behavior = "whitelist",
items = ["Authorization", "Origin", "Accept", "Access-Control-Request-Method", "Access-Control-Request-Headers", "Referer"]
},
query_string_behavior = {
header_behavior = "none",
items = []
},
query_strings_config = {
query_string_behavior = "none",
items = []
}
} }
map(object(
{
default_ttl = number,
max_ttl = number,
min_ttl = number,
cookies_config = object({
cookie_behavior = string
items = list(string)
}),
headers_config = object({
header_behavior = string
items = list(string)
}),
query_strings_config = object({
query_string_behavior = string
items = list(string)
})
}
))
{} no
cors_configuration Specifies the allowed headers, methods, origins and exposed headers when using CORS on this bucket
list(object({
allowed_headers = list(string)
allowed_methods = list(string)
allowed_origins = list(string)
expose_headers = list(string)
max_age_seconds = number
}))
null no
create_route53_records made optional route53 bool false no
custom_error_responses One or more custom error response elements
list(object({
error_caching_min_ttl = optional(number),
error_code = string,
response_code = optional(string),
response_page_path = optional(string) // eg: /custom_404.html
}))
[] no
default_cache_behavior Default cache behavior for the distribution
object({
origin_id = string // should be same as what is given in origins
allowed_methods = list(string)
cached_methods = list(string)
response_headers_policy_name = optional(string, null)
use_aws_managed_response_headers_policy = optional(bool, false)
function_association = optional(list(object({ // A config block that triggers a lambda function with specific actions (maximum 4).
event_type = string, // Specific event to trigger this function. Valid values: viewer-request or viewer-response.
function_arn = string
})))
lambda_function_association = optional(list(object({ // A config block that triggers a lambda function with specific actions (maximum 4).
event_type = string,
lambda_arn = string,
include_body = bool // When set to true it exposes the request body to the lambda function.
})))
use_aws_managed_cache_policy = bool,
cache_policy_name = string, // It can be custom or aws managed policy name , if custom cache_policies variable key should match
use_aws_managed_origin_request_policy = optional(bool),
origin_request_policy_name = optional(string), // It can be custom or aws managed policy name , if custom origin_request_policies variable key should match
compress = bool
viewer_protocol_policy = string
})
n/a yes
default_root_object Object that you want CloudFront to return (for example, index.html) when an end user requests the root URL. string "index.html" no
description CloudFron destribution description string n/a yes
enable_logging Enable logging for Clouffront destribution, this will create new S3 bucket bool false no
geo_restriction Geographic restriction
object({
restriction_type = string,
locations = list(string)
})
{
"locations": [],
"restriction_type": "none"
}
no
logging_bucket S3 bucket used for storing logs string null no
namespace Namespace for the resources. string null no
origin_request_policies Origin request policies,
eg. {
"origin-req-policy" = {
cookies_config = {
cookie_behavior = "none",
items = []
},
headers_config = {
header_behavior = "whitelist",
items = ["Accept", "Accept-Charset", "Accept-Datetime", "Accept-Language",
"Access-Control-Request-Method", "Access-Control-Request-Headers", "CloudFront-Forwarded-Proto", "CloudFront-Is-Android-Viewer",
"CloudFront-Is-Desktop-Viewer", "CloudFront-Is-IOS-Viewer"]
},
query_strings_config = {
query_string_behavior = "none",
items = []
}
} }
map(object({
cookies_config = object({
cookie_behavior = string
items = list(string)
}),
headers_config = object({
header_behavior = string
items = list(string)
}),
query_strings_config = object({
query_string_behavior = string
items = list(string)
})
}))
{} no
origins List of Origins for Cloudfront
list(object({
origin_type = string // S3 or custom origin
origin_id = string
origin_path = optional(string)
domain_name = string
bucket_name = optional(string) // required of origin is S3
create_bucket = bool // required of origin is S3
connection_attempts = optional(number, 3)
connection_timeout = optional(number, 10)
cors_configuration = optional(any) // cors for S3
origin_shield = optional(object({
enabled = bool
origin_shield_region = string
}), {
enabled = false
origin_shield_region = null
})
custom_origin_config = optional(object({
http_port = number
https_port = number
origin_protocol_policy = string
origin_ssl_protocols = list(string)
origin_keepalive_timeout = optional(number, 5)
origin_read_timeout = optional(number, 30)
}))
}))
[] no
price_class Price class for this distribution. One of PriceClass_All, PriceClass_200, PriceClass_100. string "PriceClass_All" no
response_headers_policy Header policies,
eg. {
"response-header-policy-1" = {
default_ttl = 86400,
max_ttl = 31536000,
min_ttl = 0,
cookies_config = {
cookie_behavior = "none",
items = []
},
headers_config = {
header_behavior = "whitelist",
items = ["Authorization", "Origin", "Accept", "Access-Control-Request-Method", "Access-Control-Request-Headers", "Referer"]
},
query_string_behavior = {
header_behavior = "none",
items = []
},
query_strings_config = {
query_string_behavior = "none",
items = []
}
} }
map(object(
{
name = string
comment = optional(string, "")
cors_config = optional(object({
access_control_allow_credentials = bool
access_control_allow_headers = object({
items = list(string)
})
access_control_allow_methods = object({
items = list(string)
})
access_control_allow_origins = object({
items = list(string)
})
access_control_expose_headers = object({
items = list(string)
})
access_control_max_age_sec = number
origin_override = bool
})),
server_timing_headers_config = optional(object({
enabled = bool
sampling_rate = number
}),
{
enabled = false
sampling_rate = 0
}),

remove_headers_config = optional(object({
items = list(string)
}))
custom_headers_config = optional(object({
items = list(object({
header = string
override = bool
value = string
})) }), null)

security_headers_config = optional(object({
content_type_options = object({
override = bool
})
frame_options = object({
frame_option = string
override = bool
})
referrer_policy = object({
referrer_policy = string
override = bool
})
xss_protection = object({
mode_block = bool
protection = bool
override = bool
report_uri = string
})
strict_transport_security = object({
access_control_max_age_sec = string
include_subdomains = bool
preload = bool
override = bool
})
content_security_policy = object({
content_security_policy = string
override = bool
})

}))
}
))
{} no
retain_on_delete Disables the distribution instead of deleting it when destroying the resource through Terraform. If this is set, the distribution needs to be deleted manually afterwards. bool false no
route53_record_ttl TTL for Route53 record string 60 no
route53_root_domain Domain to add to route 53 as alias to distribution string n/a yes
s3_kms_details KMS details for S3 encryption
object({
s3_bucket_encryption_type = string, //Encryption for S3 bucket , options : SSE-S3 - AES256 , SSE-KMS - aws:kms
kms_key_administrators = optional(list(string)), // "Environment where deploying,List of AWS arns that will have permissions to use kms key"
kms_key_users = optional(list(string)), // "Environment where deploying,List of AWS arns that will have permissions to use kms key"
kms_key_arn = optional(string) // In case if we need to use CMK created else where, set as null if not used
})
{
"kms_key_administrators": [],
"kms_key_arn": null,
"kms_key_users": [],
"s3_bucket_encryption_type": "SSE-S3"
}
no
tags Tags for AWS resources map(string) {} no
viewer_certificate The SSL configuration for this distribution
object({
cloudfront_default_certificate = bool,
minimum_protocol_version = string,
ssl_support_method = string
})
{
"cloudfront_default_certificate": false,
"minimum_protocol_version": "TLSv1.2_2018",
"ssl_support_method": "sni-only"
}
no
web_acl_id Unique identifier that specifies the AWS WAF web ACL, if any, to associate with this distribution. To specify a web ACL created using the latest version of AWS WAF (WAFv2), use the ACL ARN, for example aws_wafv2_web_acl.example.arn. string null no

Outputs

Name Description
acm_certificate_arn Certificate ARN
cloudfront_arn CloudFront ARN
cloudfront_domain_name CloudFront Domain name
cloudfront_hosted_zone_id CloudFront Hosted zone ID
cloudfront_id CloudFront ID
logging_s3_bucket Logging bucket name
origin_s3_bucket Origin bucket name

Development

Prerequisites

Configurations

  • Configure pre-commit hooks
pre-commit install

Git commits

while Contributing or doing git commit please specify the breaking change in your commit message whether its major,minor or patch

For Example

git commit -m "your commit message #major"
By specifying this , it will bump the version and if you dont specify this in your commit message then by default it will consider patch and will bump that accordingly

Tests

  • Tests are available in test directory
  • Configure the dependencies
1
2
3
cd test
go mod init github.com/sourcefuse/terraform-aws-refarch-cloudfront
go get github.com/gruntwork-io/terratest/modules/terraform
  • Now execute the test
cd test/
go test

Authors

This project is authored by:

  • SourceFuse ARC Team