commit 1d095e337a70d4403e5db14540f0d4447f00c98d Author: sid palas Date: Wed May 5 08:13:24 2021 -0700 initial commit 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 0000000..c89aeae Binary files /dev/null and b/03-basics/web-app/architecture.png differ diff --git a/03-basics/web-app/main.tf b/03-basics/web-app/main.tf new file mode 100644 index 0000000..42f0db7 --- /dev/null +++ b/03-basics/web-app/main.tf @@ -0,0 +1,210 @@ +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 = "03-basics/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" +} + +resource "aws_instance" "instance_1" { + 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 1" > 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 +} +