Terraform guide 4 - AWS
Terraform: AWS
Our Architecture: What We’re Going to Build
- Root (orchestrate all)
- Storage (S3)
- Networking (gateway, route tables, security group)
- Compute (2 EC2)
Storage
S3 bucket and random ID
- environment setup:
~/terraform/AWS/storage
1
2mkdir -p ~/terraform/AWS/storage
cd ~/terraform/AWS/storage - example
- Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#---------storage/main.tf---------
# Create a random id
resource "random_id" "tf_bucket_id" {
byte_length = 2
}
# Create the bucket
resource "aws_s3_bucket" "tf_code" {
bucket = "${var.project_name}-${random_id.tf_bucket_id.dec}"
acl = "private"
force_destroy = true
tags {
Name = "tf_bucket"
}
} - Edit
variables.tf
1
2#----storage/variables.tf----
variable "project_name" {} - Edit
Coutputs.tf
1
2
3
4#----storage/outputs.tf----
output "bucketname" {
value = "${aws_s3_bucket.tf_code.id}"
} - Work with Terraform
1
2
3
4
5
6
7
8
9
10
11
12terraform init
terraform validate
# plan the deployment
export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]"
export AWS_DEFAULT_REGION="us-east-1"
terraform plan -out=tfplan -var project_name=la-terraform
# deploy
terraform apply tfplan
# clean up
terraform destroy -auto-approve -var project_name=la-terraform
- Edit
Root module
- environment:
~/terraform/AWS
1
2cd ~/terraform/AWS
touch {main.tf,variables.tf,outputs.tf,terraform.tfvars} - example
- Edit
main.tf
1
2
3
4
5
6
7
8
9
10#----root/main.tf-----
provider "aws" {
region = "${var.aws_region}"
}
# Deploy Storage Resources
module "storage" {
source = "./storage"
project_name = "${var.project_name}"
} - Edit
variables.tf
1
2
3
4
5#----root/variables.tf-----
variable "aws_region" {}
#------ storage variables
variable "project_name" {} - Edit
terraform.tfvars
:1
2aws_region = "us-east-1"
project_name = "la-terraform" - Edit
outputs.tf
1
2
3
4
5
6#----root/outputs.tf-----
#----storage outputs------
output "Bucket Name" {
value = "${module.storage.bucketname}"
} - Work with Terraform
1
2
3
4
5
6
7
8
9export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]]"
terraform init
terraform validate
# deploy the S3 bucket
terraform apply -auto-approve
# clean up
terraform destroy -auto-approve
- Edit
Networking
VPC, Internet Gateway, and Route Tables
- environment:
~/terraform/AWS/networking
1
2
3mkdir -p ~/terraform/AWS/networking
cd ~/terraform/AWS/networking
touch {main.tf,variables.tf,outputs.tf,terraform.tfvars} - example
Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88#----networking/main.tf----
data "aws_availability_zones" "available" {}
resource "aws_vpc" "tf_vpc" {
cidr_block = "${var.vpc_cidr}"
enable_dns_hostnames = true
enable_dns_support = true
tags {
Name = "tf_vpc"
}
}
resource "aws_internet_gateway" "tf_internet_gateway" {
vpc_id = "${aws_vpc.tf_vpc.id}"
tags {
Name = "tf_igw"
}
}
resource "aws_route_table" "tf_public_rt" {
vpc_id = "${aws_vpc.tf_vpc.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.tf_internet_gateway.id}"
}
tags {
Name = "tf_public"
}
}
resource "aws_default_route_table" "tf_private_rt" {
default_route_table_id = "${aws_vpc.tf_vpc.default_route_table_id}"
tags {
Name = "tf_private"
}
}
resource "aws_subnet" "tf_public_subnet" {
count = 2
vpc_id = "${aws_vpc.tf_vpc.id}"
cidr_block = "${var.public_cidrs[count.index]}"
map_public_ip_on_launch = true
availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
tags {
Name = "tf_public_${count.index + 1}"
}
}
resource "aws_route_table_association" "tf_public_assoc" {
count = "${aws_subnet.tf_public_subnet.count}"
subnet_id = "${aws_subnet.tf_public_subnet.*.id[count.index]}"
route_table_id = "${aws_route_table.tf_public_rt.id}"
}
resource "aws_security_group" "tf_public_sg" {
name = "tf_public_sg"
description = "Used for access to the public instances"
vpc_id = "${aws_vpc.tf_vpc.id}"
#SSH
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["${var.accessip}"]
}
#HTTP
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["${var.accessip}"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}Edit
variables.tf
1
2
3
4
5
6
7
8#----networking/variables.tf----
variable "vpc_cidr" {}
variable "public_cidrs" {
type = "list"
}
variable "accessip" {}Edit
outputs.tf
1
2
3
4
5
6
7
8
9
10
11
12
13#-----networking/outputs.tf----
output "public_subnets" {
value = "${aws_subnet.tf_public_subnet.*.id}"
}
output "public_sg" {
value = "${aws_security_group.tf_public_sg.id}"
}
output "subnet_ips" {
value = "${aws_subnet.tf_public_subnet.*.cidr_block}"
}Edit
terraform.tfvars
1
2
3
4
5
6vpc_cidr = "10.123.0.0/16"
public_cidrs = [
"10.123.1.0/24",
"10.123.2.0/24"
]
accessip = "0.0.0.0/0"Work with Terraform
1
2
3
4
5
6
7
8
9
10export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]]"
terraform init
terraform validate
# deploy network
terraform apply -auto-approve
# clean up
terraform destroy -auto-approve
rm terraform.tfvars
Security, and the count attribute
- environment:
~/terraform/AWS/networking
1
2
3mkdir -p ~/terraform/AWS/networking
cd ~/terraform/AWS/networking
touch {main.tf,variables.tf,outputs.tf,terraform.tfvars} - example
- Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89#----networking/main.tf----
data "aws_availability_zones" "available" {}
resource "aws_vpc" "tf_vpc" {
cidr_block = "${var.vpc_cidr}"
enable_dns_hostnames = true
enable_dns_support = true
tags {
Name = "tf_vpc"
}
}
resource "aws_internet_gateway" "tf_internet_gateway" {
vpc_id = "${aws_vpc.tf_vpc.id}"
tags {
Name = "tf_igw"
}
}
resource "aws_route_table" "tf_public_rt" {
vpc_id = "${aws_vpc.tf_vpc.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.tf_internet_gateway.id}"
}
tags {
Name = "tf_public"
}
}
resource "aws_default_route_table" "tf_private_rt" {
default_route_table_id = "${aws_vpc.tf_vpc.default_route_table_id}"
tags {
Name = "tf_private"
}
}
resource "aws_subnet" "tf_public_subnet" {
count = 2
vpc_id = "${aws_vpc.tf_vpc.id}"
cidr_block = "${var.public_cidrs[count.index]}"
map_public_ip_on_launch = true
availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
tags {
Name = "tf_public_${count.index + 1}"
}
}
resource "aws_route_table_association" "tf_public_assoc" {
count = "${aws_subnet.tf_public_subnet.count}"
subnet_id = "${aws_subnet.tf_public_subnet.*.id[count.index]}"
route_table_id = "${aws_route_table.tf_public_rt.id}"
}
resource "aws_security_group" "tf_public_sg" {
name = "tf_public_sg"
description = "Used for access to the public instances"
vpc_id = "${aws_vpc.tf_vpc.id}"
#SSH
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["${var.accessip}"]
}
#HTTP
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["${var.accessip}"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
- Edit
Variables and outputs
- Edit
variables.tf
1
2
3
4
5
6
7
8#----networking/variables.tf----
variable "vpc_cidr" {}
variable "public_cidrs" {
type = "list"
}
variable "accessip" {} - Edit
outputs.tf
1
2
3
4
5
6
7
8
9
10
11
12
13#-----networking/outputs.tf----
output "public_subnets" {
value = "${aws_subnet.tf_public_subnet.*.id}"
}
output "public_sg" {
value = "${aws_security_group.tf_public_sg.id}"
}
output "subnet_ips" {
value = "${aws_subnet.tf_public_subnet.*.cidr_block}"
} - Edit
terraform.tfvars
1
2
3
4
5
6vpc_cidr = "10.123.0.0/16"
public_cidrs = [
"10.123.1.0/24",
"10.123.2.0/24"
]
accessip = "0.0.0.0/0" - Work with Terraform
1
2
3
4
5
6
7
8
9
10export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]]"
terraform init
terraform validate
# deploy network
terraform apply -auto-approve
# clean up
terraform destroy -auto-approve
rm terraform.tfvars
Root module
- environment:
~/terraform/AWS
- example
- Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17provider "aws" {
region = "${var.aws_region}"
}
# Deploy Storage Resources
module "storage" {
source = "./storage"
project_name = "${var.project_name}"
}
# Deploy Networking Resources
module "networking" {
source = "./networking"
vpc_cidr = "${var.vpc_cidr}"
public_cidrs = "${var.public_cidrs}"
accessip = "${var.accessip}"
} - Edit
variables.tf
1
2
3
4
5
6
7
8
9
10
11
12#----root/variables.tf-----
variable "aws_region" {}
#------ storage variables
variable "project_name" {}
#-------networking variables
variable "vpc_cidr" {}
variable "public_cidrs" {
type = "list"
}
variable "accessip" {} - Edit
terraform.tfvars
1
2
3
4
5
6
7
8aws_region = "us-east-1"
project_name = "la-terraform"
vpc_cidr = "10.123.0.0/16"
public_cidrs = [
"10.123.1.0/24",
"10.123.2.0/24"
]
accessip = "0.0.0.0/0" - Work with Terraform
1
2
3
4
5
6
7
8export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]]"
terraform init
terraform validate
terraform apply -auto-approve
# clean up
terraform destroy -auto-approve
- Edit
Compute
AMI data, key pair, and the file function
- environment:
~/terraform/AWS/compute
1
2
3mkdir -p ~/terraform/AWS/compute
cd ~/terraform/AWS/compute
touch {main.tf,variables.tf,outputs.tf} - create SSH key
1
ssh-keygen
- example
- Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#----compute/main.tf#----
data "aws_ami" "server_ami" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn-ami-hvm*-x86_64-gp2"]
}
}
resource "aws_key_pair" "tf_auth" {
key_name = "${var.key_name}"
public_key = "${file(var.public_key_path)}"
} - Edit
variables.tf
1
2
3
4#----compute/variables.tf----
variable "key_name" {}
variable "public_key_path" {} - Work with Terraform
1
2
3
4
5
6
7
8
9
10export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]]"
terraform init
terraform validate
# replace key_name and public_key_path for your own values
terraform plan -out=tfplan -var 'key_name=tfkey' -var 'public_key_path=/home/myUser/.ssh/id_rsa.pub'
terraform apply -auto-approve
# clen up
terraform destroy -auto-approve
- Edit
EC2 Instance
- Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40#-----compute/main.tf#-----
data "aws_ami" "server_ami" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn-ami-hvm*-x86_64-gp2"]
}
}
resource "aws_key_pair" "tf_auth" {
key_name = "${var.key_name}"
public_key = "${file(var.public_key_path)}"
}
data "template_file" "user-init" {
count = 2
template = "${file("${path.module}/userdata.tpl")}"
vars {
firewall_subnets = "${element(var.subnet_ips, count.index)}"
}
}
resource "aws_instance" "tf_server" {
count = "${var.instance_count}"
instance_type = "${var.instance_type}"
ami = "${data.aws_ami.server_ami.id}"
tags {
Name = "tf_server-${count.index +1}"
}
key_name = "${aws_key_pair.tf_auth.id}"
vpc_security_group_ids = ["${var.security_group}"]
subnet_id = "${element(var.subnets, count.index)}"
user_data = "${data.template_file.user-init.*.rendered[count.index]}"
} - Edit
userdata.tpl
1
2
3
4
5
yum install httpd -y
echo "Subnet for Firewall: ${firewall_subnets}" >> /var/www/html/index.html
service httpd start
chkconfig httpd on - Edit
variables.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#-----compute/variables.tf
variable "key_name" {}
variable "public_key_path" {}
variable "subnet_ips" {
type = "list"
}
variable "instance_count" {}
variable "instance_type" {}
variable "security_group" {}
variable "subnets" {
type = "list"
} - Edit
outputs.tf
1
2
3
4
5
6
7
8
9#-----compute/outputs.tf-----
output "server_id" {
value = "${join(", ", aws_instance.tf_server.*.id)}"
}
output "server_ip" {
value = "${join(", ", aws_instance.tf_server.*.public_ip)}"
}
Root module
- Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28provider "aws" {
region = "${var.aws_region}"
}
# Deploy Storage Resources
module "storage" {
source = "./storage"
project_name = "${var.project_name}"
}
# Deploy Networking Resources
module "networking" {
source = "./networking"
vpc_cidr = "${var.vpc_cidr}"
public_cidrs = "${var.public_cidrs}"
accessip = "${var.accessip}"
}
# Deploy Compute Resources
module "compute" {
source = "./compute"
instance_count = "${var.instance_count}"
key_name = "${var.key_name}"
public_key_path = "${var.public_key_path}"
instance_type = "${var.server_instance_type}"
subnets = "${module.networking.public_subnets}"
security_group = "${module.networking.public_sg}"
subnet_ips = "${module.networking.subnet_ips}"
} - Edit
variables.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#----root/variables.tf-----
variable "aws_region" {}
#------ storage variables
variable "project_name" {}
#-------networking variables
variable "vpc_cidr" {}
variable "public_cidrs" {
type = "list"
}
variable "accessip" {}
#-------compute variables
variable "key_name" {}
variable "public_key_path" {}
variable "server_instance_type" {}
variable "instance_count" {
default = 1
} - Edit
outputs.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#----root/outputs.tf-----
#----storage outputs------
output "Bucket Name" {
value = "${module.storage.bucketname}"
}
#---Networking Outputs -----
output "Public Subnets" {
value = "${join(", ", module.networking.public_subnets)}"
}
output "Subnet IPs" {
value = "${join(", ", module.networking.subnet_ips)}"
}
output "Public Security Group" {
value = "${module.networking.public_sg}"
}
#---Compute Outputs ------
output "Public Instance IDs" {
value = "${module.compute.server_id}"
}
output "Public Instance IPs" {
value = "${module.compute.server_ip}"
} - Edit
terraform.tfvars
1
2
3
4
5
6
7
8
9
10
11
12aws_region = "us-west-1"
project_name = "la-terraform"
vpc_cidr = "10.123.0.0/16"
public_cidrs = [
"10.123.1.0/24",
"10.123.2.0/24"
]
accessip = "0.0.0.0/0"
key_name = "tf_key"
public_key_path = "/home/cloud_user/.ssh/id_rsa.pub"
server_instance_type = "t2.micro"
instance_count = 2 - work with Terraform:
1
2
3
4
5
6
7
8
9export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]"
terraform init
terraform validate
terraform plan
terraform apply
# clean up
terraform destroy