Skip to content

Commit 14d94f8

Browse files
authored
Merge pull request #2 from tfstack/dev
feat: initial commit
2 parents 1f18c55 + beb62e7 commit 14d94f8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2697
-1
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: Commit Message Conformance
2+
on:
3+
pull_request: {}
4+
permissions:
5+
statuses: write
6+
checks: write
7+
contents: read
8+
pull-requests: read
9+
jobs:
10+
commitmsg-conform:
11+
uses: tfstack/actions/.github/workflows/commitmsg-conform.yml@main
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: Markdown Lint
2+
on:
3+
pull_request: {}
4+
permissions:
5+
statuses: write
6+
checks: write
7+
contents: read
8+
pull-requests: read
9+
jobs:
10+
markdown-lint:
11+
uses: tfstack/actions/.github/workflows/markdown-lint.yml@main

.vscode/extensions.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"recommendations": [
3+
"davidanson.vscode-markdownlint",
4+
"eamodio.gitlens",
5+
"esbenp.prettier-vscode",
6+
"foxundermoon.shell-format",
7+
"Gruntfuggly.todo-tree",
8+
"hashicorp.terraform",
9+
"mhutchie.git-graph",
10+
"streetsidesoftware.code-spell-checker",
11+
"usernamehw.errorlens",
12+
"vscode-icons-team.vscode-icons"
13+
]
14+
}

.vscode/settings.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"files.associations": {
3+
"*.dockerfile": "dockerfile",
4+
"*.sh.tpl": "shellscript",
5+
"docker-compose*.yml": "yaml",
6+
"Dockerfile*": "dockerfile"
7+
},
8+
"files.exclude": {
9+
"**/.git": true,
10+
"**/.svn": true,
11+
"**/.hg": true,
12+
"**/CVS": true,
13+
"**/.DS_Store": true,
14+
"__debug_bin": true,
15+
"vendor/": true,
16+
"go.sum": true
17+
},
18+
"files.trimTrailingWhitespace": true,
19+
"files.trimFinalNewlines": true,
20+
"files.insertFinalNewline": true,
21+
"remote.extensionKind": {
22+
"ms-azuretools.vscode-docker": "ui",
23+
"ms-vscode-remote.remote-containers": "ui"
24+
},
25+
"editor.formatOnSave": true,
26+
"editor.formatOnPaste": true,
27+
"prettier.requireConfig": true,
28+
"workbench.iconTheme": "vscode-icons",
29+
"[css]": {
30+
"editor.defaultFormatter": "vscode.css-language-features",
31+
"editor.foldingStrategy": "indentation"
32+
},
33+
"[dockerfile]": {
34+
"editor.defaultFormatter": "ms-azuretools.vscode-docker"
35+
},
36+
"[html]": {
37+
"editor.defaultFormatter": "vscode.html-language-features"
38+
},
39+
"[json]": {
40+
"editor.formatOnSave": true,
41+
"editor.defaultFormatter": "esbenp.prettier-vscode"
42+
},
43+
"[shellscript]": {
44+
"editor.defaultFormatter": "foxundermoon.shell-format"
45+
},
46+
"[terraform]": {
47+
"editor.formatOnSave": true,
48+
"editor.defaultFormatter": "hashicorp.terraform"
49+
}
50+
}

README.md

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,133 @@
11
# terraform-aws-s3-static-website
2-
Terraform module for deploying an AWS S3 static website with public access, website configuration, and index file support
2+
3+
Terraform module that deploys basic AWS S3 static website
4+
5+
## Requirements
6+
7+
| Name | Version |
8+
|------|---------|
9+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
10+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.0 |
11+
12+
## Providers
13+
14+
| Name | Version |
15+
|------|---------|
16+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.0 |
17+
| <a name="provider_aws.us_east_1"></a> [aws.us\_east\_1](#provider\_aws.us\_east\_1) | >= 4.0 |
18+
19+
## Modules
20+
21+
No modules.
22+
23+
## Resources
24+
25+
| Name | Type |
26+
|------|------|
27+
| [aws_acm_certificate.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource |
28+
| [aws_acm_certificate_validation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource |
29+
| [aws_cloudfront_distribution.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution) | resource |
30+
| [aws_cloudfront_origin_access_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_identity) | resource |
31+
| [aws_route53_record.cloudfront](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
32+
| [aws_route53_record.ssl_validation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
33+
| [aws_s3_bucket.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
34+
| [aws_s3_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
35+
| [aws_s3_bucket_acl.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
36+
| [aws_s3_bucket_lifecycle_configuration.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource |
37+
| [aws_s3_bucket_logging.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource |
38+
| [aws_s3_bucket_ownership_controls.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
39+
| [aws_s3_bucket_ownership_controls.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
40+
| [aws_s3_bucket_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
41+
| [aws_s3_bucket_public_access_block.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
42+
| [aws_s3_bucket_server_side_encryption_configuration.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
43+
| [aws_s3_bucket_versioning.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
44+
| [aws_s3_bucket_website_configuration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |
45+
| [aws_s3_object.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource |
46+
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
47+
| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |
48+
49+
## Inputs
50+
51+
### S3 Configuration
52+
53+
- **`s3_config`**: Configuration for the S3 bucket, including naming, access controls, and website settings.
54+
- **Attributes**:
55+
- **`bucket_name`**: Base name of the S3 bucket (`string`, required). Must be DNS-compliant (lowercase, numbers, hyphens only, and combined with `bucket_suffix` must not exceed 63 characters).
56+
- **`bucket_acl`**: Access control list for the S3 bucket (`string`, default: `"private"`). Allowed values: `"private"`, `"public-read"`.
57+
- **`bucket_suffix`**: Optional suffix for the bucket name (`string`, default: `""`). Must not exceed 16 characters.
58+
- **`enable_force_destroy`**: Force deletion of the bucket (`bool`, default: `false`).
59+
- **`object_ownership`**: Defines bucket ownership (`string`, default: `"BucketOwnerPreferred"`). Allowed values: `"BucketOwnerPreferred"`, `"ObjectWriter"`, `"BucketOwnerEnforced"`.
60+
- **`enable_versioning`**: Enable versioning for the bucket (`bool`, default: `false`).
61+
- **`index_document`**: Name of the index document (`string`, default: `"index.html"`). Cannot be empty.
62+
- **`error_document`**: Name of the error document (`string`, default: `""`).
63+
- **`public_access`**: Public access configuration for the bucket (`object`):
64+
- **`block_public_acls`**: Block public ACLs (`bool`, default: `true`).
65+
- **`block_public_policy`**: Block public bucket policies (`bool`, default: `true`).
66+
- **`ignore_public_acls`**: Ignore public ACLs (`bool`, default: `true`).
67+
- **`restrict_public_buckets`**: Restrict public buckets (`bool`, default: `true`).
68+
- **`source_file_path`**: Path to the local website files (`string`, default: `"/var/www"`). Cannot be empty.
69+
- **`allowed_principals`**: List of principals allowed access to the bucket (`list(string)`, default: `["*"]`). Must contain at least one principal.
70+
71+
---
72+
73+
### CDN Configuration
74+
75+
- **`cdn_config`**: Settings for enabling HTTPS, CloudFront, ACM, and optional custom domain configurations.
76+
- **Attributes**:
77+
- **`enable`**: Enable or disable CDN. When `false`, CloudFront-related outputs will be `null` (`bool`, default: `false`).
78+
- **`domain`**: Domain settings for the CDN (`object`).
79+
- **`name`**: Root domain name (`string`, default: `""`). Must be DNS-compliant.
80+
- **`sub_name`**: Subdomain name (`string`, default: `""`). Must be DNS-compliant.
81+
- **`ttl`**: Time-to-live for DNS records (`number`, default: `300`).
82+
- **`validation_method`**: ACM validation method (`string`, default: `"DNS"`).
83+
- **`origin_access_comment`**: Comment for the CloudFront origin access identity (`string`, default: `"Access Identity for S3 Origin"`).
84+
- **`allowed_methods`**: Allowed HTTP methods (`list(string)`, default: `["GET", "HEAD", "OPTIONS"]`). Must contain at least one method.
85+
- **`cached_methods`**: Cached HTTP methods (`list(string)`, default: `["GET", "HEAD"]`). Must contain at least one method.
86+
- **`enable_compression`**: Enable HTTP compression (`bool`, default: `true`).
87+
- **`protocol_policy`**: CloudFront protocol policy (`string`, default: `"redirect-to-https"`). Allowed values: `"allow-all"`, `"redirect-to-https"`, `"https-only"`.
88+
- **`forward_query_string`**: Forward query strings to the origin (`bool`, default: `false`).
89+
- **`forward_cookies`**: Cookie forwarding policy (`string`, default: `"none"`).
90+
- **`minimum_ttl`**: Minimum TTL for objects (`number`, default: `0`). Must be greater than or equal to `0`.
91+
- **`default_ttl`**: Default TTL for objects (`number`, default: `300`). Must be greater than or equal to `minimum_ttl`.
92+
- **`maximum_ttl`**: Maximum TTL for objects (`number`, default: `1200`). Must be greater than or equal to `default_ttl`.
93+
- **`price_class`**: CloudFront price class (`string`, default: `"PriceClass_All"`). Allowed values: `"PriceClass_All"`, `"PriceClass_200"`, `"PriceClass_100"`.
94+
- **`error_page_path`**: Path for custom error pages (`string`, default: `"/error.html"`).
95+
- **`error_page_cache_ttl`**: TTL for caching error pages (`number`, default: `300`).
96+
- **`ssl_support_method`**: SSL support method for CloudFront (`string`, default: `"sni-only"`). Allowed values: `"sni-only"`, `"vip"`.
97+
- **`minimum_tls_version`**: Minimum TLS version for HTTPS (`string`, default: `"TLSv1.2_2021"`). Allowed values: `"SSLv3"`, `"TLSv1"`, `"TLSv1.1"`, `"TLSv1.2"`, `"TLSv1.2_2018"`, `"TLSv1.2_2021"`.
98+
- **`geo_restriction_policy`**: Geo-restriction policy (`string`, default: `"none"`). Allowed values: `"none"`, `"whitelist"`, `"blacklist"`.
99+
100+
---
101+
102+
### Logging Configuration
103+
104+
- **`logging_config`**: Configuration for S3 bucket logging.
105+
- **Attributes**:
106+
- **`enable`**: Enable or disable logging. When `false`, `s3_logging_bucket` output will be `null` (`bool`, default: `false`).
107+
- **`s3_prefix`**: Prefix for logging files in S3 (`string`, default: `"s3/"`). Must be a valid string.
108+
- **`cloudfront_prefix`**: Prefix for CloudFront logs in S3 (`string`, default: `"cloudfront/"`). Must be a valid string.
109+
- **`log_retention_days`**: Retention period for logs (`number`, default: `30`).
110+
- **`enable_encryption`**: Enable encryption for logs (`bool`, default: `true`).
111+
- **`encryption_algorithm`**: Algorithm for log encryption (`string`, default: `"AES256"`).
112+
113+
---
114+
115+
### Tags
116+
117+
- **`tags`**: Map of tags to assign to the resources (`map(string)`, default: `{}`).
118+
119+
## Outputs
120+
121+
| Name | Description |
122+
|------|-------------|
123+
| <a name="output_acm_certificate_arn"></a> [acm\_certificate\_arn](#output\_acm\_certificate\_arn) | The ARN of the ACM certificate used for the CloudFront distribution, if HTTPS is enabled. |
124+
| <a name="output_cloudfront_distribution_arn"></a> [cloudfront\_distribution\_arn](#output\_cloudfront\_distribution\_arn) | The ARN of the CloudFront distribution, if CDN is enabled. Null if CDN is disabled. |
125+
| <a name="output_cloudfront_distribution_id"></a> [cloudfront\_distribution\_id](#output\_cloudfront\_distribution\_id) | The ID of the CloudFront distribution, if CDN is enabled. Null if CDN is disabled. |
126+
| <a name="output_cloudfront_dns_name"></a> [cloudfront\_dns\_name](#output\_cloudfront\_dns\_name) | The DNS name for the CloudFront distribution, managed by Route 53, if CDN is enabled. |
127+
| <a name="output_cloudfront_website_url"></a> [cloudfront\_website\_url](#output\_cloudfront\_website\_url) | The website URL served through CloudFront when CDN is enabled. Empty if CDN is disabled. |
128+
| <a name="output_s3_bucket_arn"></a> [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the S3 bucket. |
129+
| <a name="output_s3_bucket_id"></a> [s3\_bucket\_id](#output\_s3\_bucket\_id) | The unique ID of the S3 bucket. |
130+
| <a name="output_s3_bucket_region"></a> [s3\_bucket\_region](#output\_s3\_bucket\_region) | The AWS region where the S3 bucket is deployed. |
131+
| <a name="output_s3_logging_bucket"></a> [s3\_logging\_bucket](#output\_s3\_logging\_bucket) | The ID of the S3 bucket used for logging, if logging is enabled. Null if logging is disabled. |
132+
| <a name="output_s3_website_url"></a> [s3\_website\_url](#output\_s3\_website\_url) | The HTTP URL of the S3 static website. Note: HTTPS is not natively supported by S3. |
133+
| <a name="output_website_url"></a> [website\_url](#output\_website\_url) | The dynamic website URL, using Route 53 custom domain if CDN is enabled, otherwise S3. |

examples/cdn/README.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# CloudFront CDN with S3 Static Website
2+
3+
This Terraform module creates an Amazon S3 bucket configured for static website hosting. It also integrates with CloudFront for global content delivery and Route 53 for custom domain configuration.
4+
5+
---
6+
7+
## Features
8+
9+
- Creates an S3 bucket for static website hosting.
10+
- Configures index and error documents.
11+
- Supports versioning and public access controls.
12+
- Integrates with CloudFront for CDN.
13+
- Optionally supports Route 53 for custom domain or subdomain configuration.
14+
- Configures HTTPS with an ACM certificate.
15+
16+
---
17+
18+
## Example Usage
19+
20+
```hcl
21+
provider "aws" {
22+
region = "ap-southeast-2"
23+
}
24+
25+
resource "random_string" "suffix" {
26+
length = 8
27+
special = false
28+
upper = false
29+
}
30+
31+
module "s3_static_website" {
32+
source = "../.."
33+
34+
s3_config = {
35+
bucket_name = "s3-static-site"
36+
bucket_acl = "public-read"
37+
bucket_suffix = random_string.suffix.result
38+
enable_force_destroy = true
39+
object_ownership = "BucketOwnerPreferred"
40+
enable_versioning = true
41+
index_document = "index.html"
42+
error_document = "error.html"
43+
public_access = {
44+
block_public_acls = false
45+
block_public_policy = false
46+
ignore_public_acls = false
47+
restrict_public_buckets = false
48+
}
49+
source_file_path = "${path.module}/external"
50+
allowed_principals = ["*"]
51+
}
52+
53+
cdn_config = {
54+
enable = true
55+
domain = {
56+
name = "example.com"
57+
sub_name = "web"
58+
ttl = 300
59+
}
60+
validation_method = "DNS"
61+
origin_access_comment = "Access Identity for S3 Origin"
62+
allowed_methods = ["GET", "HEAD", "OPTIONS"]
63+
cached_methods = ["GET", "HEAD", "OPTIONS"]
64+
enable_compression = true
65+
protocol_policy = "redirect-to-https"
66+
forward_query_string = false
67+
forward_cookies = "none"
68+
minimum_ttl = 0
69+
default_ttl = 300
70+
maximum_ttl = 1200
71+
price_class = "PriceClass_All"
72+
error_page_path = "/error.html"
73+
error_page_cache_ttl = 300
74+
ssl_support_method = "sni-only"
75+
minimum_tls_version = "TLSv1.2_2021"
76+
geo_restriction_policy = "none"
77+
}
78+
79+
tags = {
80+
Name = "s3-static-site-${random_string.suffix.result}"
81+
}
82+
}
83+
```
84+
85+
---
86+
87+
## Inputs
88+
89+
| Name | Description | Type | Default |
90+
|------------------------|----------------------------------------------------|---------------|---------------|
91+
| `bucket_name` | The base name of the S3 bucket. | `string` | N/A |
92+
| `bucket_suffix` | Optional suffix for the bucket name. | `string` | `""` |
93+
| `enable_versioning` | Enable versioning for the S3 bucket. | `bool` | `false` |
94+
| `enable_force_destroy` | Force bucket deletion. | `bool` | `false` |
95+
| `object_ownership` | Defines bucket ownership. | `string` | `"BucketOwnerPreferred"` |
96+
| `index_document` | The name of the index document. | `string` | `"index.html"`|
97+
| `error_document` | The name of the error document. | `string` | `"error.html"`|
98+
| `source_file_path` | Path to the local website files. | `string` | N/A |
99+
| `allowed_principals` | List of principals allowed access to the bucket. | `list(string)`| `["*"]` |
100+
| `bucket_acl` | ACL for the S3 bucket. | `string` | `"private"` |
101+
| `public_access` | Configuration for public access settings. | `map(bool)` | N/A |
102+
| `cdn_config` | Optional CloudFront configuration. | `object` | See Example |
103+
| `tags` | Tags to apply to the S3 bucket. | `map(string)` | `{}` |
104+
105+
---
106+
107+
## Outputs
108+
109+
| Name | Description |
110+
|------------------------|----------------------------------------------------|
111+
| `cloudfront_website_url` | The CloudFront distribution URL. |
112+
| `website_url` | Dynamic website URL (custom domain or S3 endpoint).|
113+
| `s3_website_url` | The endpoint URL of the S3 static website. |
114+
115+
---
116+
117+
## Important Notes
118+
119+
- **Custom Domain with Route 53**:
120+
- If a custom domain is enabled, a Route 53 record is created for the specified domain and subdomain.
121+
- The `bucket_name` and `bucket_suffix` should be unique to avoid conflicts.
122+
123+
- **S3 and CloudFront Restrictions**:
124+
- The S3 bucket name and custom domain name do not need to match, but the domain configuration must correctly point to the S3 bucket's website endpoint.
125+
126+
- **Public Access**:
127+
- To make the website public, configure `public_access` and `bucket_acl` appropriately.
128+
129+
- **ACM Certificate Region**:
130+
- The ACM certificate **must be created in the `us-east-1` region** for CloudFront to validate and use it correctly. Ensure the ACM provider explicitly specifies this region.
131+
132+
---
133+
134+
## License
135+
136+
MIT License. See `LICENSE` for more information.
137+
138+
---
139+
140+
## Author
141+
142+
Created by John Ajera. Contributions are welcome!
740 KB
Loading

0 commit comments

Comments
 (0)