Como crear VPC, RDS y EC2 usando Terraform

Hello World 🙂

Continuando con nuestra serie de ejercicios sobre Terraform y la creación de recursos en AWS, hoy les presento otro ejercicio práctico. En esta ocasión, nos sumergiremos en la creación de una VPC, que como siempre, será la base sobre la cual desplegaremos nuestros recursos. Además, daremos un paso más allá al desplegar una instancia EC2 y una instancia de RDS (Relational Database Service), un servicio de base de datos relacional altamente escalable en AWS, en este ejemplo configuraremos una instancia de tipo MySQL.

Para asegurar el cumplimiento de las buenas prácticas, ubicaremos nuestra RDS en subredes privadas dentro de la VPC, mientras que nuestra instancia EC2 residirá en subredes públicas. Esta configuración proporciona una capa adicional de seguridad al proteger nuestra base de datos de accesos no autorizados desde el exterior. Al mismo tiempo, albergar nuestra instancia EC2 en subredes públicas nos permite mantener la conectividad necesaria para administrar y acceder a nuestra infraestructura de forma remota.

Desde la instancia EC2, estableceremos una conexión con la RDS para crear una base de datos, lo que nos permitirá evaluar nuestra configuración en un entorno práctico. Al seguir estas buenas prácticas, garantizamos un entorno más seguro y controlado.

¡Prepárate para adentrarte en el mundo de la automatización de infraestructura con Terraform!

Requisitos:

  • Tener una cuenta de AWS.
  • Tener instalado Terraform.

Esta va a hacer la estructura de nuestro proyecto;

1- Primero necesitamos exporta nuestras credenciales de AWS;

  • export AWS_ACCESS_KEY_ID="XXXXXXXXXX"
    export AWS_SECRET_ACCESS_KEY="XXXXXXXXXX"

2- Crear el fichero 0-provider.tf, definiremos el proveedor y la región que utilizaremos en este proyecto. Estos proveedores son esenciales, ya que permiten a Terraform interactuar con los distintos servicios de infraestructura en la nube necesarios para desplegar los recursos correctamente.

  • provider "aws" {
      region = "us-west-1"
    }

2- Crear el fichero 1-vpc.tf, definiremos todo lo necesario para generar nuestra VPC, que servirá como la base de la infraestructura del proyecto. Aquí, definiremos subredes y demás componentes claves que permitirán una red segura y eficiente para los recursos que desplegaremos más adelante.

  • module "vpc" {
      source  = "terraform-aws-modules/vpc/aws"
      name = "main-module"
      cidr = "10.0.0.0/16"
      azs             = ["us-west-1a", "us-west-1b"]
      private_subnets = ["10.0.0.0/19", "10.0.32.0/19"]
      public_subnets  = ["10.0.64.0/19", "10.0.96.0/19"]
      enable_dns_hostnames = true
      enable_dns_support   = true
      enable_nat_gateway     = true
      single_nat_gateway     = true
      one_nat_gateway_per_az = false
      public_subnet_tags = {
        "Name"                       = "public-subnet"
        "kubernetes.io/role/elb"     = 1
        "kubernetes.io/cluster/demo" = "owned"
      }
      private_subnet_tags = {
        "Name"                       = "private-subnet"
        "kubernetes.io/role/internal-elb" = 1
        "kubernetes.io/cluster/demo"      = "owned"
      }
    }

3- Crear el fichero 2-rds.tf, definiremos los parámetros necesarios para desplegar una instancia RDS de tipo MySQL. Este archivo incluirá los valores de configuración clave para la base de datos, como el tipo de instancia, el tamaño de almacenamiento y otros ajustes importantes que optimizarán el rendimiento y la seguridad de nuestro RDS en AWS.;

  • module "rds" {
      source  = "terraform-aws-modules/rds/aws"
      version = "2.35.0"
      identifier           = "demodb"
      engine               = "mysql"
      engine_version       = "8.0.35"
      family               = "mysql8.0" # DB parameter group
      major_engine_version = "8.0"      # DB option group
      instance_class       = "db.t4g.large"
      allocated_storage     = 5
      max_allocated_storage = 100
      username = "admin"
      port     = "3306"
      password = "Qaz123456789*2024"
      option_group_name = "mysql-8-0"
      parameter_group_name = "mysql-8-0"
      multi_az               = true
      vpc_security_group_ids = [aws_security_group.rds.id]
      maintenance_window              = "Mon:00:00-Mon:03:00"
      backup_window                   = "03:00-06:00"
      backup_retention_period         = 0
      enabled_cloudwatch_logs_exports = ["general"]
      performance_insights_enabled          = true
      performance_insights_retention_period = 7
      create_monitoring_role                = true
      monitoring_interval                   = "30"
      monitoring_role_name                  = "MyRDSMonitoringRole"
      create_db_subnet_group = true
      subnet_ids             = module.vpc.private_subnets
      skip_final_snapshot = true
      deletion_protection = false
      tags = {
        Owner       = "user"
        Environment = "dev"
      }
      parameters = [
        {
          name  = "character_set_client"
          value = "utf8mb4"
        },
        {
          name  = "character_set_server"
          value = "utf8mb4"
        }
      ]
      options = [
        {
          option_name = "MARIADB_AUDIT_PLUGIN"
          option_settings = [
            {
              name  = "SERVER_AUDIT_EVENTS"
              value = "CONNECT"
            },
            {
              name  = "SERVER_AUDIT_FILE_ROTATIONS"
              value = "37"
            },
          ]
        },
      ]
    }

4-  Crear el fichero 3-security.tf, definiremos los parámetros necesarios para generar dos Security Groups: uno para la instancia EC2 y otro para la base de datos RDS. Este archivo nos permitirá establecer las reglas de acceso y seguridad adecuadas para cada recurso, garantizando una comunicación segura y restringida entre la instancia y la base de datos en AWS.

  • SG para la instancia EC2.
    • resource "aws_security_group" "ec2" {
        name        = "ec2-sg"
        description = "SG-RC2"
        vpc_id = module.vpc.vpc_id
        ingress {
           cidr_blocks = ["0.0.0.0/0"]
           from_port = 22
           to_port = 22
           protocol = "tcp"
         }
        egress {
          from_port       = 0
          to_port         = 0
          protocol        = "-1"
          cidr_blocks     = ["0.0.0.0/0"]
        }
        tags = {
          Name = "ec2-sg"
        }
      }
  • SG para la RDS, permitiendo que se conecte a la instancia EC2
    • resource "aws_security_group" "rds" {
         name = "rds-sg"
         description= "SG-RDS"
         vpc_id = module.vpc.vpc_id
         ingress {
           security_groups = [aws_security_group.ec2.id]
           from_port = 3306
           to_port=3306
           protocol = "tcp"
         }    
             
         egress {
           security_groups = [aws_security_group.ec2.id]
           from_port = 0
           to_port = 0
           protocol = "-1"
         }
         tags = {
          Name = "rds-sg"
        }
       }

5- Crear el fichero 4-ec2.tf, definiremos los parámetros necesarios para generar la instancia EC2;

  • Creamos la instancia EC2 e instalamos el comando telnet para futuras pruebas.
    • resource "aws_instance" "testinstance" {
          count = 1
          ami = "ami-05af0694d2e8e6df3"
          instance_type = "t2.micro"
          iam_instance_profile = "${aws_iam_instance_profile.poc_profile.name}" # Aqui le paso un rol que voy a crear llamado "test_role"
          #iam_instance_profile = "demo-20231013" # Aqui le paso un rol que ya esta creado
          subnet_id = module.vpc.public_subnets[0]
          associate_public_ip_address= true
          vpc_security_group_ids = [aws_security_group.ec2.id]
          key_name="demo-rds"
          tags= {
              Name = "demo-rds"
          }
        user_data = <<-EOF
        #!/bin/bash
        sudo yum update
        sudo yum -y install telnet
        EOF
      }

6- Crear el fichero 5-iam.tf, definiremos los parámetros necesarios para generar un Role, adjuntarle algunas Policys a ese rol y generar un Instance Profile que será adjuntando a la instancia, de esta forma podemos conectarnos a la instancia usando el SSM;

  • Definimos el Role.
    • resource "aws_iam_role" "poc_role" {
        name = "poc_role"
        assume_role_policy = <<EOF
      {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Action": "sts:AssumeRole",
            "Principal": {
              "Service": "ec2.amazonaws.com"
            },
            "Effect": "Allow",
            "Sid": ""
          }
        ]
      }
      EOF
        tags = {
            tag-key = "tag-value"
        }
      }
  • Adjuntamos un par de Policys al Role.
    • resource "aws_iam_policy_attachment" "attach_amazon_ssm_full_access-poc_role" {
        name       = "attach-amazon-ssm-full-access-poc_role"
        roles      = [aws_iam_role.poc_role.name]
        policy_arn = "arn:aws:iam::aws:policy/AmazonSSMFullAccess"
      }
      resource "aws_iam_policy_attachment" "attach_amazon_ec2_role_for_ssm-poc_role" {
        name       = "attach-amazon-ec2-role-for-ssm-poc_role"
        roles      = [aws_iam_role.poc_role.name]
        policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
      }
  • Creamos un Instance Profile para adjuntárselo a la instancia EC2, de esta forma garantizamos el acceso por el SSM.
    • resource "aws_iam_instance_profile" "poc_profile" {
        name = "poc_profile"
        role = "${aws_iam_role.poc_role.name}"
      }

7- Después de crear todos estos ficheros ya estamos en condiciones de ejecutar terraform;

  • terraforn init
  • terraform plan
  • terraform apply

8- Vamos a la sección de RDS en la consola de AWS y copiamos el «endpoint » y el «puerto«;

8- Nos conectamos a la instancia EC2 utilizando el Systems Manager para acceder a la instancia RDS;

  • Usando el comando telnet podemos validar que hay conectividad hacia la RDS.
  • Ya que tenemos conectividad, ahora vamos a instalar las herramientas necesarias para crear una BD;
    • dnf update
    • dnf install mariadb105
    • Nota: Estamos usando una instancia EC2 con Amazon Linux.
  • Nos conectamos a la RDS utilizando el Endpoint y el puerto copiado anteriormente;
    • mysql -h demodb.xxxxxxxxxxxx.us-west-1.rds.amazonaws.com -P 3306 -u admin -p
    • Creamos y mostramos la BD;
  • Eliminar todos los recursos generados, este es un paso muy importante porque si no vas a incurrir en más gastos y es algo que no te va a gustar mucho 🙁
    • terraform destroy

Conclusiones

A lo largo de esta entrada, hemos logrado establecer una infraestructura breve pero sólida, aprovechando las capacidades de Terraform y los recursos de AWS. Hemos creado una instancia EC2 en subredes públicas y una RDS en subredes privadas, permitiéndonos acceder a ambas de manera segura y controlada. Validamos la conexión utilizando telnet, un método rápido de verificación antes de acceder a la RDS y crear una base de datos.

Aunque este ejemplo fue relativamente sencillo, nos ha permitido profundizar en el uso de Terraform y familiarizarnos aún más con los recursos en AWS. Espero que esta entrada haya sido útil, si tienen alguna pregunta o comentario, no duden en compartirlo.

¡Nos vemos en la próxima entrada!

Nota: Todo el código lo pueden encontrar en el siguiente repositorio

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Docker

Siguiente artículo

De Jenkins a Docker Hub en minutos.