Building A Dynamic vSphere/AWS Hybrid Cloud Lab – Part 2

This post covers the second part of the blog series on creating a dynamic hybrid cloud lab environment with a local (on-prem) vSphere environment and an AWS environment.

Part #1: http://www.greenreedtech.com/building-a-dynamic-vsphere-aws-hybrid-cloud-lab-part-1/

In this post we’ll cover building out the AWS side of the hybrid cloud.

All the code from this series can be found on github.
https://github.com/martezr/terraform-aws-vsphere-lab

Overview

Before diving into the actual Terraform code we’ll take a look at the high level tasks that we need to accomplish.

  1. Create a VPC
  2. Create an internet gateway
  3. Create a public subnet
  4. Create a route table for the public subnet
  5. Create a private subnet
  6. Create a route table for the private subnet
  7. Create a security group for the router instance
  8. Create a router instance
  9. Create a security group for the web instance
  10. Create a web instance

Setup AWS Credentials

The first task in building out the AWS infrastructure is to create an IAM user dedicated to terraform that will be used by terraform for managing the AWS environment.

I’ve decided to utilize variables for providing the access and secret keys but there are other options available (
https://www.terraform.io/docs/providers/aws/index.html).

# Configure the AWS Provider
provider "aws" {  
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
  region     = "us-east-1"
}

Create VPC

By default when you create an AWS account a “default” VPC is created which in our case would work just fine but since mult-VPC environments are pretty common we’ll figure out how to use Terraform to create a new one.

The CIDR of the VPC is defined in a variable which allows the code to be easily manipulated for different environments with different IP schemes.

We’re using the “Name” tag to populate the name in the AWS console.

# Create hybrid VPC
resource "aws_vpc" "hybrid" {  
  cidr_block = "${var.aws_vpc_cidr}"
  tags {
    Name = "hybrid"
  }
}

Create Internet Gateway

With our VPC created we now need to create an internet gateway to provide external connectivity from our VPC to the internet. Terraform will use the id of the VPC we create as an input with the syntax of ${resource.resource_name.id}.

resource "aws_internet_gateway" "hybridgw" {  
  vpc_id = "${aws_vpc.hybrid.id}"
  tags {
    Name = "hybridgw"
  }
}

Create the Public subnet

With the VPC and Internet Gateway in place we need to create the public subnet that will host our VPN/Router instance.

  • Create the subnet
  • Create the route table
  • Create the route table to subnet association

The route in the routing table allows the VPN/Router instance to send all traffic to the Internet Gateway.

# Public subnet
resource "aws_subnet" "public_subnet" {  
  vpc_id     = "${aws_vpc.hybrid.id}"
  cidr_block = "${var.aws_public_cidr}"
  tags {
    Name = "public_subnet"
  }
}

resource "aws_route_table" "public_route_table" {  
  vpc_id = "${aws_vpc.hybrid.id}"

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = "${aws_internet_gateway.hybridgw.id}"
  }
  tags {
    Name = "public_route_table"
  }
}

# Public subnet route table mapping
resource "aws_route_table_association" "public_subnet_route_mapping" {  
  subnet_id      = "${aws_subnet.public_subnet.id}"
  route_table_id = "${aws_route_table.public_route_table.id}"
}

Create the Private subnet

We now need to create a private subnet to host the web instance.

  • Create the subnet
  • Create the route table
  • Create the route table to subnet association

The route in the routing table will force all traffic to pass through the Router/VPN instance in the public subnet.

# Private subnet
resource "aws_subnet" "private_subnet" {  
  vpc_id     = "${aws_vpc.hybrid.id}"
  cidr_block = "${var.aws_private_cidr}"
  tags {
    Name = "private_subnet"
  }
}

# Private subnet route table mapping
resource "aws_route_table_association" "private_subnet_route_mapping" {  
  subnet_id      = "${aws_subnet.private_subnet.id}"
  route_table_id = "${aws_route_table.private_route_table.id}"
}

resource "aws_route_table" "private_route_table" {  
  vpc_id = "${aws_vpc.hybrid.id}"

  route {
    cidr_block = "0.0.0.0/0"
    instance_id = "${aws_instance.router.id}"
  }
  tags {
    Name = "private_route_table"
  }
}

Create the Elastic IP Address

resource "aws_eip" "router" {  
  instance = "${aws_instance.router.id}"
  vpc      = true
}

Create the Router Security Group

Let’s create our security group for our router instance to allow access to and from where we need.

  • Allow any to reach 22 (SSH)
  • Allow any to reach port 443 (OpenVPN)
  • Allow AWS VPC and vSphere network to reach ICMP
  • Allow outbound to any on port 443
  • Allow outbound to any on port 80

    resource “aws_security_group” “router” {
    name = “hybridrouter”
    description = “Allow incoming VPN connections.”

    ingress { # VPN
        from_port = 443
        to_port = 443
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    
    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    ingress {
        from_port = -1
        to_port = -1
        protocol = "icmp"
        cidr_blocks = ["${var.aws_vpc_cidr}","${var.vsphere_hybrid_cidr}"]
    }
    
    egress {
        from_port = 80
        to_port = 80
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    egress {
        from_port = 443
        to_port = 443
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    
    vpc_id = "${aws_vpc.hybrid.id}"
    
    tags {
        Name = "RouterSG"
    }
    

    }

Create the Router Instance

Now that we’ve got our security group in place for the router instance we’ll create the router instance. We’re utilizing a predefined ssh key tied to our AWS account and disabling the source/destination check to allow traffic to pass through our router instance.

We’re using a public CentOS 7 ami provided by Centos.org.

resource "aws_instance" "router" {  
  ami           = "ami-ae7bfdb8"
  instance_type = "t2.micro"
  key_name      = "terraform"
  subnet_id     = "${aws_subnet.public_subnet.id}"
  source_dest_check = "false"
  vpc_security_group_ids = ["${aws_security_group.router.id}"]
  tags {
    Name = "hybridrouter"
  }
  root_block_device {
    volume_size = "8"
    delete_on_termination = true
  }
}

Create the Web Security Group

Let’s create our security group for our router instance to allow access to and from where we need.

  • Allow any to reach 22 (SSH)
  • Allow AWS VPC and vSphere network to reach port 80 (Apache)
  • Allow AWS VPC and vSphere network to reach ICMP
  • Allow outbound to any on port 443
  • Allow outbound to any on port 80

    resource “aws_security_group” “web” {
    name = “hybridweb”
    description = “Allow incoming web connections.”

    ingress { # Web
        from_port = 80
        to_port = 80
        protocol = "tcp"
        cidr_blocks = ["${var.aws_vpc_cidr}","${var.vsphere_hybrid_cidr}"]
    }
    
    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = ["${var.aws_vpc_cidr}"]
    }
    ingress {
        from_port = -1
        to_port = -1
        protocol = "icmp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    
    egress {
        from_port = 80
        to_port = 80
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    egress {
        from_port = 443
        to_port = 443
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    
    vpc_id = "${aws_vpc.hybrid.id}"
    
    tags {
        Name = "WebSG"
    }
    

    }

Create the Web Instance

The last thing we need to do is to create our web instance.

resource "aws_instance" "web" {  
  ami           = "ami-ae7bfdb8"
  instance_type = "t2.micro"
  key_name      = "terraform"
  subnet_id     = "${aws_subnet.private_subnet.id}"
  vpc_security_group_ids = ["${aws_security_group.web.id}"]
  tags {
    Name = "hybridweb"
  }
  root_block_device {
    volume_size = "8"
    delete_on_termination = true
  }

}

Hybrid AWS Terraform

Now that we’ve walked through all of the components for building out the AWS side of hybrid cloud the complete Terraform file is displayed below.

# Configure the AWS Provider
provider "aws" {  
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
  region     = "us-east-1"
}


# Create hybrid VPC
resource "aws_vpc" "hybrid" {  
  cidr_block = "${var.aws_vpc_cidr}"
  tags {
    Name = "hybrid"
  }
}

# Create internet gateway
resource "aws_internet_gateway" "hybridgw" {  
  vpc_id = "${aws_vpc.hybrid.id}"
  tags {
    Name = "hybridgw"
  }
}

# Public subnet
resource "aws_subnet" "public_subnet" {  
  vpc_id     = "${aws_vpc.hybrid.id}"
  cidr_block = "${var.aws_public_cidr}"
  tags {
    Name = "public_subnet"
  }
}

resource "aws_route_table" "public_route_table" {  
  vpc_id = "${aws_vpc.hybrid.id}"

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = "${aws_internet_gateway.hybridgw.id}"
  }
  tags {
    Name = "public_route_table"
  }
}

# Public subnet route table mapping
resource "aws_route_table_association" "public_subnet_route_mapping" {  
  subnet_id      = "${aws_subnet.public_subnet.id}"
  route_table_id = "${aws_route_table.public_route_table.id}"
}

# Private subnet
resource "aws_subnet" "private_subnet" {  
  vpc_id     = "${aws_vpc.hybrid.id}"
  cidr_block = "${var.aws_private_cidr}"
  tags {
    Name = "private_subnet"
  }
}

# Private subnet route table mapping
resource "aws_route_table_association" "private_subnet_route_mapping" {  
  subnet_id      = "${aws_subnet.private_subnet.id}"
  route_table_id = "${aws_route_table.private_route_table.id}"
}

resource "aws_route_table" "private_route_table" {  
  vpc_id = "${aws_vpc.hybrid.id}"

  route {
    cidr_block = "0.0.0.0/0"
    instance_id = "${aws_instance.router.id}"
  }
  tags {
    Name = "private_route_table"
  }
}

resource "aws_eip" "router" {  
  instance = "${aws_instance.router.id}"
  vpc      = true
}

resource "aws_instance" "router" {  
  ami           = "ami-ae7bfdb8"
  instance_type = "t2.micro"
  key_name      = "terraform"
  subnet_id     = "${aws_subnet.public_subnet.id}"
  source_dest_check = "false"
  vpc_security_group_ids = ["${aws_security_group.router.id}"]
  tags {
    Name = "hybridrouter"
  }
  root_block_device {
    volume_size = "8"
    delete_on_termination = true
  }
}

resource "aws_security_group" "router" {  
    name = "hybridrouter"
    description = "Allow incoming VPN connections."

    ingress { # VPN
        from_port = 443
        to_port = 443
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }

    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    ingress {
        from_port = -1
        to_port = -1
        protocol = "icmp"
        cidr_blocks = ["${var.aws_vpc_cidr}","${var.vsphere_hybrid_cidr}"]
    }

    egress {
        from_port = 80
        to_port = 80
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    egress {
        from_port = 443
        to_port = 443
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }

    vpc_id = "${aws_vpc.hybrid.id}"

    tags {
        Name = "RouterSG"
    }
}


resource "aws_instance" "web" {  
  ami           = "ami-ae7bfdb8"
  instance_type = "t2.micro"
  key_name      = "terraform"
  subnet_id     = "${aws_subnet.private_subnet.id}"
  vpc_security_group_ids = ["${aws_security_group.web.id}"]
  tags {
    Name = "hybridweb"
  }
  root_block_device {
    volume_size = "8"
    delete_on_termination = true
  }

}

resource "aws_security_group" "web" {  
    name = "hybridweb"
    description = "Allow incoming web connections."

    ingress { # Web
        from_port = 80
        to_port = 80
        protocol = "tcp"
        cidr_blocks = ["${var.aws_vpc_cidr}","${var.vsphere_hybrid_cidr}"]
    }

    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = ["${var.aws_vpc_cidr}"]
    }
    ingress {
        from_port = -1
        to_port = -1
        protocol = "icmp"
        cidr_blocks = ["0.0.0.0/0"]
    }

    egress {
        from_port = 80
        to_port = 80
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    egress {
        from_port = 443
        to_port = 443
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }

    vpc_id = "${aws_vpc.hybrid.id}"

    tags {
        Name = "WebSG"
    }
}

References

Terraform AWS Example
https://nickcharlton.net/posts/terraform-aws-vpc.html

Terraform AWS Provider
https://www.terraform.io/docs/providers/aws/index.html