Con AWS, Terraform y GitHub Actions
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.
Un bucket de S3 público que será creado con Terraform y desplegado automáticamente desde GitHub.
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".
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.
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).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.
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
Este fue tu primer paso. Ahora puedes:
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!