Skip to content

terraform-aws-arc-cloudfront

Latest Release Last Updated Terraform GitHub Actions

Quality gate

Known Vulnerabilities

Overview

SourceFuse AWS Reference Architecture (ARC) Terraform module for managing Cloudfront

For more information about this repository and its usage, please see Terraform AWS ARC CloudFront Usage Guide.

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.6" --> Pin the right version
  providers = {
    aws.acm = aws.acm // Certificate has to be created in us-east-1 region
  }

  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"]
    }

    }
  ]

  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