From e7a30e043238a6e488a1f66842777ce0fb706e08 Mon Sep 17 00:00:00 2001 From: solo5star Date: Tue, 28 Nov 2023 14:41:31 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20AWS=20=EC=9D=B8=ED=94=84=EB=9D=BC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=9F=AD=EC=B3=90(cloudfront,=20s3)=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=20terraform=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infrastructure/.gitignore | 34 +++++++++ infrastructure/.terraform.lock.hcl | 25 +++++++ infrastructure/acm.tf | 15 ++++ infrastructure/cloudfront.tf | 110 +++++++++++++++++++++++++++++ infrastructure/main.tf | 31 ++++++++ infrastructure/s3.tf | 50 +++++++++++++ infrastructure/vars.tf | 19 +++++ 7 files changed, 284 insertions(+) create mode 100644 infrastructure/.gitignore create mode 100644 infrastructure/.terraform.lock.hcl create mode 100644 infrastructure/acm.tf create mode 100644 infrastructure/cloudfront.tf create mode 100644 infrastructure/main.tf create mode 100644 infrastructure/s3.tf create mode 100644 infrastructure/vars.tf diff --git a/infrastructure/.gitignore b/infrastructure/.gitignore new file mode 100644 index 00000000..9b8a46e6 --- /dev/null +++ b/infrastructure/.gitignore @@ -0,0 +1,34 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/infrastructure/.terraform.lock.hcl b/infrastructure/.terraform.lock.hcl new file mode 100644 index 00000000..f589bcff --- /dev/null +++ b/infrastructure/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.26.0" + constraints = "~> 5.0" + hashes = [ + "h1:j9vZkUHI7/jTbv7ORkVmEF5MADuFKRygSIyqy0oI2C4=", + "zh:11a4062491e574c8e96b6bc7ced67b5e9338ccfa068223fc9042f9e1e7eda47a", + "zh:4331f85aeb22223ab656d04b48337a033f44f02f685c8def604c4f8f4687d10f", + "zh:915d6c996390736709f7ac7582cd41418463cfc07696218af6fea4a282df744a", + "zh:9306c306dbb2e1597037c54d20b1bd5f22a9cdcdb2b2b7bad657c8230bea2298", + "zh:93371860b9df369243219606711bfd3cfbd263db67838c06d5d5848cf47b6ede", + "zh:98338c17764a7b9322ddb6efd3af84e6890a4a0687f846eefdfb0fa03cec892d", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:a28c9d77a5be25bac42d99418365757e4eb65a2c7c6788828263772cf2774869", + "zh:bd9c4648a090622d6b8c3c91dad513eec81e54db3dfe940ab6d155e5f37735e5", + "zh:bde63db136cccdeb282489e2ec2b3f9a7566edc9df27911a296352ab00832261", + "zh:ccd33f9490ce3f2d89efab995abf3b30e75579585f6a8a5b1f756246903d3518", + "zh:d73d1c461eb9d22833251f6533fc214cf014bc1d3165c5bfaa8ca29cd295ffb2", + "zh:db4ffb7eec5d0e1d0dbd0d65e1a3eaa6173a3337058105aec41fd0b2af5a2b46", + "zh:eb36b933419e9f6563330f3b7d53d4f1b09e62d27f7786d5dc6c4a2d0f6de182", + "zh:ec85ce1976e43f7d7fa10fa191c0a85e97326a3cb22387c0ed8b74d426ec94fd", + ] +} diff --git a/infrastructure/acm.tf b/infrastructure/acm.tf new file mode 100644 index 00000000..2a882932 --- /dev/null +++ b/infrastructure/acm.tf @@ -0,0 +1,15 @@ +resource "aws_acm_certificate" "default" { + provider = aws.virginia + domain_name = var.domain_name + validation_method = "DNS" + key_algorithm = "EC_prime256v1" + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_acm_certificate_validation" "default" { + provider = aws.virginia + certificate_arn = aws_acm_certificate.default.arn +} diff --git a/infrastructure/cloudfront.tf b/infrastructure/cloudfront.tf new file mode 100644 index 00000000..0a682493 --- /dev/null +++ b/infrastructure/cloudfront.tf @@ -0,0 +1,110 @@ +resource "aws_cloudfront_origin_access_control" "default" { + name = "${var.project_name} CloudFront OAC" + description = "${var.project_name} CloudFront S3 OAC" + origin_access_control_origin_type = "s3" + signing_behavior = "always" + signing_protocol = "sigv4" +} + +resource "aws_cloudfront_distribution" "default" { + enabled = true + comment = var.project_name + aliases = [var.domain_name] + + is_ipv6_enabled = true + default_root_object = "index.html" + + http_version = "http2and3" + + origin { + origin_id = "App" + domain_name = aws_s3_bucket.default.bucket_regional_domain_name + origin_path = "/app" + origin_access_control_id = aws_cloudfront_origin_access_control.default.id + } + + origin { + origin_id = "Images" + domain_name = aws_s3_bucket.default.bucket_regional_domain_name + origin_access_control_id = aws_cloudfront_origin_access_control.default.id + } + + origin { + origin_id = "API" + domain_name = var.api_domain_name + + custom_origin_config { + http_port = 80 + https_port = 443 + origin_protocol_policy = "http-only" + origin_ssl_protocols = ["TLSv1.2"] + } + } + + custom_error_response { + error_code = 403 + response_code = 200 + response_page_path = "/index.html" + } + + custom_error_response { + error_code = 404 + response_code = 200 + response_page_path = "/index.html" + } + + default_cache_behavior { + target_origin_id = "App" + allowed_methods = ["GET", "HEAD"] + cached_methods = ["GET", "HEAD"] + compress = true + cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id + viewer_protocol_policy = "redirect-to-https" + } + + ordered_cache_behavior { + target_origin_id = "API" + path_pattern = "/api/*" + allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"] + cached_methods = ["GET", "HEAD", "OPTIONS"] + compress = false + cache_policy_id = data.aws_cloudfront_cache_policy.caching_disabled.id + viewer_protocol_policy = "redirect-to-https" + origin_request_policy_id = data.aws_cloudfront_origin_request_policy.all_viewer_except_host_header.id + } + + ordered_cache_behavior { + target_origin_id = "Images" + path_pattern = "/images/*" + allowed_methods = ["GET", "HEAD"] + cached_methods = ["GET", "HEAD"] + compress = true + cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id + viewer_protocol_policy = "redirect-to-https" + } + + viewer_certificate { + cloudfront_default_certificate = true + acm_certificate_arn = aws_acm_certificate_validation.default.certificate_arn + minimum_protocol_version = "TLSv1.2_2021" + ssl_support_method = "sni-only" + } + + restrictions { + geo_restriction { + restriction_type = "none" + } + } +} + +data "aws_cloudfront_cache_policy" "caching_optimized" { + name = "Managed-CachingOptimized" +} + +data "aws_cloudfront_cache_policy" "caching_disabled" { + name = "Managed-CachingDisabled" +} + +data "aws_cloudfront_origin_request_policy" "all_viewer_except_host_header" { + name = "Managed-AllViewerExceptHostHeader" +} diff --git a/infrastructure/main.tf b/infrastructure/main.tf new file mode 100644 index 00000000..8d7822a4 --- /dev/null +++ b/infrastructure/main.tf @@ -0,0 +1,31 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = "ap-northeast-2" + + default_tags { + tags = { + Project = var.project_name + Environment = "Production" + } + } +} + +provider "aws" { + alias = "virginia" + region = "us-east-1" + + default_tags { + tags = { + Project = var.project_name + Environment = "Production" + } + } +} diff --git a/infrastructure/s3.tf b/infrastructure/s3.tf new file mode 100644 index 00000000..d836fc23 --- /dev/null +++ b/infrastructure/s3.tf @@ -0,0 +1,50 @@ +resource "aws_s3_bucket" "default" { + bucket = var.bucket_name +} + +resource "aws_s3_bucket_public_access_block" "default" { + bucket = aws_s3_bucket.default.id + + block_public_acls = true + block_public_policy = true + restrict_public_buckets = true + ignore_public_acls = true +} + +resource "aws_s3_bucket_policy" "default" { + bucket = aws_s3_bucket.default.id + depends_on = [aws_s3_bucket_public_access_block.default] + policy = data.aws_iam_policy_document.default.json +} + +resource "aws_s3_object" "images" { + bucket = aws_s3_bucket.default.id + key = "images/" +} + +resource "aws_s3_object" "app" { + bucket = aws_s3_bucket.default.id + key = "app/" +} + +data "aws_iam_policy_document" "default" { + statement { + sid = "Allow all HTTP from cloudfront" + principals { + type = "Service" + identifiers = ["cloudfront.amazonaws.com"] + } + actions = [ + "s3:GetObject" + ] + resources = [ + "${aws_s3_bucket.default.arn}/*" + ] + effect = "Allow" + condition { + test = "StringEquals" + variable = "AWS:SourceArn" + values = [aws_cloudfront_distribution.default.arn] + } + } +} diff --git a/infrastructure/vars.tf b/infrastructure/vars.tf new file mode 100644 index 00000000..f5771349 --- /dev/null +++ b/infrastructure/vars.tf @@ -0,0 +1,19 @@ +variable "project_name" { + type = string + description = "The unique name of the project (IMPORTANT: this value will be used for tagging) (e.g. yozm-cafe)" +} + +variable "bucket_name" { + type = string + description = "The name of the target bucket where static files will be deployed. (e.g. yozm-cafe)" +} + +variable "domain_name" { + type = string + description = "The domain name that will be connected to CloudFront. (e.g. yozm.cafe)" +} + +variable "api_domain_name" { + type = string + description = "The domain name that will be used for API request. (e.g. api.yozm.cafe)" +}