¿Qué es Terraform?
Terraform es un software de código libre que permite, a partir de un lenguaje de alto nivel, crear, modificar o destruir una infraestructura compleja en alguna plataforma de Cloud Computing, como Amazon Web Services, Microsoft Azure o Google Cloud Platform, entre otras. A este proceso de gestión de recursos mediante código se le denomina IaC (Infrastucture as Code)
Escenario
En primer lugar, veamos el escenario que vamos a desplegar:

Código
A continuación vemos el script de creación dividido en trozos que corresponden con los recursos que pretendemos crear:
Proveedor y credenciales
En primer lugar se especifican el proveedor y las credenciales:
# Proveedor y credenciales
provider "aws" {
profile = "default" # Recoge las credenciales del fichero .aws/credentials
region = "eu-west-1"
}
Creación de variables
Las variables, que podríamos rellenar con parámetros del script, y que tienen valores por defecto por si estos no se introducen:
# Variables
variable "vpc-name" {
description = "El nombre de la vpc a crear"
type = string
default = "mi-vpc"
}
variable "vpc-cidr" {
description = "La dirección de red de la vpc a crear"
type = string
default = "10.0.0.0/16"
}
variable "igw-name" {
description = "Nombre del Internet Gateway"
type = string
default = "mi-igw"
}
variable "private-subnet1-name" {
description = "Nombre de la subred privada 1"
type = string
default = "private-subnet1"
}
variable "private-subnet1-cidr" {
description = "Rango de direcciones de la subred privada 1"
type = string
default = "10.0.1.0/24"
}
variable "private-subnet2-name" {
description = "Nombre de la subred privada 2"
type = string
default = "private-subnet2"
}
variable "private-subnet2-cidr" {
description = "Rango de direcciones de la subred privada 2"
type = string
default = "10.0.2.0/24"
}
variable "public-subnet1-name" {
description = "Nombre de la subred publica 1"
type = string
default = "public-subnet1"
}
variable "public-subnet1-cidr" {
description = "Rango de direcciones de la subred pública 1"
type = string
default = "10.0.3.0/24"
}
variable "public-subnet2-name" {
description = "Nombre de la subred publica 2"
type = string
default = "public-subnet2"
}
variable "public-subnet2-cidr" {
description = "Rango de direcciones de la subred publica 2"
type = string
default = "10.0.4.0/24"
}
variable "route-table-public-subnet1-name" {
description = "Nombre de la tabla de rutas para la subred publica"
type = string
default = "route-table-public-subnet1"
}
variable "route-table-public-subnet2-name" {
description = "Nombre de la tabla de rutas para la subred publica"
type = string
default = "route-table-public-subnet2"
}
variable "eip-name" {
description = "Nombre de la eip para el nat gateway"
type = string
default = "eip-ngw"
}
variable "ngw-name" {
description = "Nombre para el nat gateway"
type = string
default = "ngw"
}
Creación de recursos
Creación de la VPC:
# VPC
resource "aws_vpc" "main" { # Se exportan los atributos del recurso, pudiendose utilizar después
cidr_block = var.vpc-cidr
tags = {
Name = var.vpc-name
}
}
Creación de subredes:
# Subnet privada 1 (eu-west-a)
resource "aws_subnet" "private-subnet1" {
vpc_id = "${aws_vpc.main.id}"
availability_zone = "${data.aws_availability_zones.available.names[0]}"
cidr_block = var.private-subnet1-cidr
tags = {
Name = var.private-subnet1-name
}
}
# Subnet privada 2 (eu-west-b)
resource "aws_subnet" "private-subnet2" {
vpc_id = "${aws_vpc.main.id}"
availability_zone = "${data.aws_availability_zones.available.names[1]}"
cidr_block = var.private-subnet2-cidr
tags = {
Name = var.private-subnet2-name
}
}
# Subnet publica 2 (eu-west-b)
resource "aws_subnet" "public-subnet2" {
vpc_id = "${aws_vpc.main.id}"
availability_zone = "${data.aws_availability_zones.available.names[1]}"
cidr_block = var.public-subnet2-cidr
map_public_ip_on_launch = true # auto-assing public ip
tags = {
Name = var.public-subnet2-name
}
}
# Subnet publica 1 (eu-west-a)
resource "aws_subnet" "public-subnet1" {
vpc_id = "${aws_vpc.main.id}"
availability_zone = "${data.aws_availability_zones.available.names[0]}"
cidr_block = var.public-subnet1-cidr
map_public_ip_on_launch = true # auto-assing public ip
tags = {
Name = var.public-subnet1-name
}
}
Creación del Internet Gateway
# Internet Gateway
resource "aws_internet_gateway" "igw" {
vpc_id = "${aws_vpc.main.id}" # Id exportado en la creación de la vpc
tags = {
Name = var.igw-name
}
}
Creación de subredes y asociaciones:
# Tabla de rutas para la subred pública 1
resource "aws_route_table" "route-table-public-subnet1" {
vpc_id = "${aws_vpc.main.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.igw.id}"
}
tags = {
Name = var.route-table-public-subnet1-name
}
}
resource "aws_route_table_association" "route-table-association-public-subnet1" {
subnet_id = "${aws_subnet.public-subnet1.id}"
route_table_id = "${aws_route_table.route-table-public-subnet1.id}"
}
# Tabla de rutas para la subred pública 2
resource "aws_route_table" "route-table-public-subnet2" {
vpc_id = "${aws_vpc.main.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.igw.id}"
}
tags = {
Name = var.route-table-public-subnet2-name
}
}
resource "aws_route_table_association" "route-table-association-public-subnet2" {
subnet_id = "${aws_subnet.public-subnet2.id}"
route_table_id = "${aws_route_table.route-table-public-subnet2.id}"
}
# Tabla de rutas para la subred privada 1
resource "aws_route_table" "route-table-private-subnet1" {
vpc_id = "${aws_vpc.main.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_nat_gateway.nat-gw.id}"
}
tags = {
Name = "route-table-private-subnet1"
}
}
resource "aws_route_table_association" "route-table-association-private-subnet1" {
subnet_id = "${aws_subnet.private-subnet1.id}"
route_table_id = "${aws_route_table.route-table-private-subnet1.id}"
}
# Tabla de rutas para la subred privada 2
resource "aws_route_table" "route-table-private-subnet2" {
vpc_id = "${aws_vpc.main.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_nat_gateway.nat-gw.id}"
}
tags = {
Name = "route-table-private-subnet2"
}
}
resource "aws_route_table_association" "route-table-association-private-subnet2" {
subnet_id = "${aws_subnet.private-subnet2.id}"
route_table_id = "${aws_route_table.route-table-private-subnet2.id}"
}
Creación de la Elastic IP
# Elastic IP para el NAT Gateway
resource "aws_eip" "eip" {
vpc = true
tags = {
Name = var.eip-name
}
}
Creación del NAT Gateway
# NAT Gateway
resource "aws_nat_gateway" "nat-gw" {
allocation_id = "${aws_eip.eip.id}"
subnet_id = "${aws_subnet.public-subnet1.id}"
tags = {
Name = var.ngw-name
}
}
Creación de un Security Group
# Security Group
resource "aws_security_group" "allow_http" {
name = "allow_http"
description = "Allow http inbound traffic"
vpc_id = "${aws_vpc.main.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"]
}
tags = {
Name = "allow-http-sg"
}
}
Creación de la instancia EC2
# EC2 Webserver
resource "aws_instance" "my-instance" {
ami = "ami-08d658f84a6d84a80"
instance_type = "t2.nano"
security_groups = ["${aws_security_group.allow_http.id}"]
subnet_id = "${aws_subnet.public-subnet1.id}"
user_data = "${file("install_apache.sh")}"
tags = {
Name = "webserver"
}
}
Script para el user_data de la instancia:
#! /bin/bash
apt-get update
apt-get install -y apache2
systemctl start apache2
systemctl enable apache2
echo "<h1>Deployed via Terraform</h1>" | sudo tee /var/www/html/index.html
A continuación se muestra el despliegue del entorno con Terraform: