Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(terraform): Create CDN demo resources #4

Merged
merged 11 commits into from
Aug 7, 2022
Merged
42 changes: 42 additions & 0 deletions terraform/cdn/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions terraform/cdn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# AWS Advanced Network Specialty CDN Demo

This module has 3 input variables which create the resources in stages to follow the steps in the demo.

The default values ensure only the S3 bucket is created initially with the required contents uploaded.

To proceed to the next stage of the demo, `enable_cloudfront` can be set to true in `cdn.auto.tfvars`.

Similarly adding a `demo_domain_name` will create an ACM Certificate, DNS records, and update the `viewer_certificate` accordingly.

Finally, setting `secure_s3_bucket` to `true` will create the origin access identity and update the bucket policy.

There is an open [issue](#8) to try and make the OAI to CloudFront association dynamic based on the value of `secure_s3_bucket`. To complete the demo, the `s3_origin_config` must be uncommented.

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.2.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.71.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | ~> 3.3.2 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 4.24.0 |
| <a name="provider_random"></a> [random](#provider\_random) | 3.3.2 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_acm"></a> [acm](#module\_acm) | terraform-aws-modules/acm/aws | ~> 4.0.1 |
| <a name="module_cdn"></a> [cdn](#module\_cdn) | terraform-aws-modules/cloudfront/aws | ~> 2.9.3 |
| <a name="module_cname_record"></a> [cname\_record](#module\_cname\_record) | terraform-aws-modules/route53/aws//modules/records | ~> 2.9.0 |
| <a name="module_s3_bucket"></a> [s3\_bucket](#module\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | ~> v3.3.0 |
| <a name="module_s3_bucket_object"></a> [s3\_bucket\_object](#module\_s3\_bucket\_object) | terraform-aws-modules/s3-bucket/aws//modules/object | ~> v3.3.0 |
| <a name="module_template_files"></a> [template\_files](#module\_template\_files) | hashicorp/dir/template | ~> v1.0.2 |

## Resources

| Name | Type |
|------|------|
| [random_string.random](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
| [aws_cloudfront_cache_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_cache_policy) | data source |
| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.bucket_policy_combined](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.bucket_policy_with_oai](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_route53_zone.demo](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_demo_domain_name"></a> [demo\_domain\_name](#input\_demo\_domain\_name) | Route53 domain name registered for the demo | `string` | `null` | no |
| <a name="input_enable_cloudfront"></a> [enable\_cloudfront](#input\_enable\_cloudfront) | Feature toggle for the cloudfront distribution | `bool` | `false` | no |
| <a name="input_secure_s3_bucket"></a> [secure\_s3\_bucket](#input\_secure\_s3\_bucket) | Set to true to restrict access to the S3 bucket to the CloudFront OAI | `bool` | `false` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_alternate_cname"></a> [alternate\_cname](#output\_alternate\_cname) | The CNAME records associated with CloudFront |
| <a name="output_certificat_arn"></a> [certificat\_arn](#output\_certificat\_arn) | The arn of the ACM certificate |
| <a name="output_cloudfront_url"></a> [cloudfront\_url](#output\_cloudfront\_url) | The CloudFront distribution domain name |
| <a name="output_s3_website_url"></a> [s3\_website\_url](#output\_s3\_website\_url) | The S3 Bucket website endpoint |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
3 changes: 3 additions & 0 deletions terraform/cdn/cdn.auto.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# enable_cloudfront = true
# demo_domain_name = "your-demo-domain"
# secure_s3_bucket = true
166 changes: 166 additions & 0 deletions terraform/cdn/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
locals {
#* Bucket name is shared between the resource and the policy. This overcomes cycle dependancy between the two
bucket_name = "ans-cdn-top10cats-demo-${random_string.random.result}"
#* Do not create the CNAME when the demo domain name is not specified
alternate_cname = var.demo_domain_name != null ? "merlin.${var.demo_domain_name}" : null
#* Use the default CloudFront certificate when the demo domain name is not specified
use_default_cert = var.demo_domain_name == null
}

data "aws_iam_policy_document" "bucket_policy" {
statement {
sid = "AllowPublicAccessToS3Bucket"
principals {
type = "*"
identifiers = ["*"]
}
actions = ["s3:GetObject", ]
resources = ["arn:aws:s3:::${local.bucket_name}/*", ]
}
}

data "aws_iam_policy_document" "bucket_policy_with_oai" {
statement {
sid = "AllowAccessFromCloudFrontToS3Bucket"
principals {
type = "AWS"
identifiers = module.cdn[0].cloudfront_origin_access_identity_iam_arns
}
actions = ["s3:GetObject"]
resources = ["arn:aws:s3:::${local.bucket_name}/*"]
}
}

data "aws_iam_policy_document" "bucket_policy_combined" {
source_policy_documents = [(
var.secure_s3_bucket ?
data.aws_iam_policy_document.bucket_policy_with_oai.json :
data.aws_iam_policy_document.bucket_policy.json
)
]
}

resource "random_string" "random" {
length = 12
special = false
upper = false
}

module "template_files" {
source = "hashicorp/dir/template"
version = "~> v1.0.2"

base_dir = "${path.module}/static"
}

module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "~> v3.3.0"

bucket = local.bucket_name
force_destroy = true

attach_policy = true
policy = data.aws_iam_policy_document.bucket_policy_combined.json

website = {
index_document = "index.html"
error_document = "error.html"
}
}

module "s3_bucket_object" {
for_each = module.template_files.files
source = "terraform-aws-modules/s3-bucket/aws//modules/object"
version = "~> v3.3.0"

bucket = module.s3_bucket.s3_bucket_id
key = each.key
content_type = each.value.content_type
file_source = each.value.source_path
}

data "aws_cloudfront_cache_policy" "this" {
count = var.enable_cloudfront ? 1 : 0
name = "Managed-CachingOptimized"
}

module "cdn" {
count = var.enable_cloudfront ? 1 : 0
source = "terraform-aws-modules/cloudfront/aws"
version = "~> 2.9.3"

aliases = var.demo_domain_name != null ? [local.alternate_cname] : null

comment = "Top 10 Cats CDN"
enabled = true
price_class = "PriceClass_All"

origin = {
top10cats = {
domain_name = module.s3_bucket.s3_bucket_bucket_regional_domain_name

#? Can s3_origin_config be added dynamically based on the value of var.secure_s3_bucket
#* Uncomment to associated the OAI with the cloudfront distribution and secure the S3 bucket
# s3_origin_config = {
# origin_access_identity = "top-10-cats-bucket"
# }
}
}

create_origin_access_identity = var.secure_s3_bucket ? true : false
origin_access_identities = {
top-10-cats-bucket = "top-10-cats-bucket"
}

default_root_object = "index.html"

default_cache_behavior = {
use_forwarded_values = false
viewer_protocol_policy = "allow-all"
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
cache_policy_id = data.aws_cloudfront_cache_policy.this[0].id
target_origin_id = "top10cats"
}

viewer_certificate = {
acm_certificate_arn = local.use_default_cert ? null : module.acm[0].acm_certificate_arn
mminimum_protocol_version = local.use_default_cert ? null : "TLSv1.2_2021"
ssl_support_method = local.use_default_cert ? null : "sni-only"
cloudfront_default_certificate = local.use_default_cert
}
}

data "aws_route53_zone" "demo" {
count = var.demo_domain_name != null ? 1 : 0
name = var.demo_domain_name
}

module "acm" {
count = var.demo_domain_name != null ? 1 : 0
source = "terraform-aws-modules/acm/aws"
version = "~> 4.0.1"

domain_name = local.alternate_cname
zone_id = data.aws_route53_zone.demo[0].zone_id
wait_for_validation = true
}

module "cname_record" {
count = var.demo_domain_name != null ? 1 : 0
source = "terraform-aws-modules/route53/aws//modules/records"
version = "~> 2.9.0"

zone_id = data.aws_route53_zone.demo[0].zone_id
records = [
{
name = "merlin"
type = "A"
alias = {
name = module.cdn[0].cloudfront_distribution_domain_name
zone_id = module.cdn[0].cloudfront_distribution_hosted_zone_id
}
}
]
}
19 changes: 19 additions & 0 deletions terraform/cdn/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
output "s3_website_url" {
description = "The S3 Bucket website endpoint"
value = "http://${module.s3_bucket.s3_bucket_website_endpoint}"
}

output "cloudfront_url" {
description = "The CloudFront distribution domain name"
value = module.cdn[*].cloudfront_distribution_domain_name
}

output "certificat_arn" {
description = "The arn of the ACM certificate"
value = module.acm[*].acm_certificate_arn
}

output "alternate_cname" {
description = "The CNAME records associated with CloudFront"
value = var.demo_domain_name != null ? "https://${local.alternate_cname}" : null
}
34 changes: 34 additions & 0 deletions terraform/cdn/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
provider "aws" {
profile = "ans-gen"
region = "us-east-1"

default_tags {
tags = {
"Project" = "aws-network-specialty"
"Environment" = "general"
"Demo" = "CDN"
"Terraform" = true
}
}
}

terraform {
required_version = ">= 1.2.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 3.71.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.3.2"
}
}

backend "remote" {
organization = "3ware"
workspaces {
name = "aws-net-spec-cdn"
}
}
}
9 changes: 9 additions & 0 deletions terraform/cdn/static/error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<head>
<title>Error</title>
</head>
<body>
<h1><Center>Another Cat won the top10 - clearly there is an error</Center></h1>
<center><img src="img/thejudge.jpg"></center>
</body>
</html>
Binary file added terraform/cdn/static/img/annnndmerlin.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/anothermerlin.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/boris.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/differentcat1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/differentcat2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/merlin.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/merlinagain.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/samson.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/stillmerlin.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/thefamily.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added terraform/cdn/static/img/thejudge.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading