If you ever had to manage AWS Amazon environment you probably start by clicking through AWS console - Web GUI, then you quickly realize that it won't scale, because all your clicks are not repeatable and there is no code review of some sort.
Next logical step it to use aws cli, but then you'd need to create a bunch of scripts to manage it. There are a couple of options for scalable and systematic approach - one is Cloud Formation from Amazon, another cool solution is Terraform.
Terraform allows you to manage pretty much any AWS resource from a single configuration file that could be sitting in your GIT repo. This is truly "infrastructure as a code" solution.
Here is an example of how to create a simple ec2 instance with your parameters.
resource "aws_instance" "my-server" {
ami = "ami-93742ea3"
availability_zone = "us-west-2a"
ebs_optimized = false
instance_type = "t2.micro"
monitoring = false
key_name = "your-ssh-access-key"
subnet_id = "subnet-99999999"
security_group_ids = ["sg-ae33333", "sg-bb33333"]
associate_public_ip_address = true
private_ip = "10.10.10.50"
source_dest_check = true
root_block_device {
volume_type = "gp2"
volume_size = 10
iops = 30
delete_on_termination = true
}
tags {
"Name" = "my-server"
}
}
As you can see above, some values like subnet and security group are hardcoded since we are dealing with existing infrastructure, and those resources were created by hand.
It's easy enough to refactor those into a separate file called "variables.tf", so that you could at least use them by readable human names.
variable "access_key" {}
variable "secret_key" {}
# Default AWS region is West
variable "aws_region" {
default = "us-west-2"
}
# Security Groups
variable "sg" {
default = {
internal_ping = "sg-0444444"
internal_ssh = "sg-444444444"
}
}
Now you can start using those variables and you terraform template will be more readable
security_group_id = "${var.sg.internal_ping},${var.sg.internal_ssh},${var.sg.internal_http}"
Here is another example on how Terraform can manage a single DNS entry assuming that you have all your zones in that variables.tf file
resource "aws_route53_record" "my-server-A" {
zone_id = "${var.dns_zone.company_com}"
name = "my-server.company.com"
type = "A"
records = ["10.10.10.50"]
ttl = "600"
}
More info and examples are available here:
https://github.com/igorgrin/terraform
After you played enough with it, you'd want to create all your resources at the same time, so that you can use them in the same template instead of hard-coding those aws IDs.
Below is an example of creating two additional VPCs (ops and stag) when a current prod VPC already exists.
# Prod VPC is hard-coded
variable "aws_vpc" {
default = {
prod = "vpc-2222222"
}
}
# Current subnets
variable "ops_subnet" {
default = {
main = "10.1.0.0/16"
private = "10.1.0.0/24"
public = "10.1.1.0/24"
}
}
variable "prod_subnet" {
default = {
main = "10.10.0.0/16"
}
}
variable "stag_subnet" {
default = {
main = "10.2.0.0/16"
private = "10.2.0.0/24"
public = "10.2.1.0/24"
cache = "10.2.3.0/24"
}
}
variable "office_subnet" {
default = {
main = "10.101.0.0/16"
}
}
# VPCs
resource "aws_vpc" "ops" {
cidr_block = "${var.ops_subnet.main}"
enable_dns_support = true
enable_dns_hostnames = true
tags {
Provisioner = "Terraform"
Name = "vpc-ops"
}
}
resource "aws_vpc" "stag" {
cidr_block = "${var.stag_subnet.main}"
enable_dns_support = true
enable_dns_hostnames = true
tags {
Provisioner = "Terraform"
Name = "vpc-stag"
}
}
### DHCP
# OPS
resource "aws_vpc_dhcp_options" "ops" {
domain_name = "ops.int stag.int"
domain_name_servers = ["10.1.0.2"]
tags {
Provisioner = "Terraform"
Name = "dhcp-ops"
}
}
resource "aws_vpc_dhcp_options_association" "ops_resolver" {
vpc_id = "${aws_vpc.ops.id}"
dhcp_options_id = "${aws_vpc_dhcp_options.ops.id}"
}
# STAG
resource "aws_vpc_dhcp_options" "stag" {
domain_name = "stag.int"
domain_name_servers = ["10.2.0.2"]
tags {
Provisioner = "Terraform"
Name = "dhcp-stag"
}
}
resource "aws_vpc_dhcp_options_association" "stag_resolver" {
vpc_id = "${aws_vpc.stag.id}"
dhcp_options_id = "${aws_vpc_dhcp_options.stag.id}"
}
### DNS
# OPS
resource "aws_route53_zone" "ops" {
name = "ops.int"
vpc_id = "${aws_vpc.ops.id}"
}
# attaching zone to stag VPC
resource "aws_route53_zone_association" "ops_stag" {
zone_id = "${aws_route53_zone.ops.zone_id}"
vpc_id = "${aws_vpc.stag.id}"
}
# attaching zone to prod VPC
resource "aws_route53_zone_association" "ops_prod" {
zone_id = "${aws_route53_zone.ops.zone_id}"
vpc_id = "${var.aws_vpc.prod}"
}
# STAG
resource "aws_route53_zone" "stag" {
name = "stag.int"
vpc_id = "${aws_vpc.stag.id}"
}
# attaching zone to stag VPC
resource "aws_route53_zone_association" "stag_ops" {
zone_id = "${aws_route53_zone.stag.zone_id}"
vpc_id = "${aws_vpc.ops.id}"
}
# attaching zone to prod VPC
resource "aws_route53_zone_association" "stag_prod" {
zone_id = "${aws_route53_zone.stag.zone_id}"
vpc_id = "${var.aws_vpc.prod}"
}
### Internet gateway
resource "aws_internet_gateway" "ops" {
vpc_id = "${aws_vpc.ops.id}"
tags {
Provisioner = "Terraform"
Name = "ops-gw"
}
}
resource "aws_internet_gateway" "stag" {
vpc_id = "${aws_vpc.stag.id}"
tags {
Provisioner = "Terraform"
Name = "stag-gw"
}
}
### Elastic IPs
# OPS
resource "aws_eip" "ops_eip" {
vpc = true
}
# STAG
resource "aws_eip" "stag_eip" {
vpc = true
}
### NAT gateway
# OPS
resource "aws_nat_gateway" "ops_gw" {
allocation_id = "${aws_eip.ops_eip.id}"
subnet_id = "${aws_subnet.ops_subnet_public.id}"
depends_on = ["aws_internet_gateway.ops"]
}
# STAG
resource "aws_nat_gateway" "stag_gw" {
allocation_id = "${aws_eip.stag_eip.id}"
subnet_id = "${aws_subnet.stag_subnet_public.id}"
}
### Subnets
# OPS
resource "aws_subnet" "ops_subnet_private" {
vpc_id = "${aws_vpc.ops.id}"
cidr_block = "${var.ops_subnet.private}"
map_public_ip_on_launch = false
tags {
Provisioner = "Terraform"
Name = "ops-subnet-private"
}
}
resource "aws_subnet" "ops_subnet_public" {
vpc_id = "${aws_vpc.ops.id}"
cidr_block = "${var.ops_subnet.public}"
map_public_ip_on_launch = false
tags {
Provisioner = "Terraform"
Name = "ops-subnet-public"
}
}
# STAG
resource "aws_subnet" "stag_subnet_private" {
vpc_id = "${aws_vpc.stag.id}"
cidr_block = "${var.stag_subnet.private}"
map_public_ip_on_launch = false
tags {
Provisioner = "Terraform"
Name = "stag-subnet-private"
}
}
resource "aws_subnet" "stag_subnet_public" {
vpc_id = "${aws_vpc.stag.id}"
cidr_block = "${var.stag_subnet.public}"
map_public_ip_on_launch = false
tags {
Provisioner = "Terraform"
Name = "stag-subnet-public"
}
}
resource "aws_subnet" "stag_subnet_cache" {
vpc_id = "${aws_vpc.stag.id}"
cidr_block = "${var.stag_subnet.cache}"
map_public_ip_on_launch = false
tags {
Provisioner = "Terraform"
Name = "stag-subnet-cache"
}
}
### Peering
# OPS - STAG
resource "aws_vpc_peering_connection" "ops_stag" {
vpc_id = "${aws_vpc.ops.id}"
peer_vpc_id = "${aws_vpc.stag.id}"
peer_owner_id = "22222222222222"
auto_accept = true
tags {
Provisioner = "Terraform"
Name = "ops-stag"
}
}
# OPS - PROD
resource "aws_vpc_peering_connection" "ops_prod" {
vpc_id = "${aws_vpc.ops.id}"
peer_vpc_id = "${var.aws_vpc.prod}"
peer_owner_id = "22222222222"
auto_accept = true
tags {
Provisioner = "Terraform"
Name = "ops-prod"
}
}
# STAG - PROD
resource "aws_vpc_peering_connection" "stag_prod" {
vpc_id = "${aws_vpc.stag.id}"
peer_vpc_id = "${var.aws_vpc.prod}"
peer_owner_id = "222222222222"
auto_accept = true
tags {
Provisioner = "Terraform"
Name = "stag-prod"
}
}
### Routing
# OPS
resource "aws_route_table" "ops_route_private" {
vpc_id = "${aws_vpc.ops.id}"
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = "${aws_nat_gateway.ops_gw.id}"
}
route {
cidr_block = "${var.stag_subnet.private}"
vpc_peering_connection_id = "${aws_vpc_peering_connection.ops_stag.id}"
}
route {
cidr_block = "${var.prod_subnet.main}"
vpc_peering_connection_id = "${aws_vpc_peering_connection.ops_prod.id}"
}
tags {
Provisioner = "Terraform"
Name = "ops-private-route"
}
}
resource "aws_route_table" "ops_route_public" {
vpc_id = "${aws_vpc.ops.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.ops.id}"
}
route {
cidr_block = "${var.stag_subnet.public}"
vpc_peering_connection_id = "${aws_vpc_peering_connection.ops_stag.id}"
}
tags {
Provisioner = "Terraform"
Name = "ops-public-route"
}
}
# STAG
resource "aws_route_table" "stag_route_private" {
vpc_id = "${aws_vpc.stag.id}"
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = "${aws_nat_gateway.stag_gw.id}"
}
route {
cidr_block = "${var.prod_subnet.main}"
vpc_peering_connection_id = "${aws_vpc_peering_connection.stag_prod.id}"
}
tags {
Provisioner = "Terraform"
Name = "stag-route-private"
}
}
resource "aws_route_table" "stag_route_public" {
vpc_id = "${aws_vpc.stag.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.stag.id}"
}
tags {
Provisioner = "Terraform"
Name = "stag-route-public"
}
}
### Route table associations.
# OPS to Internet
resource "aws_route_table_association" "ops_subnet_public" {
subnet_id = "${aws_subnet.ops_subnet_public.id}"
route_table_id = "${aws_route_table.ops_route_public.id}"
}
resource "aws_route_table_association" "ops_private_subnet" {
subnet_id = "${aws_subnet.ops_subnet_private.id}"
route_table_id = "${aws_route_table.ops_route_private.id}"
}
# STAG to Internet
resource "aws_route_table_association" "stag_subnet_public" {
subnet_id = "${aws_subnet.stag_subnet_public.id}"
route_table_id = "${aws_route_table.stag_route_public.id}"
}
resource "aws_route_table_association" "stag_subnet_private" {
subnet_id = "${aws_subnet.stag_subnet_private.id}"
route_table_id = "${aws_route_table.stag_route_private.id}"
}
# Remote state file
resource "terraform_remote_state" "ops" {
backend = "s3"
config = {
bucket = "yourbucket"
key = "terraform-states/ops.json"
}
}
### Redis Cache subnets
# Ops
# STAG
resource "aws_elasticache_subnet_group" "stag_subnet_cache" {
name = "stag-subnet-cache"
description = "stag-subnet-cache"
subnet_ids = ["${aws_subnet.stag_subnet_cache.id}"]
}
No comments:
Post a Comment