Tu primer proyecto DevOps

Con AWS, Terraform y GitHub Actions

Tu primer proyecto DevOps

Cuando hablamos de DevOps, no hablamos solo de herramientas, sino de una filosofía que busca mejorar la colaboración entre desarrollo y operaciones. Dicho esto, muchas veces nos preguntan: "¿Por dónde empiezo?". Esta guía responde a esa pregunta desde el lado práctico.

Aquí no vas a encontrar "DevOps en 5 pasos", pero sí una forma sencilla de comenzar a trabajar con herramientas que pueden formar parte de una cultura DevOps: AWS como proveedor de infraestructura, Terraform como lenguaje declarativo para crear recursos, y GitHub Actions como herramienta de automatización.

Repetimos: usar estas herramientas no te hace automáticamente DevOps. Pero saber usarlas te prepara para contribuir a equipos que aplican prácticas DevOps reales.

¿Qué vamos a construir?

Un bucket de S3 público que será creado con Terraform y desplegado automáticamente desde GitHub.

Requisitos previos

  • Cuenta de AWS. Comienza con una cuenta gratuita.
  • Terraform. Infraestructura como código.
  • Repositorio en GitHub. Un repo donde agregarás el código y los workflows.

Organización del código

Lo que bien empieza, bien continúa. Es importante establecer una organización de directorios que permita que tu código crezca, agregar nuevos ambientes sin complicaciones y estandarizar los distintos recursos.

dev/
├── us-east-1/
│   └── s3/
│       ├── terraform.tf
│       ├── main.tf
│       └── outputs.tf
stage/
├── us-east-1/
│   └── s3/
prod/
├── us-east-1/
│   └── s3/
modules/
└── s3/
    ├── main.tf
    ├── variables.tf
    └── outputs.tf

Con este diseño de directorios tenemos definidos tres ambientes: dev, stage y prod. También hemos definido un módulo de Terraform llamado "s3".

¿Por qué usar módulos? Porque te permiten reutilizar lógica, mantener consistencia entre entornos, reducir errores y versionar componentes reutilizables. Piensa en los módulos como piezas de infraestructura que puedes ensamblar según tus necesidades.

El primer directorio dentro de cada ambiente define la región de AWS donde desplegaremos nuestros recursos; el siguiente nivel define el módulo o código que utilizaremos para crearlos.

¿Qué hace cada archivo?

  • terraform.tf: aquí se define el backend para guardar el estado remotamente, así como los proveedores (providers).
  • main.tf: la lógica principal, donde instancias los módulos y recursos.
  • variables.tf: define las entradas (como el nombre del bucket).
  • outputs.tf: define las salidas (por ejemplo, la URL del bucket).

Nuestro primer código Terraform

Antes de comenzar a crear recursos, tenemos que definir dónde alojar nuestro backend de Terraform. El backend es un archivo utilizado por Terraform para llevar registro de todos los recursos creados y sus configuraciones. Lo podemos definir dentro del archivo terraform.tf:

Por defecto, Terraform utiliza un backend local, lo que significa que se crea en la máquina que ejecuta el terraform apply. Esto puede funcionar inicialmente, pero al ejecutar nuestro código en GitHub Actions, ese archivo se perderá y Terraform no podrá hacer seguimiento. Por eso, debemos usar un backend remoto, como un bucket S3 o Terraform Cloud. En este ejemplo, usaremos un bucket S3 ya creado.

terraform {
  backend "s3" {
    bucket = "myorg-state-buckets"
    key    = "s3.tfstate"
    region = "us-east-1"
  }
}

Ahora debemos definir nuestros "providers". Los providers son responsables de hacer las llamadas a las APIs para la creación o modificación de recursos. Para AWS lo definiremos así:

terraform {
  backend "s3" {
    ...
  }

  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "5.99.1"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

Ahora estamos listos para comenzar a crear nuestro bucket. Ve al archivo modules/s3/main.tf:

resource "aws_s3_bucket" "example" {
  bucket = var.buket_name
}

resource "aws_s3_bucket_ownership_controls" "example" {
  bucket = aws_s3_bucket.example.id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

resource "aws_s3_bucket_public_access_block" "example" {
  bucket = aws_s3_bucket.example.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

resource "aws_s3_bucket_acl" "example" {
  depends_on = [
    aws_s3_bucket_ownership_controls.example,
    aws_s3_bucket_public_access_block.example,
  ]

  bucket = aws_s3_bucket.example.id
  acl    = "public-read"
}

En nuestro código estamos utilizando una variable var.bucket_name, que debemos definir en modules/s3/variables.tf:

variable "bucket_name" {
  type = string
  description = "Name of the bucket to create"
}

Además, podemos agregar un output a nuestro código. Con esto, podemos obtener propiedades del recurso creado, como la URL del bucket. Esto lo definimos en modules/s3/outputs.tf:

output "bucket_domain_name" {
  value = aws_s3_bucket.example.bucket_domain_name
}

Ahora nuestro módulo está listo y podemos usarlo para crear buckets de forma estandarizada en todos nuestros ambientes. Vamos a desplegarlo en dev utilizando el archivo dev/s3/main.tf:

module "my_awesome_bucket" {
  source = "../../modules/s3"

  bucket_name = "dev-awesome-bucket"
}

Este código es suficiente para desplegar un bucket con acceso público en el ambiente de desarrollo.

Automatizando con GitHub Actions

Lo primero es configurar la autenticación desde GitHub Actions hacia nuestra cuenta de AWS.

Ve a tu repositorio → Settings → Secrets and variables → Actions y crea dos secretos:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

Agrega este archivo en .github/workflows/deploy.yml:

name: Deploy S3 Bucket
on:
  push:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.6.6

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4.1.0
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Terraform Init
        run: terraform init

      - name: Terraform Apply
        run: terraform apply -auto-approve

¿Y ahora qué?

Este fue tu primer paso. Ahora puedes:

  • Configurar DNS para apuntar a tu bucket (Route 53)
  • Crear una distribución de CloudFront para configurar caché y tiempos de respuesta
  • Diseñar tu propia página web y alojarla en el bucket

Aprender DevOps no se trata de memorizar comandos, sino de entender cómo conectar herramientas para lograr flujos confiables. ¡Y hoy diste un gran primer paso!