Skip to content

Commit

Permalink
Better doucmentation
Browse files Browse the repository at this point in the history
Separate to multiple file
Rename back to private and front to public
Use The NAT gatway managed by AWS
Use VPC DHCP option
Use ENI for Bastion with DNS Health check
Support for extra user data
  • Loading branch information
tuier committed Jul 22, 2016
1 parent d0c8862 commit 4bc47a3
Show file tree
Hide file tree
Showing 12 changed files with 500 additions and 314 deletions.
39 changes: 23 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,34 @@ This terraform module can create a cross zone vpc.
###### Result

This will create a vpc across multiple zone with a couple of subnet, routing
table, bastion instance, elb ans Managed NAT
table, bastion instance, elb and Managed NAT

### Subnet

There will be two level of subnet the front and the back and there will be
a subnet per type per zone
There will be two level of subnet the public and the private and there will be
a subnet per level per zone

### route table

two routing table are create one public and one private the public is linked to
the front subnets and the Gateway
the private to the back subnets and the Managed NAT
one routing table are create one public and one for each subnet in private
subnets.
the public is linked to the public's subnets and the Internet Gateway, the
private to the private's subnets and the Managed NAT

### bastion instance and routing

all bastion instance will be launched in an auto scaling group in the from subnets
When a new Bastion instance is spawned a command to change the EIP association
is run to make sure we always have the same IP
A bastion is launched through an auto scaling group in all public's subnets.
In each subnet an ENI and EIP is created.
When a new Bastion instance is booted a command to change the ENI association
is run to make sure he always have associated with one ENI/EIP.
Some DNS record and healh-check will route to bastion no matter with ENI is
using.

### NAT

All front subnet have one Managed NAT gateway and a routing table is create for
each back subnet with a route from the subnet to the (AZ) corresponding NAT gateway
All public's subnet have one Managed NAT gateway and a routing table is create
for each private's subnet with a route from the subnet to the (AZ)
corresponding NAT gateway

## Schema for 3 zone

Expand All @@ -48,7 +53,7 @@ All front subnet have one Managed NAT gateway and a routing table is create for
| .---------. .---------. .---------. |
| | | | | | | |
| | subnet | | subnet | | subnet | |
| | front a | | front b | | front c | |
| | pub a | | pub b | | pub c | |
| | | | | | | |
| | .---. | | .---. | | .---. | |
| | |NAT| | | |NAT| | | |NAT| | |
Expand All @@ -64,7 +69,7 @@ All front subnet have one Managed NAT gateway and a routing table is create for
| .---------. .---------. .---------. |
| | | | | | | |
| | subnet | | subnet | | subnet | |
| | back a | | back b | | back c | |
| | priv a | | priv b | | priv c | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
Expand All @@ -82,8 +87,10 @@ All front subnet have one Managed NAT gateway and a routing table is create for
As input you need to specified the following variable:


region: the region you want you vpc on
azs_name: the zone you want your vps on
region: the region you want your vpc on
azs_name: the zone you want your vpc on
azs_count: number of zone you want your vpc on, must be not be superior of the
number of zone available on the region

network_network: a number to determine the network prefix
(10,<network_number>.0.0/24)
Expand All @@ -105,7 +112,7 @@ tags: all tag who should be on every resources

vpc_id: id of the vpc newly created

subnets: all subnet id from the private area value
subnets: all subnet id from the private

default_sg: the default SecurityGroup for the vpc

Expand Down
127 changes: 127 additions & 0 deletions bastion.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# bastion configuration

resource "aws_eip" "bastion" {
count = "${var.azs_count}"

# count = "${length(aws_network_interface.bastion.*.id)}"
network_interface = "${element(aws_network_interface.bastion.*.id,count.index)}"
vpc = true

lifecycle {
create_before_destroy = true
}
}

resource "aws_network_interface" "bastion" {
count = "${var.azs_count}"

# count = "${length(aws_subnet.public.*.id)}"
subnet_id = "${element(aws_subnet.public.*.id,count.index)}"
description = "${var.cluster_name}_bastion endpoint for ${element(aws_subnet.public.*.availability_zone,count.index)}"
security_groups = ["${aws_security_group.allow_bastion_ingress.id}"]

tags {
Name = "${var.cluster_name}_eni-bastion-${element(aws_subnet.public.*.id,count.index)}"
cluster = "${var.cluster_name}"
product = "${var.tag_product}"
purpose = "${var.tag_purpose}"
builder = "terraform"
}

lifecycle {
create_before_destroy = true
}
}

resource "aws_autoscaling_group" "bastion" {
name = "${var.cluster_name}_bastion"
max_size = 1
min_size = 1
desired_capacity = 1

health_check_grace_period = 120
health_check_type = "EC2"
force_delete = true
launch_configuration = "${aws_launch_configuration.bastion.name}"
vpc_zone_identifier = ["${aws_subnet.public.*.id}"]

tag {
key = "Name"
value = "${var.cluster_name}_bastion"
propagate_at_launch = true
}

tag {
key = "builder"
value = "terraform"
propagate_at_launch = true
}

tag {
key = "cluster"
value = "${var.cluster_name}"
propagate_at_launch = true
}

tag {
key = "product"
value = "${var.tag_product}"
propagate_at_launch = true
}

tag {
key = "purpose"
value = "${var.tag_purpose}"
propagate_at_launch = true
}

lifecycle {
create_before_destroy = true
}
}

resource "aws_launch_configuration" "bastion" {
name_prefix = "${var.cluster_name}_bastion-"
image_id = "${lookup(var.ami_bastion, var.region)}"
instance_type = "${var.instance_type_bastion}"
key_name = "${var.aws_key_name}"
associate_public_ip_address = true
iam_instance_profile = "${aws_iam_instance_profile.bastion_profile.name}"
security_groups = ["${aws_security_group.allow_bastion_ingress.id}", "${aws_security_group.bastion.id}"]

user_data = "${template_file.launch_bastion.rendered}\n${var.user_data}"

lifecycle {
create_before_destroy = true
}
}

resource "template_file" "launch_bastion" {
vars {
region = "${var.region}"
enis_map = "${join(" ",template_file.enis_map.*.rendered)}"
}

template = "${file("${path.module}/bastion_user_data.tpl")}"

lifecycle {
create_before_destroy = true
}
}

resource "template_file" "enis_map" {
# count = "${length(aws_network_interface.bastion.*.id)}"

count = "${var.azs_count}"

vars {
zone = "${replace(element(split(" ", element(aws_network_interface.bastion.*.description,count.index)),3),"${var.region}","")}"
eni = "${element(aws_network_interface.bastion.*.id,count.index)}"
}

template = "[\"${zone}\"]=\"${eni}\""

lifecycle {
create_before_destroy = true
}
}
37 changes: 8 additions & 29 deletions bastion_user_data.tpl
Original file line number Diff line number Diff line change
@@ -1,32 +1,11 @@
#!/bin/bash -v
ZONE=$(wget -qO - http://169.254.169.254/latest/meta-data/placement/availability-zone)
INSTANCE_ID=$$(wget http://169.254.169.254/latest/meta-data/instance-id -O - -q)

EC2_URL=https://ec2.${region}.amazonaws.com
declare -A enis_map
enis_map=(${enis_map})
eni=$${enis_map[$${ZONE: -1}]}
/usr/bin/aws ec2 attach-network-interface --network-interface-id $${eni} --instance-id $${INSTANCE_ID} --device-index 1 --region '${region}'
/bin/sleep 60

# set fqdn pointing to instance ip
if [[ -n "${route53_zone_id}" && -n "${fqdn}" ]]; then
MYMAC=$(wget http://169.254.169.254/latest/meta-data/network/interfaces/macs/ -O - -q)
MYIP=$(wget http://169.254.169.254/latest/meta-data/network/interfaces/macs/$MYMAC/ipv4-associations -O - -q)

cat << EOF > /tmp/route53-change.json
{
"Comment": "Updating Bastion Host Record",
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "bastion-${fqdn}",
"Type": "A",
"TTL": 60,
"ResourceRecords": [
{
"Value": "$MYIP"
}
]
}
}
]
}
EOF
aws route53 change-resource-record-sets --hosted-zone-id "${route53_zone_id}" --change-batch file:///tmp/route53-change.json
rm -rf /tmp/route53-change.json
fi
ec2ifscan
36 changes: 36 additions & 0 deletions dns.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
resource "aws_route53_record" "bastion" {
#count = "${length(aws_network_interface.bastion.*.private_ips)}"
count = "${var.azs_count}"

name = "bastion-${var.cluster_name}"
zone_id = "${var.route_zone_id}"
type = "A"
set_identifier = "${count.index}"
weight = "${count.index}"
records = ["${element(aws_eip.bastion.*.public_ip,count.index)}"]
ttl = 60
health_check_id = "${element(aws_route53_health_check.bastion_check.*.id,count.index)}"
}

resource "aws_route53_health_check" "bastion_check" {
count = "${var.azs_count}"

#count = "${length(aws_route53_record.bastion.*.)}"
ip_address = "${element(aws_eip.bastion.*.public_ip,count.index)}"
port = 22
type = "TCP"
failure_threshold = "2"
request_interval = "10"

tags {
Name = "${var.cluster_name}_bastion-check"
cluster = "${var.cluster_name}"
product = "${var.tag_product}"
purpose = "${var.tag_purpose}"
builder = "terraform"
}

lifecycle {
create_before_destroy = true
}
}
58 changes: 58 additions & 0 deletions iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
resource "aws_iam_role" "bastion_role" {
name = "${var.cluster_name}_bastion_role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF

lifecycle {
create_before_destroy = true
}
}

resource "aws_iam_instance_profile" "bastion_profile" {
name = "${var.cluster_name}_bastion_profile"
roles = ["${aws_iam_role.bastion_role.name}"]

lifecycle {
create_before_destroy = true
}
}

resource "aws_iam_role_policy" "bastion_policy" {
name = "${var.cluster_name}_bastion_policy"
role = "${aws_iam_role.bastion_role.id}"

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": [
"ec2:AttachNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DetachNetworkInterface"
],
"Resource": [
"*"
]
}
}
EOF

lifecycle {
create_before_destroy = true
}
}
23 changes: 23 additions & 0 deletions nat.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# NAT
resource "aws_eip" "gw_ip" {
count = "${var.azs_count}"

# count = "${length(aws_subnet.public.*.id)}"
vpc = true

lifecycle {
create_before_destroy = true
}
}

resource "aws_nat_gateway" "gw" {
count = "${var.azs_count}"

# count = "${length(aws_eip.gw_ip.*.id)}"

allocation_id = "${element(aws_eip.gw_ip.*.id, count.index)}"
subnet_id = "${element(aws_subnet.public.*.id, count.index)}"
lifecycle {
create_before_destroy = true
}
}
Loading

0 comments on commit 4bc47a3

Please sign in to comment.