camilo cabrales
Posted on August 16, 2022
Terraform es una herramienta de IaC multicloud, basado en el lenguaje de configuración Hansicorp que es un tipo de JSON.
Las plantillas de Terraform generalmente se distribuyen de la siguiente forma:
Provider: Se define la nube en la cual vamos a trabajar.
DataSources-Parameters: Se definen los parámetros o variables que vamos a utilizar dentro de nuestra plantilla.
Resources: Definimos los recursos a desplegar.
La siguiente arquitectura muestra los recursos que vamos a desplegar en AWS con Terraform.
# PROVIDER
provider "aws" {
region = "${var.aws_regions[0]}"
#Se definen los tags comunes a todos los resources
default_tags {
tags = {
despliegue = "Terraform AWS"
}
}
}
#DATA - PARAMETERS - VARIABLES
locals {
tags = {
despliegue = "Terraform AWS"
}
}
variable "amiinstance" {
default = "ami-090fa75af13c156b4"
type = string
}
variable "aws_regions" {
default = [
"us-east-1",
"us-east-2"
]
type = list
}
variable "aws_instance_size" {
type = map
default= {
small = "t2.micro",
medium = "t2.medium",
large = "t2.large"
}
}
variable "aws_instancedb_size" {
type = map
default= {
small = "db.t2.micro",
medium = "db.t2.small"
}
}
variable "tag" {
default = "template_terraform"
type = string
}
variable "cidr_subnets" {
default = [
"10.0.1.0/24",#public
"10.0.2.0/28",#private
"10.0.3.0/28"#private
]
type = list
}
#RESOURCES
resource "aws_vpc" "vpcterraform" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "${var.tag}"
}
}
resource "aws_internet_gateway" "igwterraform" {
vpc_id = "${aws_vpc.vpcterraform.id}"
tags = {
Name = "routeTableTFPublic_${var.tag}"
}
}
resource "aws_route_table" "routeTableTFPublic" {
vpc_id = "${aws_vpc.vpcterraform.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.igwterraform.id}"
}
tags = {
Name = "routeTableTFPublic_${var.tag}"
}
}
resource "aws_route_table" "routeTablerivate" {
vpc_id = "${aws_vpc.vpcterraform.id}"
tags = {
Name = "routeTableTFPrivate_${var.tag}"
}
}
resource "aws_subnet" "publicsubnet1" {
vpc_id = "${aws_vpc.vpcterraform.id}"
cidr_block = var.cidr_subnets[0]
map_public_ip_on_launch = true
availability_zone = "us-east-1a"
tags = {
Name = "publicsubnet1_${var.tag}"
}
}
resource "aws_route_table_association" "routeTableAssociationTF" {
subnet_id = "${aws_subnet.publicsubnet1.id}"
route_table_id = "${aws_route_table.routeTableTFPublic.id}"
}
resource "aws_subnet" "privatesubnet1" {
vpc_id = "${aws_vpc.vpcterraform.id}"
cidr_block = var.cidr_subnets[1]
availability_zone = "us-east-1b"
tags = {
Name = "privatesubnet1_${var.tag}"
}
}
resource "aws_subnet" "privatesubnet2" {
vpc_id = "${aws_vpc.vpcterraform.id}"
cidr_block = var.cidr_subnets[2]
availability_zone = "us-east-1c"
tags = {
Name = "privatesubnet2_${var.tag}"
}
}
resource "aws_db_subnet_group" "dbprivatesubnetgroup"{
name = "dbprivatesubnetgroup"
subnet_ids = ["${aws_subnet.privatesubnet1.id}","${aws_subnet.privatesubnet2.id}"]
tags = {
Name = "dbprivatesubnetgroup_${var.tag}"
}
}
resource "aws_iam_role" "instance_role" {
assume_role_policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
}
}
]
})
tags = {
tag-key = "role_${var.tag}"
}
}
resource "aws_iam_policy" "policy"{
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData",
"ds:CreateComputer",
"ds:DescribeDirectories",
"ec2:DescribeInstanceStatus",
"logs:*",
"ssm:*",
"ec2messages:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM*",
"Condition": {
"StringLike": {
"iam:AWSServiceName": "ssm.amazonaws.com"
}
}
},
{
"Effect": "Allow",
"Action": [
"iam:DeleteServiceLinkedRole",
"iam:GetServiceLinkedRoleDeletionStatus"
],
"Resource": "arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM*"
},
{
"Effect": "Allow",
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
}
]
})
tags = {
tag-key = "policy_${var.tag}"
}
}
resource "aws_iam_role_policy_attachment" "policy_attachment" {
role = "${aws_iam_role.instance_role.name}"
policy_arn = "${aws_iam_policy.policy.arn}"
}
resource "aws_iam_instance_profile" "instance_profile" {
role = "${aws_iam_role.instance_role.name}"
}
resource "aws_security_group" "sg_instance" {
vpc_id = "${aws_vpc.vpcterraform.id}"
ingress {
from_port = 22
to_port = 22
protocol ="tcp"
cidr_blocks = ["0.0.0.0/0"]
}
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 = "sg_instance_${var.tag}"
}
}
resource "aws_instance" "instancia" {
ami= "${var.amiinstance}"
instance_type = "${var.aws_instance_size.small}"
subnet_id = "${aws_subnet.publicsubnet1.id}"
vpc_security_group_ids = [ "${aws_security_group.sg_instance.id}" ]
tags = {
Name = "instancia_${var.tag}"
}
iam_instance_profile = "${aws_iam_instance_profile.instance_profile.id}"
depends_on = [aws_internet_gateway.igwterraform]
user_data = <<EOF
#!/bin/bash
yum update -y
yum install httpd -y
service httpd start
chkconfig httpd on
cd /var/www/html
echo "<html><body><h1>Terraform Instancia: $(hostname -f)</h1></body></html>" > index.html
EOF
}
resource "aws_security_group" "sgbd" {
name = "sg"
vpc_id = "${aws_vpc.vpcterraform.id}"
ingress {
from_port = 3306
to_port = 3306
protocol ="tcp"
cidr_blocks = ["${aws_instance.instancia.private_ip}/32"]
}
tags = {
Name = "sgbd_${var.tag}"
}
}
resource "aws_db_instance" "dbinstance" {
db_subnet_group_name = "${aws_db_subnet_group.dbprivatesubnetgroup.name}"
engine = "mysql"
db_name = "dbterraform"
engine_version = "5.7.28"
instance_class = "${var.aws_instancedb_size.small}"
username = "admin"
password = "admin12345678"
allocated_storage = 10
skip_final_snapshot = true
vpc_security_group_ids = [ "${aws_security_group.sgbd.id}" ]
tags = {
Name = "dbinstance_${var.tag}"
}
}
output "endpoint" {
value = "${aws_instance.instancia.public_ip}"
}
Terraform tiene comandos para iniciar, validar, desplegar y destruir los recursos definidos en nuestras plantillas (Antes de usar los siguientes comandos recuerden configurar sus access keys con el comando aws configure).
1.terraform init: Configura el directorio donde se encuentra nuestra plantilla para que pueda ser usado con Terraform.
2.terraform validate: Valida que la plantilla no tenga errores de sintaxis o de lógica.
3.terraform plan: Nos muestra una descripción de los recursos que van a ser desplegados, valida si hay cambios en nuestros recursos (en caso de actualización) y valida que nuestra plantilla no tenga errores.
4.terraform apply: Nos va a mostrar la infraestructura que se va a desplegar y nos va a preguntar que sí estamos de acuerdo por lo que debemos escribir yes para que inicie el despliegue de nuestros servicios.
Al final nos va a mostrar la cantidad de recursos que se crearon y los outputs que tenemos definidos en la plantilla.
Terraform al desplegar nuestra infraestructura crea el archivo
terraform.tfstate donde se guarda la información de los recursos desplegados.
Podemos ver qué recursos se desplegaron con el comando: terraform state list y podemos ver la información del recurso con el comando terraform state show nombre_del_recurso.
Una vez que se han creado todos nuestros recursos, vamos a verlos en la consola de AWS. Para esto buscamos resources groups y le damos click.
Ya en la pantalla principal damos click en Tag Editor, seleccionamos todas las regiones, todos los recursos, en los tags escribimos despliegue - Terraform AWS y damos click en le botón Search Resources. Esto nos va a traer todos los recursos que tiene este tag.
El resultado de la búsqueda nos va a mostrar los recursos que cumplen las condiciones anteriores.
Este resultado lo obtuvimos ya que a todos los recursos de la plantilla les asignamos el tag: Name: despliegue - Value: Terraform AWS, en el siguiente fragmento de la plantilla:
provider "aws" {
region = "${var.aws_regions[0]}"
#Se definen los tags comunes a todos los resources
default_tags {
tags = {
despliegue = "Terraform AWS"
}
}
}
Ahora vamos a validar el funcionamiento de nuestra aplicación. En la pantalla de resultados damos click en la instancia de EC2 que creamos.
Los que nos lleva a la pantalla de EC2 y copiamos la Ip publica de la instancia y la colocamos en una nueva pestaña de nuestro navegador.
lo que nos va a mostrar lo siguiente:
Este resultado lo obtuvimos ya que en nuestra definición de la instancia dentro dentro de la propiedad user data le pedimos que instalara apache y creara una pagina web.
resource "aws_instance" "instancia" {
ami= "${var.amiinstance}"
instance_type = "${var.aws_instance_size.small}"
subnet_id = "${aws_subnet.publicsubnet1.id}"
vpc_security_group_ids = [ "${aws_security_group.sg_instance.id}" ]
tags = {
Name = "instancia_${var.tag}"
}
iam_instance_profile = "${aws_iam_instance_profile.instance_profile.id}"
user_data = <<EOF
#!/bin/bash
yum update -y
yum install httpd -y
service httpd start
chkconfig httpd on
cd /var/www/html
echo "<html><body><h1>Terraform Instancia: $(hostname -f)</h1></body></html>" > index.html
yum install mysql
EOF
}
De acuerdo a la anterior definición de nuestra instancia, no definimos un access key para poder ingresar a ella desde la terminal.
Para acceder a la instancia y validar nuestra base de datos vamos a buscar el servicio System Manager.
En la pagina principal damos click Session Manager, después en Start Session.
Ahora seleccionamos la instancia que creamos y damos click en el botón Start Session
Lo que nos va abrir una terminal como la siguiente.
Donde podemos ejecutar los comandos para acceder a la base de datos creada con la plantilla:
Instalar mysql (para usar como cliente):
sudo su
sudo yum install mysql
Conectarse a la base de datos:
mysql -h reemplezar_por_endpoint_basededatos -u admin -p database
Para lograr abrir la terminal desde System Manager se crearon los siguientes recursos en la plantilla:
aws_iam_role
aws_iam_policy
aws_iam_role_policy_attachment
aws_iam_instance_profile
para asociar el recurso aws_iam_instance_profile a nuestra instancia.
5.terraform destroy: al ejecutar el comando nos muestra los recursos que se van a eliminar, después nos solicita que autoricemos la eliminación por lo que debemos escribir yes.
Con la ejecución de estos comando terminamos con el despliegue y la liberación de los recursos desplegados con Terraform.
Terraform me parece practico ya que su sintaxis se me asemeja al desarrollo (he trabajado como desarrollador). Una de las cosas que creo mas importantes para trabajar con herramientas IAC es la documentación la cual me pareció sencilla pero practica.
Para mejorar la plantilla pueden crear archivos separados para las variables, para los outputs y realizar la misma ejecución de comandos, esto con el fin que nuestra plantilla quede más organizada y legible.
Referencias
Posted on August 16, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 17, 2024