From a4bfe5d6362fddfb2934dc9a89344c304e59cef7 Mon Sep 17 00:00:00 2001 From: Allen Gooch Date: Tue, 26 Jun 2018 18:39:42 -0500 Subject: [PATCH] Initial commit --- .gitignore | 3 + README.md | 6 ++ live/prod/main.tf | 4 + live/prod/vars.tf | 19 ++++ live/prod/web/main.tf | 23 +++++ live/prod/web/outputs.tf | 3 + live/prod/web/vars.tf | 4 + live/root.tf | 31 +++++++ modules/web/main.tf | 183 +++++++++++++++++++++++++++++++++++++++ modules/web/outputs.tf | 11 +++ modules/web/vars.tf | 48 ++++++++++ 11 files changed, 335 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 live/prod/main.tf create mode 100644 live/prod/vars.tf create mode 100644 live/prod/web/main.tf create mode 100644 live/prod/web/outputs.tf create mode 100644 live/prod/web/vars.tf create mode 100644 live/root.tf create mode 100644 modules/web/main.tf create mode 100644 modules/web/outputs.tf create mode 100644 modules/web/vars.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8d0510 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +**/*.tfstate* +**/.terraform/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..eaaae72 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Terraform AWS Web Stack + +## Overview + +This repository contains the [Terraform](https://www.terraform.io/) +configuration modules for a demo web service on AWS infrastructure. diff --git a/live/prod/main.tf b/live/prod/main.tf new file mode 100644 index 0000000..888fb51 --- /dev/null +++ b/live/prod/main.tf @@ -0,0 +1,4 @@ +module "web" { + source = "./web" + tags = "${var.tags}" +} diff --git a/live/prod/vars.tf b/live/prod/vars.tf new file mode 100644 index 0000000..aa76782 --- /dev/null +++ b/live/prod/vars.tf @@ -0,0 +1,19 @@ +variable "tags" { + description = "The tags to apply to AWS resources." + type = "map" +} + +variable "subnets" { + description = "Subnets to use." + type = "list" + default = [ + "subnet-789edf13", + "subnet-799edf12", + "subnet-7f9edf14" + ] +} + +variable "vpc_id" { + description = "VPC to use." + default = "vpc-7a9edf11" +} diff --git a/live/prod/web/main.tf b/live/prod/web/main.tf new file mode 100644 index 0000000..db32f78 --- /dev/null +++ b/live/prod/web/main.tf @@ -0,0 +1,23 @@ +locals { + subnets = [ + "subnet-789edf13", + "subnet-799edf12", + "subnet-7f9edf14" + ] + vpc_id = "vpc-7a9edf11" +} + +module "web" { + source = "../../../modules/web" + environment = "prod" + region = "us-west-2" + owner = "Allen Gooch" + description = "terraform-aws-web-stack demo service" + source_ami = "ami-e6d5969e" + instance_type = "c5.xlarge" + min_size = 1 + max_size = 1 + subnets = "${local.subnets}" + vpc_id = "${local.vpc_id}" + tags = "${var.tags}" +} diff --git a/live/prod/web/outputs.tf b/live/prod/web/outputs.tf new file mode 100644 index 0000000..c9485fd --- /dev/null +++ b/live/prod/web/outputs.tf @@ -0,0 +1,3 @@ +output "elb_dns_name" { + value = "${module.web.alb_dns_name}" +} diff --git a/live/prod/web/vars.tf b/live/prod/web/vars.tf new file mode 100644 index 0000000..918e813 --- /dev/null +++ b/live/prod/web/vars.tf @@ -0,0 +1,4 @@ +variable "tags" { + description = "The tags to apply to AWS resources." + type = "map" +} diff --git a/live/root.tf b/live/root.tf new file mode 100644 index 0000000..295633f --- /dev/null +++ b/live/root.tf @@ -0,0 +1,31 @@ +terraform { + required_version = ">= 0.11, < 0.12" + + backend "s3" { + bucket = "agooch-demo-svc-tfstate" + key = "terraform.tfstate" + region = "us-west-2" + } +} + +locals { + tags = { + System = "demo-svc" + Owner = "Allen Gooch" + } +} + + +provider "aws" { + version = "~> 1.24" + alias = "usw2" + region = "us-west-2" +} + +module "prod" { + source = "./prod" + tags = "${local.tags}" + providers = { + aws = "aws.usw2" + } +} diff --git a/modules/web/main.tf b/modules/web/main.tf new file mode 100644 index 0000000..014cb4e --- /dev/null +++ b/modules/web/main.tf @@ -0,0 +1,183 @@ +data "aws_availability_zones" "all" {} + +locals { + cluster_name = "${var.tags["System"]}-${var.environment}" + http_port = 80 + https_port = 443 + ssh_port = 22 + tags = { + Description = "${var.description}" + Environment = "${var.environment}" + Owner = "${var.owner}" + } +} + +#------------------------------------------------------------------------------ +# Back-end Auto-scaling Group (ASG) Resources +#------------------------------------------------------------------------------ + +resource "aws_launch_configuration" "backend" { + name = "${local.cluster_name}-launch-configuration" + image_id = "${var.source_ami}" + instance_type = "${var.instance_type}" + security_groups = ["${aws_security_group.backend.id}"] + + user_data = <<-EOF + #!/bin/bash + echo "Hello, World" > index.html + nohup busybox httpd -f -p "${local.http_port}" & + EOF + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_autoscaling_group" "backend" { + name = "${local.cluster_name}-asg" + launch_configuration = "${aws_launch_configuration.backend.id}" + availability_zones = ["${data.aws_availability_zones.all.names}"] + load_balancers = ["${aws_alb.frontend.name}"] + health_check_type = "ELB" + min_size = "${var.min_size}" + max_size = "${var.max_size}" + // This resource type uses different tags specification format. + // A list comp over the locals tags map would sure come in handy to keep + // things DRY. + tags = [ + { + key = "System" + value = "${var.tags["System"]}" + propagate_at_launch = true + }, + { + key = "Environment" + value = "${local.tags["Environment"]}" + propagate_at_launch = true + }, + { + key = "Owner" + value = "${local.tags["Owner"]}" + propagate_at_launch = true + }, + { + key = "Description" + value = "${local.tags["Description"]}" + propagate_at_launch = true + } + ] +} + +resource "aws_security_group" "backend" { + name = "${local.cluster_name}-backend-sg" + tags = "${merge(var.tags, local.tags)}" + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_security_group_rule" "backend_allow_http_inbound" { + type = "ingress" + security_group_id = "${aws_security_group.backend.id}" + from_port = "${local.http_port}" + to_port = "${local.http_port}" + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] +} + +resource "aws_security_group_rule" "backend_allow_ssh_inbound" { + type = "ingress" + security_group_id = "${aws_security_group.backend.id}" + from_port = "${local.ssh_port}" + to_port = "${local.ssh_port}" + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] +} + +#------------------------------------------------------------------------------ +# Front-end Application Load Balancer (ALB) Resources +#------------------------------------------------------------------------------ + +resource "aws_alb" "frontend" { + name = "${local.cluster_name}-alb" + internal = false + load_balancer_type = "application" + subnets = "${var.subnets}" + security_groups = ["${aws_security_group.frontend.id}"] + idle_timeout = "60" + tags = "${merge(var.tags, local.tags)}" +} + +resource "aws_alb_listener" "frontend" { + load_balancer_arn = "${aws_alb.frontend.arn}" + port = "${local.https_port}" + protocol = "HTTPS" + ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01" + + default_action { + target_group_arn = "${aws_alb.frontend.arn}" + type = "forward" + } +} + +resource "aws_alb_listener_rule" "frontend" { + depends_on = ["aws_alb_target_group.frontend"] + listener_arn = "${aws_alb_listener.frontend.arn}" + + action { + target_group_arn = "${aws_alb_target_group.frontend.arn}" + type = "forward" + } + + condition { + field = "host-header" + values = ["*"] + } +} + +resource "aws_alb_target_group" "frontend" { + name = "${local.cluster_name}-alb" + port = "${local.http_port}" + protocol = "HTTP" + vpc_id = "${var.vpc_id}" + tags = "${merge(var.tags, local.tags)}" + + health_check { + healthy_threshold = 2 + unhealthy_threshold = 2 + timeout = 3 + interval = 30 + port = "${local.http_port}" + protocol = "HTTP" + path = "/" + } +} + +resource "aws_autoscaling_attachment" "frontend" { + alb_target_group_arn = "${aws_alb_target_group.frontend.arn}" + autoscaling_group_name = "${aws_autoscaling_group.backend.id}" +} + +resource "aws_security_group" "frontend" { + name = "${local.cluster_name}-frontend-sg" + tags = "${merge(var.tags, local.tags)}" +} + +resource "aws_security_group_rule" "frontend_allow_http_inbound" { + type = "ingress" + security_group_id = "${aws_security_group.frontend.id}" + from_port = "${local.http_port}" + to_port = "${local.http_port}" + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] +} + +resource "aws_security_group_rule" "frontend_allow_all_outbound" { + type = "egress" + security_group_id = "${aws_security_group.frontend.id}" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] +} diff --git a/modules/web/outputs.tf b/modules/web/outputs.tf new file mode 100644 index 0000000..c408a3d --- /dev/null +++ b/modules/web/outputs.tf @@ -0,0 +1,11 @@ +output "alb_dns_name" { + value = "${aws_alb.frontend.dns_name}" +} + +output "alb_security_group_id" { + value = "${aws_security_group.frontend.id}" +} + +output "asg_name" { + value = "${aws_autoscaling_group.backend.name}" +} diff --git a/modules/web/vars.tf b/modules/web/vars.tf new file mode 100644 index 0000000..15b7601 --- /dev/null +++ b/modules/web/vars.tf @@ -0,0 +1,48 @@ +variable "environment" { + description = "Environment name" +} + +variable "region" { + description = "Environment region" +} + +variable "owner" { + description = "Environment owner name" +} + +variable "description" { + description = "Environment description" +} + +variable "source_ami" { + description = "The AMI to use" +} + +variable "instance_type" { + description = "The type of EC2 Instances to run in the ASG" + default = "t2.micro" +} + +variable "min_size" { + description = "The minimum number of EC2 Instances in the ASG" + default = 1 +} + +variable "max_size" { + description = "The maximum number of EC2 Instances in the ASG" + default = 1 +} + +variable "tags" { + description = "The tags to apply to AWS resources" + type = "map" +} + +variable "subnets" { + description = "EC2 instance subnets" + type = "list" +} + +variable "vpc_id" { + description = "EC2 VPC id" +}