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

Initial commit static site tf module #1

Merged
merged 6 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ on:
push:
branches:
- main
permissions:
actions: read
checks: read
contents: read
pull-requests: write
security-events: write
jobs:
ci:
uses: SPHTech-Platform/reusable-workflows/.github/workflows/terraform.yaml@main
Expand Down
67 changes: 67 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,68 @@
# Terraform Modules Template

<!-- BEGIN_TF_DOCS -->
## Requirements

No requirements.

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | n/a |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_cdn"></a> [cdn](#module\_cdn) | terraform-aws-modules/cloudfront/aws | n/a |
| <a name="module_s3"></a> [s3](#module\_s3) | terraform-aws-modules/s3-bucket/aws | 3.5.0 |

## Resources

| Name | Type |
|------|------|
| [aws_cloudfront_function.viewer_request](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_function) | resource |
| [aws_s3_bucket_policy.docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_iam_policy_document.s3_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.s3_policy_merge](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_acl"></a> [acl](#input\_acl) | Private or Public ACL | `string` | `"private"` | no |
| <a name="input_attach_policy"></a> [attach\_policy](#input\_attach\_policy) | Controls if S3 bucket should have bucket policy attached (set to `true` to use value of `policy` as bucket policy) | `bool` | `true` | no |
| <a name="input_block_public_acls"></a> [block\_public\_acls](#input\_block\_public\_acls) | Whether Amazon S3 should block public ACLs for this bucket. | `bool` | `true` | no |
| <a name="input_block_public_policy"></a> [block\_public\_policy](#input\_block\_public\_policy) | Whether Amazon S3 should block public bucket policies for this bucket. | `bool` | `true` | no |
| <a name="input_bucket_name"></a> [bucket\_name](#input\_bucket\_name) | bucket name | `string` | `""` | no |
| <a name="input_cors_rule"></a> [cors\_rule](#input\_cors\_rule) | List of maps containing rules for Cross-Origin Resource Sharing. | `any` | <pre>{<br> "cors_rule": {<br> "allowed_headers": [<br> "*"<br> ],<br> "allowed_methods": [<br> "PUT",<br> "POST",<br> "GET",<br> "DELETE"<br> ],<br> "allowed_origins": [<br> "*"<br> ],<br> "expose_headers": [<br> "ETag"<br> ],<br> "max_age_seconds": 3000<br> }<br>}</pre> | no |
| <a name="input_create_origin_access_identity"></a> [create\_origin\_access\_identity](#input\_create\_origin\_access\_identity) | Whether Amazon S3 should restrict public bucket policies for this bucket. | `bool` | `true` | no |
| <a name="input_default_cache_behavior"></a> [default\_cache\_behavior](#input\_default\_cache\_behavior) | The default cache behavior for this distribution | `any` | `{}` | no |
| <a name="input_ignore_public_acls"></a> [ignore\_public\_acls](#input\_ignore\_public\_acls) | Whether Amazon S3 should ignore public ACLs for this bucket. | `bool` | `true` | no |
| <a name="input_lifecycle_rule"></a> [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. | `any` | `[]` | no |
| <a name="input_logging"></a> [logging](#input\_logging) | Map containing access bucket logging configuration. | `map(string)` | `{}` | no |
| <a name="input_ordered_cache_behavior"></a> [ordered\_cache\_behavior](#input\_ordered\_cache\_behavior) | An ordered list of cache behaviors resource for this distribution. List from top to bottom in order of precedence. The topmost cache behavior will have precedence 0. | `any` | `[]` | no |
| <a name="input_origin"></a> [origin](#input\_origin) | One or more origins for this distribution (multiples allowed). | `any` | `{}` | no |
| <a name="input_origin_access_identities"></a> [origin\_access\_identities](#input\_origin\_access\_identities) | Map of CloudFront origin access identities (value as a comment) | `map(string)` | `{}` | no |
| <a name="input_policy"></a> [policy](#input\_policy) | (Optional) A valid bucket policy JSON document. Note that if the policy document is not specific enough (but still valid), Terraform may view the policy as constantly changing in a terraform plan. In this case, please make sure you use the verbose/specific version of the policy. For more information about building AWS IAM policy documents with Terraform, see the AWS IAM Policy Document Guide. | `string` | `""` | no |
| <a name="input_price_class"></a> [price\_class](#input\_price\_class) | The price class for this distribution. One of PriceClass\_All, PriceClass\_200, PriceClass\_100 | `string` | `"PriceClass_All"` | no |
| <a name="input_restrict_public_buckets"></a> [restrict\_public\_buckets](#input\_restrict\_public\_buckets) | Whether Amazon S3 should restrict public bucket policies for this bucket. | `bool` | `true` | no |
| <a name="input_server_side_encryption_configuration"></a> [server\_side\_encryption\_configuration](#input\_server\_side\_encryption\_configuration) | Map containing server-side encryption configuration. | `any` | `{}` | no |
| <a name="input_versioning"></a> [versioning](#input\_versioning) | Map containing versioning configuration. | `map(string)` | <pre>{<br> "enabled": true<br>}</pre> | no |
| <a name="input_wait_for_deployment"></a> [wait\_for\_deployment](#input\_wait\_for\_deployment) | Whether Amazon S3 should restrict public bucket policies for this bucket. | `bool` | `false` | no |
| <a name="input_website"></a> [website](#input\_website) | Map containing static web-site hosting or redirect configuration. | `any` | <pre>{<br> "error_document": "error.html",<br> "index_document": "index.html"<br>}</pre> | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_cloudfront_distribution_arn"></a> [cloudfront\_distribution\_arn](#output\_cloudfront\_distribution\_arn) | The ARN (Amazon Resource Name) for the distribution. |
| <a name="output_cloudfront_distribution_domain_name"></a> [cloudfront\_distribution\_domain\_name](#output\_cloudfront\_distribution\_domain\_name) | The domain name corresponding to the distribution. |
| <a name="output_cloudfront_distribution_id"></a> [cloudfront\_distribution\_id](#output\_cloudfront\_distribution\_id) | The Arn of the cloudfront distribution |
| <a name="output_cloudfront_origin_access_identity_iam_arns"></a> [cloudfront\_origin\_access\_identity\_iam\_arns](#output\_cloudfront\_origin\_access\_identity\_iam\_arns) | The IAM arns of the origin access identities created |
| <a name="output_s3_bucket_arn"></a> [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the bucket. Will be of format arn:aws:s3:::bucketname. |
| <a name="output_s3_bucket_bucket_domain_name"></a> [s3\_bucket\_bucket\_domain\_name](#output\_s3\_bucket\_bucket\_domain\_name) | The bucket domain name. Will be of format bucketname.s3.amazonaws.com. |
| <a name="output_s3_bucket_bucket_regional_domain_name"></a> [s3\_bucket\_bucket\_regional\_domain\_name](#output\_s3\_bucket\_bucket\_regional\_domain\_name) | The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL. |
| <a name="output_s3_bucket_id"></a> [s3\_bucket\_id](#output\_s3\_bucket\_id) | The name of the bucket. |
<!-- END_TF_DOCS -->
67 changes: 67 additions & 0 deletions cloudfront.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
module "cdn" {
source = "terraform-aws-modules/cloudfront/aws"
version = "~> 3.1.0"

comment = "Distribution for static website"
is_ipv6_enabled = true
price_class = var.price_class
wait_for_deployment = var.wait_for_deployment
create_origin_access_identity = var.create_origin_access_identity

origin_access_identities = merge({
origin_access_identity = module.s3.s3_bucket_id
}, var.origin_access_identities)

origin = merge({
origin_access_identity = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@franklinpashok franklinpashok Jan 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@niroz89 the cloudfront module which i am taking reference currently OAC is not added, i saw few PR's to add support for OAC, but not merged yet. Once its added i will include OAC in our module as well.

terraform-aws-modules/terraform-aws-cloudfront#97

or shall we add as a resource for using OAC in static site module?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okk, we can wait until the PR gets merged.

domain_name = module.s3.s3_bucket_bucket_regional_domain_name
s3_origin_config = {
origin_access_identity = "origin_access_identity"
# key in `origin_access_identities`
} }
}, var.origin)

default_cache_behavior = merge({
target_origin_id = "origin_access_identity" # key in `origin` above
viewer_protocol_policy = "redirect-to-https"

default_ttl = 360
min_ttl = 300
max_ttl = 600

allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
compress = true
query_string = false

function_association = {
viewer-request = {
function_arn = aws_cloudfront_function.viewer_request.arn
}
}
}, var.default_cache_behavior)

ordered_cache_behavior = var.ordered_cache_behavior

default_root_object = "index.html"

custom_error_response = [
{
error_code = 404
response_code = 404
response_page_path = "/errors/404.html"
},
{
error_code = 403
response_code = 403
response_page_path = "/errors/403.html"
}
]
}

resource "aws_cloudfront_function" "viewer_request" {
name = "default_viewer_request"
runtime = "cloudfront-js-1.0"
publish = true
code = file("${path.module}/templates/viewer-request-default.js")
}
21 changes: 21 additions & 0 deletions data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
data "aws_iam_policy_document" "s3_policy" {
statement {
actions = ["s3:GetObject"]
resources = ["${module.s3.s3_bucket_arn}/*"]
principals {
type = "AWS"
identifiers = module.cdn.cloudfront_origin_access_identity_iam_arns
}
}
depends_on = [
module.cdn.cloudfront_distribution_id
]
}

data "aws_iam_policy_document" "s3_policy_merge" {

source_policy_documents = [
data.aws_iam_policy_document.s3_policy.json,
var.policy
]
}
39 changes: 39 additions & 0 deletions output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
output "s3_bucket_id" {
description = "The name of the bucket."
value = module.s3.s3_bucket_id
}

output "s3_bucket_arn" {
description = "The ARN of the bucket. Will be of format arn:aws:s3:::bucketname."
value = module.s3.s3_bucket_arn
}

output "s3_bucket_bucket_domain_name" {
description = "The bucket domain name. Will be of format bucketname.s3.amazonaws.com."
value = module.s3.s3_bucket_bucket_domain_name
}

output "s3_bucket_bucket_regional_domain_name" {
description = "The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL."
value = module.s3.s3_bucket_bucket_regional_domain_name
}

output "cloudfront_distribution_id" {
description = "The Arn of the cloudfront distribution"
value = module.cdn.cloudfront_distribution_id
}

output "cloudfront_distribution_arn" {
description = "The ARN (Amazon Resource Name) for the distribution."
value = module.cdn.cloudfront_distribution_arn
}

output "cloudfront_distribution_domain_name" {
description = "The domain name corresponding to the distribution."
value = module.cdn.cloudfront_distribution_domain_name
}

output "cloudfront_origin_access_identity_iam_arns" {
description = "The IAM arns of the origin access identities created"
value = module.cdn.cloudfront_origin_access_identity_iam_arns
}
31 changes: 31 additions & 0 deletions s3.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module "s3" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "~> 3.5.0"

bucket = var.bucket_name
acl = var.acl
block_public_acls = var.block_public_acls
block_public_policy = var.block_public_policy
ignore_public_acls = var.ignore_public_acls
restrict_public_buckets = var.restrict_public_buckets

server_side_encryption_configuration = var.server_side_encryption_configuration
cors_rule = var.cors_rule
lifecycle_rule = var.lifecycle_rule

versioning = var.versioning
logging = var.logging

website = var.website

}


resource "aws_s3_bucket_policy" "docs" {
bucket = module.s3.s3_bucket_id
policy = data.aws_iam_policy_document.s3_policy_merge.json

depends_on = [
module.cdn.cloudfront_distribution_id
]
}
15 changes: 15 additions & 0 deletions templates/viewer-request-default.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function handler(event) {
var request = event.request;
var uri = request.uri;

// Check whether the URI is missing a file name.
if (uri.endsWith('/')) {
request.uri += 'index.html';
}
// Check whether the URI is missing a file extension.
else if (!uri.includes('.')) {
request.uri += '/index.html';
}

return request;
}
138 changes: 138 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
variable "bucket_name" {
description = "bucket name"
type = string
default = ""
}

variable "acl" {
description = "Private or Public ACL"
type = string
default = "private"
}

variable "attach_policy" {
description = "Controls if S3 bucket should have bucket policy attached (set to `true` to use value of `policy` as bucket policy)"
type = bool
default = true
}

variable "policy" {
description = "A valid bucket policy JSON document (Optional)"
type = string
default = ""
}

variable "block_public_acls" {
description = "Whether Amazon S3 should block public ACLs for this bucket."
type = bool
default = true
}

variable "block_public_policy" {
description = "Whether Amazon S3 should block public bucket policies for this bucket."
type = bool
default = true
}

variable "ignore_public_acls" {
description = "Whether Amazon S3 should ignore public ACLs for this bucket."
type = bool
default = true
}

variable "restrict_public_buckets" {
description = "Whether Amazon S3 should restrict public bucket policies for this bucket."
type = bool
default = true
}

variable "server_side_encryption_configuration" {
description = "Map containing server-side encryption configuration."
type = any
default = {}
}

variable "lifecycle_rule" {
description = "List of maps containing configuration of object lifecycle management."
type = any
default = []
}

variable "cors_rule" {
description = "List of maps containing rules for Cross-Origin Resource Sharing."
type = any
default = {
cors_rule = {
allowed_headers = ["*"]
allowed_methods = ["PUT", "POST", "GET", "DELETE"]
allowed_origins = ["*"]
expose_headers = ["ETag"]
max_age_seconds = 3000
}
}
}

variable "versioning" {
description = "Map containing versioning configuration."
type = map(string)
default = {
enabled = true
}
}

variable "logging" {
description = "Map containing access bucket logging configuration."
type = map(string)
default = {}
}

variable "website" {
description = "Map containing static web-site hosting or redirect configuration."
type = any # map(string)
default = {
index_document = "index.html"
error_document = "error.html"
}
}

variable "wait_for_deployment" {
description = "Whether Amazon S3 should restrict public bucket policies for this bucket."
type = bool
default = false
}

variable "create_origin_access_identity" {
description = "Whether Amazon S3 should restrict public bucket policies for this bucket."
type = bool
default = true
}

variable "ordered_cache_behavior" {
description = "An ordered list of cache behaviors resource for this distribution. List from top to bottom in order of precedence. The topmost cache behavior will have precedence 0."
type = any
default = []
}

variable "price_class" {
description = "The price class for this distribution. One of PriceClass_All, PriceClass_200, PriceClass_100"
type = string
default = "PriceClass_All"
}

variable "origin_access_identities" {
description = "Map of CloudFront origin access identities (value as a comment)"
type = map(string)
default = {}
}

variable "origin" {
description = "One or more origins for this distribution (multiples allowed)."
type = any
default = {}
}

variable "default_cache_behavior" {
description = "The default cache behavior for this distribution"
type = any
default = {}
}
Loading