From 1d095e337a70d4403e5db14540f0d4447f00c98d Mon Sep 17 00:00:00 2001 From: sid palas Date: Wed, 5 May 2021 08:13:24 -0700 Subject: [PATCH] initial commit --- .gitignore | 2 + 01-prerequisites/README.md | 8 + 02-overview/main.tf | 17 ++ 03-basics/README.md | 4 + 03-basics/aws-backend/README.md | 31 +++ 03-basics/aws-backend/bootstrap/main.tf | 47 ++++ .../aws-backend/import-bootstrap/main.tf | 47 ++++ 03-basics/terraform-cloud-backend/README.md | 8 + 03-basics/terraform-cloud-backend/main.tf | 20 ++ 03-basics/web-app/README.md | 14 ++ 03-basics/web-app/architecture.png | Bin 0 -> 49436 bytes 03-basics/web-app/main.tf | 210 +++++++++++++++++ 04-variables-and-outputs/examples/README.md | 71 ++++++ .../examples/another-variable-file.tfvars | 1 + 04-variables-and-outputs/examples/main.tf | 47 ++++ 04-variables-and-outputs/examples/outputs.tf | 7 + .../examples/terraform.tfvars | 3 + .../examples/variables.tf | 30 +++ 04-variables-and-outputs/web-app/main.tf | 211 ++++++++++++++++++ 04-variables-and-outputs/web-app/outputs.tf | 11 + .../web-app/terraform.tfvars | 5 + 04-variables-and-outputs/web-app/variables.tf | 55 +++++ 05-organization-and-modules/README.md | 4 + .../web-app-module/compute/main.tf | 0 .../web-app-module/main.tf | 198 ++++++++++++++++ .../web-app-module/outputs.tf | 11 + .../web-app-module/variables.tf | 55 +++++ 05-organization-and-modules/web-app/main.tf | 51 +++++ 28 files changed, 1168 insertions(+) create mode 100644 .gitignore create mode 100644 01-prerequisites/README.md create mode 100644 02-overview/main.tf create mode 100644 03-basics/README.md create mode 100644 03-basics/aws-backend/README.md create mode 100644 03-basics/aws-backend/bootstrap/main.tf create mode 100644 03-basics/aws-backend/import-bootstrap/main.tf create mode 100644 03-basics/terraform-cloud-backend/README.md create mode 100644 03-basics/terraform-cloud-backend/main.tf create mode 100644 03-basics/web-app/README.md create mode 100644 03-basics/web-app/architecture.png create mode 100644 03-basics/web-app/main.tf create mode 100644 04-variables-and-outputs/examples/README.md create mode 100644 04-variables-and-outputs/examples/another-variable-file.tfvars create mode 100644 04-variables-and-outputs/examples/main.tf create mode 100644 04-variables-and-outputs/examples/outputs.tf create mode 100644 04-variables-and-outputs/examples/terraform.tfvars create mode 100644 04-variables-and-outputs/examples/variables.tf create mode 100644 04-variables-and-outputs/web-app/main.tf create mode 100644 04-variables-and-outputs/web-app/outputs.tf create mode 100644 04-variables-and-outputs/web-app/terraform.tfvars create mode 100644 04-variables-and-outputs/web-app/variables.tf create mode 100644 05-organization-and-modules/README.md create mode 100644 05-organization-and-modules/web-app-module/compute/main.tf create mode 100644 05-organization-and-modules/web-app-module/main.tf create mode 100644 05-organization-and-modules/web-app-module/outputs.tf create mode 100644 05-organization-and-modules/web-app-module/variables.tf create mode 100644 05-organization-and-modules/web-app/main.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca17c2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.terraform* +*.tfstate* \ No newline at end of file diff --git a/01-prerequisites/README.md b/01-prerequisites/README.md new file mode 100644 index 0000000..e79466b --- /dev/null +++ b/01-prerequisites/README.md @@ -0,0 +1,8 @@ +## Install Terraform +1) install terraform + +## AWS Account Setup +2) create non-root AWS user +3) Add AmazonEC2FullAccess +4) Save Access key + secret key (or use AWS CLI `aws configure` -- https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) + diff --git a/02-overview/main.tf b/02-overview/main.tf new file mode 100644 index 0000000..3008f18 --- /dev/null +++ b/02-overview/main.tf @@ -0,0 +1,17 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} + +provider "aws" { + region = "us-east-1" +} + +resource "aws_instance" "example" { + ami = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1 + instance_type = "t2.micro" +} diff --git a/03-basics/README.md b/03-basics/README.md new file mode 100644 index 0000000..4761e3f --- /dev/null +++ b/03-basics/README.md @@ -0,0 +1,4 @@ +1) Create account credentials + + + diff --git a/03-basics/aws-backend/README.md b/03-basics/aws-backend/README.md new file mode 100644 index 0000000..340feed --- /dev/null +++ b/03-basics/aws-backend/README.md @@ -0,0 +1,31 @@ +Steps to initialize backend in AWS and manage it with Terraform: + +1) Use config from `bootstrap` (init, plan, apply) to provision s3 bucket and dynamoDB table with local state +2) copy state file into import-bootstrap + 1) cp terraform.tfstate ../import-bootstrap +3) Initialize within `import-bootstrap` using `terraform init` +4) Uncomment out s3 backend provider: + +``` + backend "s3" { + bucket = "devops-directive-tf-state" + key = "tf-infra/terraform.tfstate" + region = "us-east-1" + dynamodb_table = "terraform-state-locking" + encrypt = true + } +``` + +4) Reinitialize with `terraform init`: + +``` +Do you want to copy existing state to the new backend? + Pre-existing state was found while migrating the previous "local" backend to the + newly configured "s3" backend. No existing state was found in the newly + configured "s3" backend. Do you want to copy this state to the new "s3" + backend? Enter "yes" to copy and "no" to start with an empty state. + + Enter a value: yes +``` + +Now the S3 bucket and dynamoDB table are managed by Terraform and are able to be used as the state backend! \ No newline at end of file diff --git a/03-basics/aws-backend/bootstrap/main.tf b/03-basics/aws-backend/bootstrap/main.tf new file mode 100644 index 0000000..5706a84 --- /dev/null +++ b/03-basics/aws-backend/bootstrap/main.tf @@ -0,0 +1,47 @@ +terraform { + # THIS BACKEND CONFIG GETS UNCOMMENTED IN IMPORT-BOOTSTRAP + # backend "s3" { + # bucket = "devops-directive-tf-state" + # key = "03-basics/import-bootstrap/terraform.tfstate" + # region = "us-east-1" + # dynamodb_table = "terraform-state-locking" + # encrypt = true + # } + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} + +provider "aws" { + region = "us-east-1" +} + +resource "aws_s3_bucket" "terraform_state" { + bucket = "devops-directive-tf-state" + force_destroy = true + versioning { + enabled = true + } + + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } + } +} + +resource "aws_dynamodb_table" "terraform_locks" { + name = "terraform-state-locking" + billing_mode = "PAY_PER_REQUEST" + hash_key = "LockID" + attribute { + name = "LockID" + type = "S" + } +} diff --git a/03-basics/aws-backend/import-bootstrap/main.tf b/03-basics/aws-backend/import-bootstrap/main.tf new file mode 100644 index 0000000..492cd89 --- /dev/null +++ b/03-basics/aws-backend/import-bootstrap/main.tf @@ -0,0 +1,47 @@ +terraform { + ### UNCOMMENT THIS AFTER INITIALIZNG ### + # backend "s3" { + # bucket = "devops-directive-tf-state" + # key = "03-basics/import-bootstrap/terraform.tfstate" + # region = "us-east-1" + # dynamodb_table = "terraform-state-locking" + # encrypt = true + # } + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} + +provider "aws" { + region = "us-east-1" +} + +resource "aws_s3_bucket" "terraform_state" { + bucket = "devops-directive-tf-state" + force_destroy = true + versioning { + enabled = true + } + + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } + } +} + +resource "aws_dynamodb_table" "terraform_locks" { + name = "terraform-state-locking" + billing_mode = "PAY_PER_REQUEST" + hash_key = "LockID" + attribute { + name = "LockID" + type = "S" + } +} diff --git a/03-basics/terraform-cloud-backend/README.md b/03-basics/terraform-cloud-backend/README.md new file mode 100644 index 0000000..63738e1 --- /dev/null +++ b/03-basics/terraform-cloud-backend/README.md @@ -0,0 +1,8 @@ +## Terraform Cloud Account Setup +1) Create account at terraform.io +2) Use terraform login +3) Set any necessary credentials for whichever cloud services you are using because with terraform cloud backend, the plan/apply are actually run remotely. +``` +# AWS_ACCESS_KEY_ID +# AWS_SECRET_ACCESS_KEY +``` \ No newline at end of file diff --git a/03-basics/terraform-cloud-backend/main.tf b/03-basics/terraform-cloud-backend/main.tf new file mode 100644 index 0000000..7063235 --- /dev/null +++ b/03-basics/terraform-cloud-backend/main.tf @@ -0,0 +1,20 @@ +terraform { + backend "remote" { + organization = "devops-directive" + + workspaces { + name = "devops-directive-terraform-course" + } + } + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} + +provider "aws" { + region = "us-east-1" +} diff --git a/03-basics/web-app/README.md b/03-basics/web-app/README.md new file mode 100644 index 0000000..501fd13 --- /dev/null +++ b/03-basics/web-app/README.md @@ -0,0 +1,14 @@ +## DNS Nameservers + +Add AWS Nameservers so that Route 53 settings will be applied: +- ns-1147.awsdns-15.org +- ns-161.awsdns-20.com +- ns-1629.awsdns-11.co.uk +- ns-876.awsdns-45.net + +## Architecture +![](architecture.png) + +## Notes: +- Had to add security group with IP of ec2 instance for inbound access (by default inbound traffic was blocked) +- To connect to DB (`psql -U foo -d mydb -p 5432 -h terraform-20210127022433201300000001.cr2ub9wmsmpg.us-east-1.rds.amazonaws.com`) diff --git a/03-basics/web-app/architecture.png b/03-basics/web-app/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..c89aeae4f9e9208390e58220d012d4bf5c817b39 GIT binary patch literal 49436 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 z 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
  • ug^0^1sKBbjWlm*Vaf$&D+~>R~UWvr{m<^*Y_q41spvVmY+`& zj-7i^iS<Yz}n{Qe`o#32el7L1Tkim0pcJq} zel2Y~8(7?Wc2sr+s%IMvnL6B@Yo#O+$2VwVxqpEd+NIJrs`;cbe_(?T_O|C6K0t#k zX!?4R(~I5|5gea3+bCKV+?D*FAt)%ZjY|DRv{=PZT$B2exad+wcZ@{036h`XR6 index.html + python3 -m http.server 8080 & + EOF +} + +resource "aws_instance" "instance_2" { + ami = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1 + instance_type = "t2.micro" + security_groups = [aws_security_group.instances.name] + user_data = <<-EOF + #!/bin/bash + echo "Hello, World 2" > index.html + python3 -m http.server 8080 & + EOF +} + +resource "aws_s3_bucket" "bucket" { + bucket = "devops-directive-web-app-data" + force_destroy = true + versioning { + enabled = true + } + + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } + } +} + +data "aws_vpc" "default_vpc" { + default = true +} + +data "aws_subnet_ids" "default_subnet" { + vpc_id = data.aws_vpc.default_vpc.id +} + +resource "aws_security_group" "instances" { + name = "instance-security-group" +} + +resource "aws_security_group_rule" "allow_http_inbound" { + type = "ingress" + security_group_id = aws_security_group.instances.id + + from_port = 8080 + to_port = 8080 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] +} + +resource "aws_lb_listener" "http" { + load_balancer_arn = aws_lb.load_balancer.arn + + port = 80 + + protocol = "HTTP" + + # By default, return a simple 404 page + default_action { + type = "fixed-response" + + fixed_response { + content_type = "text/plain" + message_body = "404: page not found" + status_code = 404 + } + } +} + +resource "aws_lb_target_group" "instances" { + name = "example-target-group" + port = 8080 + protocol = "HTTP" + vpc_id = data.aws_vpc.default_vpc.id + + health_check { + path = "/" + protocol = "HTTP" + matcher = "200" + interval = 15 + timeout = 3 + healthy_threshold = 2 + unhealthy_threshold = 2 + } +} + +resource "aws_lb_target_group_attachment" "instance_1" { + target_group_arn = aws_lb_target_group.instances.arn + target_id = aws_instance.instance_1.id + port = 8080 +} + +resource "aws_lb_target_group_attachment" "instance_2" { + target_group_arn = aws_lb_target_group.instances.arn + target_id = aws_instance.instance_2.id + port = 8080 +} + +resource "aws_lb_listener_rule" "instances" { + listener_arn = aws_lb_listener.http.arn + priority = 100 + + condition { + path_pattern { + values = ["*"] + } + } + + action { + type = "forward" + target_group_arn = aws_lb_target_group.instances.arn + } +} + + +resource "aws_security_group" "alb" { + name = "alb-security-group" +} + +resource "aws_security_group_rule" "allow_alb_http_inbound" { + type = "ingress" + security_group_id = aws_security_group.alb.id + + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + +} + +resource "aws_security_group_rule" "allow_alb_all_outbound" { + type = "egress" + security_group_id = aws_security_group.alb.id + + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + +} + + +resource "aws_lb" "load_balancer" { + name = "web-app-lb" + load_balancer_type = "application" + subnets = data.aws_subnet_ids.default_subnet.ids + security_groups = [aws_security_group.alb.id] + +} + +resource "aws_route53_zone" "primary" { + name = "mysuperawesomesite.com" +} + +resource "aws_route53_record" "root" { + zone_id = aws_route53_zone.primary.zone_id + name = "mysuperawesomesite.com" + type = "A" + + alias { + name = aws_lb.load_balancer.dns_name + zone_id = aws_lb.load_balancer.zone_id + evaluate_target_health = true + } +} + +resource "aws_db_instance" "db_instance" { + allocated_storage = 20 + storage_type = "standard" + engine = "postgres" + engine_version = "12.5" + instance_class = "db.t2.micro" + name = "mydb" + username = "foo" + password = "foobarbaz" + skip_final_snapshot = true +} diff --git a/04-variables-and-outputs/examples/README.md b/04-variables-and-outputs/examples/README.md new file mode 100644 index 0000000..3664729 --- /dev/null +++ b/04-variables-and-outputs/examples/README.md @@ -0,0 +1,71 @@ +# Variables + +## Variable block + +must define variable block + +``` +variable "var_name" { + type = string +} +``` + +## Variable types +- string +- number +- bool +- list() +- set() +- map() +- object({ = , ... }) +- tuple([, ...]) + +## Variable files +`variables.tfvars` (or `.auto.tfvars`) automatically applied + +## Apply default +`terraform apply` + +## Apply a different variable file +`terraform apply -var-file=another-variable-file.tfvars` + +## Passing Variable via Prompt +If value not specified, Terraform will prompt for value. (this is okay for testing... but don't depend on it since you should be automating things!) +``` + var.db_pass + password for database + + Enter a value: +``` + +## Passing Variables via CLI +`terraform apply -var="db_pass=$DB_PASS_ENV_VAR"` + +# Local Variables + +Allows you to store the value of expression for reuse but doesn't allow for passing in values +``` +locals { + extra_tag = "extra-tag" +} +``` + +# Output Variables + +Allows you to output some value (which might not be known ahead of time). + +For example it might be useful to know the IP address of a VM that was created: + +``` +output "instance_ip_addr" { + value = aws_instance.instance.private_ip +} +``` + +Sample output: +``` +db_instance_addr = "terraform-20210504182745335900000001.cr2ub9wmsmpg.us-east-1.rds.amazonaws.com" +instance_ip_addr = "172.31.24.95" +``` + +Will be output after `terraform apply` or `terraform output` diff --git a/04-variables-and-outputs/examples/another-variable-file.tfvars b/04-variables-and-outputs/examples/another-variable-file.tfvars new file mode 100644 index 0000000..7270112 --- /dev/null +++ b/04-variables-and-outputs/examples/another-variable-file.tfvars @@ -0,0 +1 @@ +instance_name = "hello-world-2" diff --git a/04-variables-and-outputs/examples/main.tf b/04-variables-and-outputs/examples/main.tf new file mode 100644 index 0000000..baf60c6 --- /dev/null +++ b/04-variables-and-outputs/examples/main.tf @@ -0,0 +1,47 @@ +terraform { + backend "s3" { + bucket = "devops-directive-tf-state" + key = "04-variables-and-outputs/examples/terraform.tfstate" + region = "us-east-1" + dynamodb_table = "terraform-state-locking" + encrypt = true + } + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} + +provider "aws" { + region = "us-east-1" +} + +locals { + extra_tag = "extra-tag" +} + +resource "aws_instance" "instance" { + ami = var.ami + instance_type = var.instance_type + + tags = { + Name = var.instance_name + ExtraTag = local.extra_tag + } +} + +resource "aws_db_instance" "db_instance" { + allocated_storage = 20 + storage_type = "gp2" + engine = "postgres" + engine_version = "12.4" + instance_class = "db.t2.micro" + name = "mydb" + username = var.db_user + password = var.db_pass + skip_final_snapshot = true +} + diff --git a/04-variables-and-outputs/examples/outputs.tf b/04-variables-and-outputs/examples/outputs.tf new file mode 100644 index 0000000..42d6ab7 --- /dev/null +++ b/04-variables-and-outputs/examples/outputs.tf @@ -0,0 +1,7 @@ +output "instance_ip_addr" { + value = aws_instance.instance.private_ip +} + +output "db_instance_addr" { + value = aws_db_instance.db_instance.address +} diff --git a/04-variables-and-outputs/examples/terraform.tfvars b/04-variables-and-outputs/examples/terraform.tfvars new file mode 100644 index 0000000..5723e98 --- /dev/null +++ b/04-variables-and-outputs/examples/terraform.tfvars @@ -0,0 +1,3 @@ +instance_name = "hello-world" +ami = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1 +instance_type = "t2.micro" \ No newline at end of file diff --git a/04-variables-and-outputs/examples/variables.tf b/04-variables-and-outputs/examples/variables.tf new file mode 100644 index 0000000..b3d5211 --- /dev/null +++ b/04-variables-and-outputs/examples/variables.tf @@ -0,0 +1,30 @@ +# should specify optional vs required + +variable "instance_name" { + description = "Name of ec2 instance" + type = string +} + +variable "ami" { + description = "Amazon machine image to use for ec2 instance" + type = string + default = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1 +} + +variable "instance_type" { + description = "ec2 instance type" + type = string + default = "t2.micro" +} + +variable "db_user" { + description = "username for database" + type = string + default = "foo" +} + +variable "db_pass" { + description = "password for database" + type = string + sensitive = true +} diff --git a/04-variables-and-outputs/web-app/main.tf b/04-variables-and-outputs/web-app/main.tf new file mode 100644 index 0000000..34dd47b --- /dev/null +++ b/04-variables-and-outputs/web-app/main.tf @@ -0,0 +1,211 @@ +terraform { + # Assumes s3 bucket and dynamo DB table already set up + # See /code/03-basics/aws-backend + backend "s3" { + bucket = "devops-directive-tf-state" + key = "04-variables-and-outputs/web-app/terraform.tfstate" + region = "us-east-1" + dynamodb_table = "terraform-state-locking" + encrypt = true + } + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} + + +provider "aws" { + region = var.region +} + +resource "aws_instance" "instance_1" { + ami = var.ami + instance_type = var.instance_type + security_groups = [aws_security_group.instances.name] + user_data = <<-EOF + #!/bin/bash + echo "Hello, World 1" > index.html + python3 -m http.server 8080 & + EOF +} + +resource "aws_instance" "instance_2" { + ami = var.ami + instance_type = var.instance_type + security_groups = [aws_security_group.instances.name] + user_data = <<-EOF + #!/bin/bash + echo "Hello, World 2" > index.html + python3 -m http.server 8080 & + EOF +} + +resource "aws_s3_bucket" "bucket" { + bucket = var.bucket_name + force_destroy = true + versioning { + enabled = true + } + + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } + } +} + +data "aws_vpc" "default_vpc" { + default = true +} + +data "aws_subnet_ids" "default_subnet" { + vpc_id = data.aws_vpc.default_vpc.id +} + +resource "aws_security_group" "instances" { + name = "instance-security-group" +} + +resource "aws_security_group_rule" "allow_http_inbound" { + type = "ingress" + security_group_id = aws_security_group.instances.id + + from_port = 8080 + to_port = 8080 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] +} + +resource "aws_lb_listener" "http" { + load_balancer_arn = aws_lb.load_balancer.arn + + port = 80 + + protocol = "HTTP" + + # By default, return a simple 404 page + default_action { + type = "fixed-response" + + fixed_response { + content_type = "text/plain" + message_body = "404: page not found" + status_code = 404 + } + } +} + +resource "aws_lb_target_group" "instances" { + name = "example-target-group" + port = 8080 + protocol = "HTTP" + vpc_id = data.aws_vpc.default_vpc.id + + health_check { + path = "/" + protocol = "HTTP" + matcher = "200" + interval = 15 + timeout = 3 + healthy_threshold = 2 + unhealthy_threshold = 2 + } +} + +resource "aws_lb_target_group_attachment" "instance_1" { + target_group_arn = aws_lb_target_group.instances.arn + target_id = aws_instance.instance_1.id + port = 8080 +} + +resource "aws_lb_target_group_attachment" "instance_2" { + target_group_arn = aws_lb_target_group.instances.arn + target_id = aws_instance.instance_2.id + port = 8080 +} + +resource "aws_lb_listener_rule" "instances" { + listener_arn = aws_lb_listener.http.arn + priority = 100 + + condition { + path_pattern { + values = ["*"] + } + } + + action { + type = "forward" + target_group_arn = aws_lb_target_group.instances.arn + } +} + + +resource "aws_security_group" "alb" { + name = "alb-security-group" +} + +resource "aws_security_group_rule" "allow_alb_http_inbound" { + type = "ingress" + security_group_id = aws_security_group.alb.id + + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + +} + +resource "aws_security_group_rule" "allow_alb_all_outbound" { + type = "egress" + security_group_id = aws_security_group.alb.id + + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + +} + + +resource "aws_lb" "load_balancer" { + name = "web-app-lb" + load_balancer_type = "application" + subnets = data.aws_subnet_ids.default_subnet.ids + security_groups = [aws_security_group.alb.id] + +} + +resource "aws_route53_zone" "primary" { + name = var.domain +} + +resource "aws_route53_record" "root" { + zone_id = aws_route53_zone.primary.zone_id + name = var.domain + type = "A" + + alias { + name = aws_lb.load_balancer.dns_name + zone_id = aws_lb.load_balancer.zone_id + evaluate_target_health = true + } +} + +resource "aws_db_instance" "db_instance" { + allocated_storage = 20 + storage_type = "standard" + engine = "postgres" + engine_version = "12.5" + instance_class = "db.t2.micro" + name = var.db_name + username = var.db_user + password = var.db_pass + skip_final_snapshot = true +} diff --git a/04-variables-and-outputs/web-app/outputs.tf b/04-variables-and-outputs/web-app/outputs.tf new file mode 100644 index 0000000..61b998c --- /dev/null +++ b/04-variables-and-outputs/web-app/outputs.tf @@ -0,0 +1,11 @@ +output "instance_1_ip_addr" { + value = aws_instance.instance_1.public_ip +} + +output "instance_2_ip_addr" { + value = aws_instance.instance_2.public_ip +} + +output "db_instance_addr" { + value = aws_db_instance.db_instance.address +} diff --git a/04-variables-and-outputs/web-app/terraform.tfvars b/04-variables-and-outputs/web-app/terraform.tfvars new file mode 100644 index 0000000..5343cf1 --- /dev/null +++ b/04-variables-and-outputs/web-app/terraform.tfvars @@ -0,0 +1,5 @@ +bucket_name = "devops-directive-web-app-data" +domain = "mysuperawesomesite.com" +db_name = "mydb" +db_user = "foo" +# db_pass = "foobarbaz" \ No newline at end of file diff --git a/04-variables-and-outputs/web-app/variables.tf b/04-variables-and-outputs/web-app/variables.tf new file mode 100644 index 0000000..af9502b --- /dev/null +++ b/04-variables-and-outputs/web-app/variables.tf @@ -0,0 +1,55 @@ +# General Variables + +variable "region" { + description = "Default region for provider" + type = string + default = "us-east-1" +} + +# EC2 Variables + +variable "ami" { + description = "Amazon machine image to use for ec2 instance" + type = string + default = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1 +} + +variable "instance_type" { + description = "ec2 instance type" + type = string + default = "t2.micro" +} + +# S3 Variables + +variable "bucket_name" { + description = "name of s3 bucket for app data" + type = string +} + +# Route 53 Variables + +variable "domain" { + description = "Domain for website" + type = string +} + +# RDS Variables + +variable "db_name" { + description = "Name of DB" + type = string +} + +variable "db_user" { + description = "Username for DB" + type = string +} + +variable "db_pass" { + description = "Password for DB" + type = string + sensitive = true +} + + diff --git a/05-organization-and-modules/README.md b/05-organization-and-modules/README.md new file mode 100644 index 0000000..0271c67 --- /dev/null +++ b/05-organization-and-modules/README.md @@ -0,0 +1,4 @@ +## Modifications +- remove backend definition +- remove provider definition + diff --git a/05-organization-and-modules/web-app-module/compute/main.tf b/05-organization-and-modules/web-app-module/compute/main.tf new file mode 100644 index 0000000..e69de29 diff --git a/05-organization-and-modules/web-app-module/main.tf b/05-organization-and-modules/web-app-module/main.tf new file mode 100644 index 0000000..70e61eb --- /dev/null +++ b/05-organization-and-modules/web-app-module/main.tf @@ -0,0 +1,198 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} + + + +resource "aws_instance" "instance_1" { + ami = var.ami + instance_type = var.instance_type + security_groups = [aws_security_group.instances.name] + user_data = <<-EOF + #!/bin/bash + echo "Hello, World 1" > index.html + python3 -m http.server 8080 & + EOF +} + +resource "aws_instance" "instance_2" { + ami = var.ami + instance_type = var.instance_type + security_groups = [aws_security_group.instances.name] + user_data = <<-EOF + #!/bin/bash + echo "Hello, World 2" > index.html + python3 -m http.server 8080 & + EOF +} + +resource "aws_s3_bucket" "bucket" { + bucket = var.bucket_name + force_destroy = true + versioning { + enabled = true + } + + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } + } +} + +data "aws_vpc" "default_vpc" { + default = true +} + +data "aws_subnet_ids" "default_subnet" { + vpc_id = data.aws_vpc.default_vpc.id +} + +resource "aws_security_group" "instances" { + name = "instance-security-group" +} + +resource "aws_security_group_rule" "allow_http_inbound" { + type = "ingress" + security_group_id = aws_security_group.instances.id + + from_port = 8080 + to_port = 8080 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] +} + +resource "aws_lb_listener" "http" { + load_balancer_arn = aws_lb.load_balancer.arn + + port = 80 + + protocol = "HTTP" + + # By default, return a simple 404 page + default_action { + type = "fixed-response" + + fixed_response { + content_type = "text/plain" + message_body = "404: page not found" + status_code = 404 + } + } +} + +resource "aws_lb_target_group" "instances" { + name = "example-target-group" + port = 8080 + protocol = "HTTP" + vpc_id = data.aws_vpc.default_vpc.id + + health_check { + path = "/" + protocol = "HTTP" + matcher = "200" + interval = 15 + timeout = 3 + healthy_threshold = 2 + unhealthy_threshold = 2 + } +} + +resource "aws_lb_target_group_attachment" "instance_1" { + target_group_arn = aws_lb_target_group.instances.arn + target_id = aws_instance.instance_1.id + port = 8080 +} + +resource "aws_lb_target_group_attachment" "instance_2" { + target_group_arn = aws_lb_target_group.instances.arn + target_id = aws_instance.instance_2.id + port = 8080 +} + +resource "aws_lb_listener_rule" "instances" { + listener_arn = aws_lb_listener.http.arn + priority = 100 + + condition { + path_pattern { + values = ["*"] + } + } + + action { + type = "forward" + target_group_arn = aws_lb_target_group.instances.arn + } +} + + +resource "aws_security_group" "alb" { + name = "alb-security-group" +} + +resource "aws_security_group_rule" "allow_alb_http_inbound" { + type = "ingress" + security_group_id = aws_security_group.alb.id + + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + +} + +resource "aws_security_group_rule" "allow_alb_all_outbound" { + type = "egress" + security_group_id = aws_security_group.alb.id + + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + +} + + +resource "aws_lb" "load_balancer" { + name = "web-app-lb" + load_balancer_type = "application" + subnets = data.aws_subnet_ids.default_subnet.ids + security_groups = [aws_security_group.alb.id] + +} + +resource "aws_route53_zone" "primary" { + name = var.domain +} + +resource "aws_route53_record" "root" { + zone_id = aws_route53_zone.primary.zone_id + name = var.domain + type = "A" + + alias { + name = aws_lb.load_balancer.dns_name + zone_id = aws_lb.load_balancer.zone_id + evaluate_target_health = true + } +} + +resource "aws_db_instance" "db_instance" { + allocated_storage = 20 + storage_type = "standard" + engine = "postgres" + engine_version = "12.5" + instance_class = "db.t2.micro" + name = var.db_name + username = var.db_user + password = var.db_pass + skip_final_snapshot = true +} diff --git a/05-organization-and-modules/web-app-module/outputs.tf b/05-organization-and-modules/web-app-module/outputs.tf new file mode 100644 index 0000000..61b998c --- /dev/null +++ b/05-organization-and-modules/web-app-module/outputs.tf @@ -0,0 +1,11 @@ +output "instance_1_ip_addr" { + value = aws_instance.instance_1.public_ip +} + +output "instance_2_ip_addr" { + value = aws_instance.instance_2.public_ip +} + +output "db_instance_addr" { + value = aws_db_instance.db_instance.address +} diff --git a/05-organization-and-modules/web-app-module/variables.tf b/05-organization-and-modules/web-app-module/variables.tf new file mode 100644 index 0000000..af9502b --- /dev/null +++ b/05-organization-and-modules/web-app-module/variables.tf @@ -0,0 +1,55 @@ +# General Variables + +variable "region" { + description = "Default region for provider" + type = string + default = "us-east-1" +} + +# EC2 Variables + +variable "ami" { + description = "Amazon machine image to use for ec2 instance" + type = string + default = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1 +} + +variable "instance_type" { + description = "ec2 instance type" + type = string + default = "t2.micro" +} + +# S3 Variables + +variable "bucket_name" { + description = "name of s3 bucket for app data" + type = string +} + +# Route 53 Variables + +variable "domain" { + description = "Domain for website" + type = string +} + +# RDS Variables + +variable "db_name" { + description = "Name of DB" + type = string +} + +variable "db_user" { + description = "Username for DB" + type = string +} + +variable "db_pass" { + description = "Password for DB" + type = string + sensitive = true +} + + diff --git a/05-organization-and-modules/web-app/main.tf b/05-organization-and-modules/web-app/main.tf new file mode 100644 index 0000000..5852e60 --- /dev/null +++ b/05-organization-and-modules/web-app/main.tf @@ -0,0 +1,51 @@ +terraform { + # Assumes s3 bucket and dynamo DB table already set up + # See /code/03-basics/aws-backend + backend "s3" { + bucket = "devops-directive-tf-state" + key = "05-organization-and-modules/web-app/terraform.tfstate" + region = "us-east-1" + dynamodb_table = "terraform-state-locking" + encrypt = true + } + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} + +provider "aws" { + region = "us-east-1" +} + +variable "db_pass" { + description = "password for database" + type = string + sensitive = true +} + +module "web_app_1" { + source = "../web-app-module" + + # Input Variables + bucket_name = "devops-directive-web-app-data" + domain = "mysuperawesomesite.com" + db_name = "mydb" + db_user = "foo" + db_pass = var.db_pass +} + +module "web_app_2" { + source = "../web-app-module" + + # Input Variables + bucket_name = "devops-directive-web-app-data" + domain = "myothersuperawesomesite.com" + db_name = "mydb" + db_user = "foo" + db_pass = var.db_pass +} +