Terraform Tutorial - VPC, Subnets, RouteTable, ELB, Security Group, and Apache server II
Continued from Terraform VPC I, we're going to go over how to make a web server on top of the VPC, subnets, and route table we constructed.
Let's create a security group for our web servers with inbound allowing port 80 and with outbound allowing all traffic:
:
resource "aws_security_group" "webservers" { name = "allow_http" description = "Allow http inbound traffic" vpc_id = aws_vpc.terra_vpc.id ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
Create it with terraform apply
command.
Here are the files to create our web severs.
instances.tf:
resource "aws_instance" "webservers" { count = "${length(var.subnets_cidr)}" ami = "${var.webservers_ami}" instance_type = "${var.instance_type}" security_groups = ["${aws_security_group.webservers.id}"] subnet_id = "${element(aws_subnet.public.*.id,count.index)}" user_data = "${file("install_httpd.sh")}" }
We added ami, security groups, subnet, user_data.
variables.tf:
variable "aws_region" { default = "us-east-1" } variable "vpc_cidr" { default = "10.20.0.0/16" } variable "subnets_cidr" { type = "list" default = ["10.20.1.0/24", "10.20.2.0/24"] } variable "azs" { type = "list" default = ["us-east-1a", "us-east-1b"] } variable "webservers_ami" { default = "ami-0ff8a91507f77f867" } variable "instance_type" { default = "t2.nano" }
The boot strapping script for our user_data is defined in install_httpd.sh:
#! /bin/bash sudo yum update sudo yum install -y httpd sudo chkconfig httpd on sudo service httpd start echo "<h1>Deployed via Terraform wih ELB</h1>" | sudo tee /var/www/html/index.html
Let's run terraform apply
command:
$ terraform apply ... Terraform will perform the following actions: + aws_instance.webservers[0] ... + aws_instance.webservers[1] Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
We may want to put a tag on the server with the modified instances.tf:
resource "aws_instance" "webservers" { count = length(var.subnets_cidr) ami = var.webservers_ami instance_type = var.instance_type security_groups = [aws_security_group.webservers.id] subnet_id = element(aws_subnet.public.*.id,count.index) user_data = file("install_httpd.sh") tags = { Name = "Server-${count.index}" } }
At this point, we may find the instances do not have public ips. So, we want to enable public ip via subnet setting ("Modify auto-assign IP Settings"):
From Terraform docs (aws_subnet):
"map_public_ip_on_launch - (Optional) Specify true to indicate that instances launched into the subnet should be assigned a public IP address. Default is false."
Let's modify our subnet definition in vpc.tf:
# VPC resource "aws_vpc" "terra_vpc" { cidr_block = var.vpc_cidr tags = { Name = "TerraVPC" } } # Internet Gateway resource "aws_internet_gateway" "terra_igw" { vpc_id = aws_vpc.terra_vpc.id tags = { Name = "main" } } # Subnets : public resource "aws_subnet" "public" { count = length(var.subnets_cidr) vpc_id = aws_vpc.terra_vpc.id cidr_block = element(var.subnets_cidr,count.index) availability_zone = element(var.azs,count.index) map_public_ip_on_launch = true tags = { Name = "Subnet-${count.index+1}" } } # Route table: attach Internet Gateway resource "aws_route_table" "public_rt" { vpc_id = aws_vpc.terra_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.terra_igw.id } tags = { Name = "publicRouteTable" } } # Route table association with public subnets resource "aws_route_table_association" "a" { count = length(var.subnets_cidr) subnet_id = element(aws_subnet.public.*.id,count.index) route_table_id = aws_route_table.public_rt.id }
Run Terraform again:
$ terraform apply ... ~ aws_subnet.public[0] map_public_ip_on_launch: "false" => "true" ~ aws_subnet.public[1] map_public_ip_on_launch: "false" => "true"
Check if our instances have public ips:
We can get the basic tf from aws_elb and modified it like this, elb.tf:
# Create a new load balancer resource "aws_elb" "terra-elb" { name = "terra-elb" subnets = aws_subnet.public.*.id security_groups = [aws_security_group.webservers.id] listener { instance_port = 80 instance_protocol = "http" lb_port = 80 lb_protocol = "http" } health_check { healthy_threshold = 2 unhealthy_threshold = 2 timeout = 3 target = "HTTP:80/index.html" interval = 30 } instances = [aws_instance.webservers[0].id, aws_instance.webservers[1].id] cross_zone_load_balancing = true idle_timeout = 100 connection_draining = true connection_draining_timeout = 300 tags = { Name = "terraform-elb" } } output "elb-dns-name" { value = aws_elb.terra-elb.dns_name }
Run:
$ terraform apply ... Apply complete! Resources: 3 added, 0 changed, 2 destroyed. Outputs: elb-dns-name = terra-elb-954318058.us-east-1.elb.amazonaws.com
We can check if the ELB's dns name is working:
The files used in this tutorial is available from Terraform-Turotials/VPC-LoadBalancer/ .
provider.tf:
provider "aws" { region = "${var.aws_region}" }
variables.tf:
variable "aws_region" { default = "us-east-1" } variable "vpc_cidr" { default = "10.20.0.0/16" } variable "subnets_cidr" { type = "list" default = ["10.20.1.0/24", "10.20.2.0/24"] } variable "azs" { type = "list" default = ["us-east-1a", "us-east-1b"] } variable "webservers_ami" { default = "ami-0ff8a91507f77f867" } variable "instance_type" { default = "t2.nano" }
vpc.tf:
# VPC resource "aws_vpc" "terra_vpc" { cidr_block = "${var.vpc_cidr}" tags { Name = "TerraVPC" } } # Internet Gateway resource "aws_internet_gateway" "terra_igw" { vpc_id = "${aws_vpc.terra_vpc.id}" tags { Name = "main" } } # Subnets : public resource "aws_subnet" "public" { count = "${length(var.subnets_cidr)}" vpc_id = "${aws_vpc.terra_vpc.id}" cidr_block = "${element(var.subnets_cidr,count.index)}" availability_zone = "${element(var.azs,count.index)}" map_public_ip_on_launch = true tags { Name = "Subnet-${count.index+1}" } } # Route table: attach Internet Gateway resource "aws_route_table" "public_rt" { vpc_id = "${aws_vpc.terra_vpc.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.terra_igw.id}" } tags { Name = "publicRouteTable" } } # Route table association with public subnets resource "aws_route_table_association" "a" { count = "${length(var.subnets_cidr)}" subnet_id = "${element(aws_subnet.public.*.id,count.index)}" route_table_id = "${aws_route_table.public_rt.id}" }
instances.tf:
resource "aws_instance" "webservers" { count = "${length(var.subnets_cidr)}" ami = "${var.webservers_ami}" instance_type = "${var.instance_type}" security_groups = ["${aws_security_group.webservers.id}"] subnet_id = "${element(aws_subnet.public.*.id,count.index)}" user_data = "${file("install_httpd.sh")}" tags { Name = "Server-${count.index}" } }
security-groups.tf:
resource "aws_security_group" "webservers" { name = "allow_http" description = "Allow http inbound traffic" vpc_id = "${aws_vpc.terra_vpc.id}" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
elb.tf:
# Create a new load balancer resource "aws_elb" "terra-elb" { name = "terra-elb" #availability_zones = ["${var.azs}"] subnets = ["${aws_subnet.public.*.id}"] security_groups = ["${aws_security_group.webservers.id}"] listener { instance_port = 80 instance_protocol = "http" lb_port = 80 lb_protocol = "http" } health_check { healthy_threshold = 2 unhealthy_threshold = 2 timeout = 3 target = "HTTP:80/index.html" interval = 30 } instances = ["${aws_instance.webservers.*.id}"] cross_zone_load_balancing = true idle_timeout = 100 connection_draining = true connection_draining_timeout = 300 tags { Name = "terraform-elb" } } output "elb-dns-name" { value = "${aws_elb.terra-elb.dns_name}" }
install_httpd.sh:
#! /bin/bash sudo yum update sudo yum install -y httpd sudo chkconfig httpd on sudo service httpd start echo "<h1>Deployed via Terraform wih ELB</h1>" | sudo tee /var/www/html/index.html
Terraform
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization