Introducción
Los proveedores son el puente entre Terraform y el mundo exterior. Sin providers, Terraform es solo un motor de ejecución sin acceso a ningún servicio. Con ellos, puede gestionar recursos en AWS, Azure, Google Cloud, Kubernetes, GitHub, Cloudflare, bases de datos, y cientos de servicios más. Entender cómo funcionan, cómo se configuran y cómo se versionan es esencial para cualquier profesional de Terraform.
¿Qué son los providers en Terraform?
Un provider es un plugin que Terraform descarga y usa para comunicarse con una API específica. Cada provider:
- Expone tipos de recursos (como
aws_instance,azurerm_resource_group). - Expone data sources (como
aws_ami,azurerm_image). - Implementa las llamadas a la API del servicio correspondiente.
- Gestiona la autenticación con el servicio.
Tu código HCL
|
v
Terraform Core
|
v
Provider Plugin <--> API del servicio (AWS, Azure, GCP...)
|
v
Infraestructura real¿Cómo obtiene Terraform los providers?
Terraform descarga los providers automáticamente al ejecutar terraform init. Los busca en el Terraform Registry (registry.terraform.io) o en fuentes privadas si se configuran.
# Al ejecutar init, Terraform muestra los providers que descarga
terraform init
# Salida típica:
# Initializing provider plugins...
# - Finding hashicorp/aws versions matching "~> 5.0"...
# - Installing hashicorp/aws v5.31.0...
# - Installed hashicorp/aws v5.31.0 (signed by HashiCorp)El Terraform Registry: cómo encontrar providers
El Terraform Registry (registry.terraform.io) es el repositorio público oficial de providers y módulos. Es el primer lugar donde buscar cuando necesitas interactuar con un nuevo servicio.
Estructura de un provider en el registry
Los providers se identifican por una dirección en formato:
- HOSTNAME: por defecto
registry.terraform.io(se puede omitir) - NAMESPACE: organización que publica el provider (ej:
hashicorp,aws) - TIPO: nombre del provider (ej:
aws,azurerm,google)
Ejemplos:
hashicorp/aws=registry.terraform.io/hashicorp/awshashicorp/azurerm=registry.terraform.io/hashicorp/azurermhashicorp/google=registry.terraform.io/hashicorp/google
Categorías de providers
| Categoría | Descripción | Ejemplos |
|---|---|---|
| Official | Mantenidos por HashiCorp | aws, azurerm, google, kubernetes |
| Partner | Mantenidos por empresas verificadas | datadog, newrelic, cloudflare |
| Community | Mantenidos por la comunidad | Miles de providers especializados |
Configuración básica de un provider
Sintaxis mínima
# La forma más simple: Terraform usa los valores por defecto del provider
provider "aws" {
region = "us-east-1"
}El bloque terraform con required_providers
Siempre es buena práctica declarar los providers requeridos en el bloque terraform. Esto garantiza que Terraform descargue la versión correcta y falla con un mensaje claro si no puede.
# Bloque terraform: configuración del propio Terraform
terraform {
# Versión mínima de Terraform requerida
required_version = ">= 1.0"
# Providers requeridos con sus versiones
required_providers {
# Provider de AWS
aws = {
source = "hashicorp/aws" # Dirección en el registry
version = "~> 5.0" # Restricción de versión
}
# Provider de Azure
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.0, < 4.0"
}
# Provider para archivos locales (muy útil para pruebas)
local = {
source = "hashicorp/local"
version = "~> 2.4"
}
}
}
# Configuración del provider AWS
provider "aws" {
region = "us-east-1"
# Tags que se añaden a TODOS los recursos creados por este provider
default_tags {
tags = {
Gestionado = "terraform"
Equipo = "devops"
}
}
}Operadores de versión: restricciones de versión
Las restricciones de versión permiten controlar qué versiones de un provider se pueden usar. Esto es crucial para la estabilidad: una actualización del provider podría cambiar el comportamiento.
Tabla de operadores
| Operador | Significado | Ejemplo | Versiones que acepta |
|---|---|---|---|
= |
Exactamente esta versión | = 5.0.0 |
Solo 5.0.0 |
!= |
No esta versión | != 5.1.0 |
Cualquiera excepto 5.1.0 |
> |
Mayor que | > 5.0 |
5.1, 5.2, 6.0... |
>= |
Mayor o igual que | >= 5.0 |
5.0, 5.1, 6.0... |
< |
Menor que | < 6.0 |
5.9, 5.0... pero no 6.0 |
<= |
Menor o igual que | <= 5.9 |
5.9 y anteriores |
~> |
Solo patch/minor (más usado) | ~> 5.31 |
5.31.x (no 5.32) |
~> |
Solo minor (versión mayor fija) | ~> 5.0 |
5.x.y (no 6.x) |
Casos de uso de cada operador
terraform {
required_providers {
# ~> 5.31: permite 5.31.0, 5.31.1, 5.31.2 pero NO 5.32.0
# Bueno para bloquear a una versión minor específica en producción
aws = {
source = "hashicorp/aws"
version = "~> 5.31"
}
# ~> 5.0: permite 5.0, 5.1, 5.2... pero NO 6.0
# Bueno para permitir actualizaciones minor pero no major
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
# >= 2.0, < 3.0: equivalente a ~> 2.0 pero más explícito
google = {
source = "hashicorp/google"
version = ">= 5.0, < 6.0"
}
# = 2.4.1: versión exacta (máximo control, mínima flexibilidad)
# Útil cuando una versión específica tiene una corrección crítica
local = {
source = "hashicorp/local"
version = "= 2.4.1"
}
}
}El archivo .terraform.lock.hcl
Cuando ejecutas terraform init, Terraform crea un archivo de lock que registra las versiones exactas descargadas:
# Archivo: .terraform.lock.hcl (generado automáticamente, NO editarlo)
# Asegura que todos los miembros del equipo usen las mismas versiones
provider "registry.terraform.io/hashicorp/aws" {
version = "5.31.0" # Versión exacta instalada
constraints = "~> 5.0" # Restricción que especificamos nosotros
hashes = [
"h1:abc123...", # Hash de verificación de integridad
]
}# Actualizar las versiones respetando las restricciones
terraform init -upgrade
# Ver qué providers están instalados
terraform providersProviders más populares
| Provider | Fuente | Uso principal |
|---|---|---|
| AWS | hashicorp/aws |
Amazon Web Services (EC2, S3, RDS, Lambda...) |
| Azure | hashicorp/azurerm |
Microsoft Azure (VMs, Storage, AKS...) |
| Google Cloud | hashicorp/google |
Google Cloud Platform (GCE, GCS, GKE...) |
| Kubernetes | hashicorp/kubernetes |
Gestión de recursos de Kubernetes |
| Helm | hashicorp/helm |
Despliegue de charts de Helm en Kubernetes |
| GitHub | integrations/github |
Repositorios, teams, webhooks de GitHub |
| Cloudflare | cloudflare/cloudflare |
DNS, CDN, certificados de Cloudflare |
| Datadog | datadog/datadog |
Monitoreo y alertas con Datadog |
| Vault | hashicorp/vault |
Gestión de secretos con HashiCorp Vault |
| Local | hashicorp/local |
Archivos locales (ideal para pruebas) |
| Random | hashicorp/random |
Generación de valores aleatorios únicos |
| TLS | hashicorp/tls |
Generación de certificados y claves TLS |
| Null | hashicorp/null |
Recursos vacíos para triggers y provisioners |
Configurar múltiples instancias del mismo provider (alias)
A veces necesitas usar el mismo provider pero con diferentes configuraciones: por ejemplo, desplegar recursos en dos regiones de AWS distintas o en dos cuentas diferentes. Para esto usamos alias.
Alias de region
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Provider principal: región us-east-1 (Virginia)
# Al no tener alias, este es el provider "por defecto"
provider "aws" {
region = "us-east-1"
}
# Provider secundario: región eu-west-1 (Irlanda)
# Le asignamos un alias para referenciarlo explícitamente
provider "aws" {
alias = "europa" # Nombre del alias
region = "eu-west-1"
}
# Provider terciario: región ap-southeast-1 (Singapur)
provider "aws" {
alias = "asia"
region = "ap-southeast-1"
}
# --- Uso de los providers con alias ---
# Sin especificar provider: usa el provider por defecto (us-east-1)
resource "aws_s3_bucket" "bucket_usa" {
bucket = "mi-bucket-usa"
}
# Con provider = aws.europa: usa el provider con alias "europa"
resource "aws_s3_bucket" "bucket_europa" {
bucket = "mi-bucket-europa"
provider = aws.europa # Usamos el alias
}
# Con provider = aws.asia
resource "aws_s3_bucket" "bucket_asia" {
bucket = "mi-bucket-asia"
provider = aws.asia
}Alias de cuenta (multi-account)
# Provider para la cuenta de producción
provider "aws" {
region = "us-east-1"
# Asumimos un rol en la cuenta de producción
assume_role {
role_arn = "arn:aws:iam::111122223333:role/TerraformRole"
}
}
# Provider para la cuenta de desarrollo (alias)
provider "aws" {
alias = "desarrollo"
region = "us-east-1"
assume_role {
role_arn = "arn:aws:iam::444455556666:role/TerraformRole"
}
}
# VPC en la cuenta de producción (provider por defecto)
resource "aws_vpc" "produccion" {
cidr_block = "10.0.0.0/16"
tags = { Name = "vpc-produccion" }
}
# VPC en la cuenta de desarrollo (alias)
resource "aws_vpc" "desarrollo" {
provider = aws.desarrollo # Especificamos el alias
cidr_block = "10.1.0.0/16"
tags = { Name = "vpc-desarrollo" }
}Provider heredado en módulos (inheritance)
Cuando usas módulos, los providers del módulo raíz se heredan automáticamente por los módulos hijos. Sin embargo, cuando usas alias, debes ser explícito.
Herencia automática (comportamiento por defecto)
# =============================================
# Archivo: main.tf (módulo raíz)
# =============================================
provider "aws" {
region = "us-east-1"
}
# El módulo hereda automáticamente el provider "aws" de arriba
module "red" {
source = "./modules/red"
# No necesitamos especificar el provider: se hereda automáticamente
}# =============================================
# Archivo: modules/red/main.tf (módulo hijo)
# =============================================
# El módulo usa "aws" sin configurarlo: hereda del módulo raíz
resource "aws_vpc" "principal" {
cidr_block = "10.0.0.0/16"
}Pasar providers con alias a módulos
Cuando necesitas que un módulo use un provider con alias, debes pasarlo explícitamente:
# =============================================
# Archivo: main.tf (módulo raíz)
# =============================================
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "europa"
region = "eu-west-1"
}
# Pasamos los providers explícitamente al módulo
module "infraestructura_europa" {
source = "./modules/infraestructura"
# Mapeamos qué provider de este módulo raíz
# corresponde a qué alias dentro del módulo hijo
providers = {
aws = aws.europa # En el módulo hijo, "aws" será nuestro "aws.europa"
}
}# =============================================
# Archivo: modules/infraestructura/main.tf
# =============================================
# Declaramos qué providers necesita este módulo
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
# Usamos "aws" normalmente; el módulo raíz decide qué region/cuenta usar
resource "aws_vpc" "principal" {
cidr_block = "10.0.0.0/16"
}Autenticación con providers
Cada provider tiene sus propios mecanismos de autenticación. Aquí los más comunes:
Autenticación con AWS
# Método 1: Variables de entorno (RECOMENDADO para CI/CD)
# export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
# export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# export AWS_DEFAULT_REGION="us-east-1"
provider "aws" {
# No necesita credenciales en el código: las lee de las variables de entorno
region = "us-east-1"
}
# Método 2: Perfil de AWS CLI (para desarrollo local)
# Configurado en ~/.aws/credentials y ~/.aws/config
provider "aws" {
region = "us-east-1"
profile = "mi-perfil-produccion" # Nombre del perfil en ~/.aws/credentials
}
# Método 3: Variables directamente en el código (NO RECOMENDADO)
# Nunca subas credenciales al repositorio de código
provider "aws" {
region = "us-east-1"
access_key = "AKIAIOSFODNN7EXAMPLE" # PELIGROSO: no hagas esto
secret_key = "wJalrXUtnFEMI/K7MDENG/..." # PELIGROSO: no hagas esto
}
# Método 4: Asumir un rol IAM (recomendado para multi-cuenta y CI/CD)
provider "aws" {
region = "us-east-1"
assume_role {
# ARN del rol que Terraform asumirá
role_arn = "arn:aws:iam::123456789012:role/TerraformDeploymentRole"
# Identificador de sesión (aparece en los logs de CloudTrail)
session_name = "terraform-deployment"
}
}Autenticación con Azure
# Método 1: Azure CLI (para desarrollo local)
# Ejecutar previamente: az login
provider "azurerm" {
features {} # Requerido aunque esté vacío en azurerm
# Terraform usa automáticamente las credenciales de az login
}
# Método 2: Service Principal con client_secret (para CI/CD)
# Variables de entorno:
# ARM_CLIENT_ID = "client-id-del-service-principal"
# ARM_CLIENT_SECRET = "secreto-del-service-principal"
# ARM_SUBSCRIPTION_ID = "id-de-la-suscripcion"
# ARM_TENANT_ID = "id-del-tenant"
provider "azurerm" {
features {}
# Lee automáticamente ARM_* de las variables de entorno
}
# Método 3: Managed Identity (para recursos en Azure como VMs o AKS)
provider "azurerm" {
features {}
use_msi = true # Usar Managed Service Identity
}Autenticación con Google Cloud
# Método 1: Application Default Credentials
# Ejecutar previamente: gcloud auth application-default login
provider "google" {
project = "mi-proyecto-gcp"
region = "us-central1"
# Lee automáticamente las credenciales de gcloud
}
# Método 2: Archivo de Service Account
provider "google" {
project = "mi-proyecto-gcp"
region = "us-central1"
credentials = file("~/.gcp/service-account.json") # Ruta al archivo JSON
}
# Método 3: Variable de entorno (recomendado para CI/CD)
# export GOOGLE_APPLICATION_CREDENTIALS="/ruta/al/service-account.json"
provider "google" {
project = "mi-proyecto-gcp"
region = "us-central1"
# Lee GOOGLE_APPLICATION_CREDENTIALS de las variables de entorno
}Ejemplos de código completos
Ejemplo 1: Provider AWS con el provider "hashicorp/local"
El provider local permite crear archivos en el sistema de archivos local. Es muy útil para generar archivos de configuración o para pruebas sin necesitar acceso a la nube.
terraform {
required_providers {
# Provider de AWS para recursos en la nube
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
# Provider local para crear archivos en el sistema de archivos
local = {
source = "hashicorp/local"
version = "~> 2.4"
}
# Provider random para generar valores únicos
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
}
# Configuración del provider AWS
provider "aws" {
region = "us-east-1"
# default_tags aplica estas etiquetas a TODOS los recursos AWS
default_tags {
tags = {
Gestionado = "terraform"
Proyecto = "ejemplo-providers"
}
}
}
# El provider "local" y "random" no necesitan configuración adicional
# --- Usando el provider random ---
# Generamos un sufijo único para el nombre del bucket S3
resource "random_string" "sufijo" {
length = 8 # 8 caracteres de longitud
special = false # Sin caracteres especiales
upper = false # Solo minúsculas (los nombres de S3 deben ser en minúsculas)
}
# --- Usando el provider AWS ---
resource "aws_s3_bucket" "mi_bucket" {
# Usamos el sufijo aleatorio para garantizar un nombre único globalmente
bucket = "mi-bucket-ejemplo-${random_string.sufijo.result}"
}
# --- Usando el provider local ---
# Creamos un archivo local con la información del bucket
resource "local_file" "info_bucket" {
# El contenido del archivo (en formato JSON para ser parseable)
content = jsonencode({
nombre_bucket = aws_s3_bucket.mi_bucket.id
arn_bucket = aws_s3_bucket.mi_bucket.arn
region = "us-east-1"
sufijo = random_string.sufijo.result
})
# Ruta donde se creará el archivo
filename = "${path.module}/bucket-info.json"
# path.module es la ruta del directorio donde está este archivo .tf
}
output "nombre_bucket" {
value = aws_s3_bucket.mi_bucket.id
}
output "ruta_archivo_info" {
value = local_file.info_bucket.filename
}Ejemplo 2: Configuración completa multi-provider
# =============================================
# Configuración para desplegar en AWS y Azure
# simultáneamente desde el mismo código Terraform
# =============================================
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
}
# =============================================
# PROVIDER: AWS (us-east-1 - región principal)
# =============================================
provider "aws" {
region = var.region_aws
}
# Provider AWS secundario para failover en otra región
provider "aws" {
alias = "dr" # DR = Disaster Recovery
region = "us-west-2" # Costa oeste como región de respaldo
}
# =============================================
# PROVIDER: Azure
# =============================================
provider "azurerm" {
features {
# Configuración específica de azurerm para key vaults
key_vault {
purge_soft_delete_on_destroy = false
recover_soft_deleted_key_vaults = true
}
}
subscription_id = var.subscription_azure
}
# =============================================
# VARIABLES
# =============================================
variable "region_aws" {
type = string
default = "us-east-1"
}
variable "subscription_azure" {
type = string
description = "ID de la suscripción de Azure"
default = "" # Se pasa como variable de entorno ARM_SUBSCRIPTION_ID
}
variable "nombre_proyecto" {
type = string
default = "proyecto-multicloud"
}
# =============================================
# RECURSOS EN AWS
# =============================================
# Grupo de seguridad en AWS
resource "aws_security_group" "web" {
# Sin provider especificado: usa el provider por defecto (us-east-1)
name = "${var.nombre_proyecto}-sg-web"
description = "Security group para servidores web"
ingress {
from_port = 443
to_port = 443
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"]
}
}
# Bucket S3 en la región de DR (con alias)
resource "aws_s3_bucket" "backups_dr" {
provider = aws.dr # Usamos el provider de la región de DR
bucket = "${var.nombre_proyecto}-backups-dr-${random_string.id.result}"
}
resource "random_string" "id" {
length = 6
special = false
upper = false
}
# =============================================
# RECURSOS EN AZURE
# =============================================
# Grupo de recursos en Azure (equivalente a un namespace)
resource "azurerm_resource_group" "principal" {
name = "rg-${var.nombre_proyecto}"
location = "East US" # Equivalente a us-east-1 en AWS
tags = {
Proyecto = var.nombre_proyecto
Gestionado = "terraform"
}
}
# Cuenta de storage en Azure
resource "azurerm_storage_account" "datos" {
name = replace("${var.nombre_proyecto}data${random_string.id.result}", "-", "")
resource_group_name = azurerm_resource_group.principal.name
location = azurerm_resource_group.principal.location
account_tier = "Standard"
account_replication_type = "GRS" # Geo-Redundant Storage
tags = azurerm_resource_group.principal.tags
}
# =============================================
# OUTPUTS
# =============================================
output "sg_aws_id" {
description = "ID del security group en AWS"
value = aws_security_group.web.id
}
output "bucket_dr_aws" {
description = "Nombre del bucket de DR en AWS us-west-2"
value = aws_s3_bucket.backups_dr.id
}
output "rg_azure_nombre" {
description = "Nombre del resource group en Azure"
value = azurerm_resource_group.principal.name
}
output "storage_azure_nombre" {
description = "Nombre de la cuenta de storage en Azure"
value = azurerm_storage_account.datos.name
}Ejercicio práctico
Enunciado
Crea una configuración Terraform que:
- Configure tres providers:
aws(región us-east-1),awscon aliassecundario(región us-west-2), y el providerlocal. - Declare las versiones en
required_providers. - Use el provider
randompara generar un ID único de 6 caracteres. - Cree un bucket S3 en us-east-1 y otro en us-west-2 (réplica) usando los dos providers de AWS.
- Use el provider
localpara crear un archivoinfraestructura.jsoncon los nombres y ARNs de los buckets. - Defina outputs para todos los recursos creados.
Solución detallada
# =============================================
# Archivo: terraform.tf
# Configuración de Terraform y providers
# =============================================
terraform {
# Versión mínima de Terraform requerida
required_version = ">= 1.5.0"
required_providers {
# Provider de AWS: para los buckets S3
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Cualquier versión 5.x
}
# Provider local: para crear el archivo JSON
local = {
source = "hashicorp/local"
version = "~> 2.4" # Cualquier versión 2.x >= 2.4
}
# Provider random: para generar el ID único
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
}
# =============================================
# Archivo: providers.tf
# Configuración de las instancias de providers
# =============================================
# Provider AWS principal: región us-east-1 (Virginia del Norte)
# Este es el provider "por defecto" para todos los recursos sin provider=
provider "aws" {
region = "us-east-1"
# Etiquetas que se añaden automáticamente a todos los recursos
default_tags {
tags = {
Gestionado = "terraform"
Region = "us-east-1"
}
}
}
# Provider AWS secundario: región us-west-2 (Oregón)
# Este provider se usa para la réplica del bucket
provider "aws" {
alias = "secundario" # Nombre del alias para referenciar este provider
region = "us-west-2"
default_tags {
tags = {
Gestionado = "terraform"
Region = "us-west-2"
Rol = "replica"
}
}
}
# El provider "local" y "random" no necesitan bloque provider
# =============================================
# Archivo: main.tf
# Recursos principales
# =============================================
# Generamos un ID único para los nombres de los buckets
# Los nombres de S3 son globalmente únicos, necesitamos evitar colisiones
resource "random_string" "id_unico" {
length = 6 # 6 caracteres es suficiente para unicidad práctica
special = false # Sin caracteres especiales (no permitidos en nombres S3)
upper = false # Solo minúsculas (S3 es sensible a mayúsculas en nombres)
numeric = true # Incluir números para más combinaciones posibles
}
# Valores locales para los nombres de los buckets
locals {
# Nombre base compartido por ambos buckets
nombre_base = "mi-empresa-datos-${random_string.id_unico.result}"
# Nombre del bucket primario (sin sufijo adicional)
nombre_bucket_primario = "${local.nombre_base}-este"
# Nombre del bucket de réplica (con sufijo "-oeste" para diferenciarlo)
nombre_bucket_secundario = "${local.nombre_base}-oeste"
}
# --- Bucket S3 PRIMARIO en us-east-1 ---
resource "aws_s3_bucket" "primario" {
# Sin provider= explícito: usa el provider por defecto (us-east-1)
bucket = local.nombre_bucket_primario
tags = {
Nombre = "Bucket Primario"
Rol = "primario"
}
}
# Habilitamos el versionado en el bucket primario
resource "aws_s3_bucket_versioning" "primario" {
bucket = aws_s3_bucket.primario.id
versioning_configuration {
status = "Enabled" # Necesario para que funcione la replicación
}
}
# --- Bucket S3 SECUNDARIO en us-west-2 (réplica) ---
resource "aws_s3_bucket" "secundario" {
# provider= explícito: usamos el alias "secundario" (us-west-2)
provider = aws.secundario
bucket = local.nombre_bucket_secundario
tags = {
Nombre = "Bucket Réplica"
Rol = "replica"
Replica = local.nombre_bucket_primario # Referencia al bucket origen
}
}
# Habilitamos versionado también en el bucket de réplica (requerido por AWS)
resource "aws_s3_bucket_versioning" "secundario" {
provider = aws.secundario
bucket = aws_s3_bucket.secundario.id
versioning_configuration {
status = "Enabled"
}
}
# --- Archivo local con la información de la infraestructura ---
resource "local_file" "infraestructura" {
# jsonencode convierte un objeto HCL a formato JSON
content = jsonencode({
# Metadata de la infraestructura
metadata = {
generado_por = "Terraform"
fecha = "Generado durante terraform apply"
id_unico = random_string.id_unico.result
}
# Información del bucket primario
bucket_primario = {
nombre = aws_s3_bucket.primario.id
arn = aws_s3_bucket.primario.arn
region = aws_s3_bucket.primario.region
}
# Información del bucket secundario (réplica)
bucket_secundario = {
nombre = aws_s3_bucket.secundario.id
arn = aws_s3_bucket.secundario.arn
region = aws_s3_bucket.secundario.region
}
})
# Ruta donde se creará el archivo
# path.module = directorio donde está este archivo main.tf
filename = "${path.module}/infraestructura.json"
# Permisos del archivo (octal): 0644 = lectura/escritura para dueño, lectura para otros
file_permission = "0644"
}
# =============================================
# Archivo: outputs.tf
# Valores exportados
# =============================================
output "id_unico" {
description = "ID único generado para esta infraestructura"
value = random_string.id_unico.result
}
output "bucket_primario_nombre" {
description = "Nombre del bucket S3 primario (us-east-1)"
value = aws_s3_bucket.primario.id
}
output "bucket_primario_arn" {
description = "ARN del bucket S3 primario"
value = aws_s3_bucket.primario.arn
}
output "bucket_secundario_nombre" {
description = "Nombre del bucket S3 secundario - réplica (us-west-2)"
value = aws_s3_bucket.secundario.id
}
output "bucket_secundario_arn" {
description = "ARN del bucket S3 secundario"
value = aws_s3_bucket.secundario.arn
}
output "archivo_json_ruta" {
description = "Ruta del archivo JSON con la información de la infraestructura"
value = local_file.infraestructura.filename
}
output "resumen_completo" {
description = "Resumen de toda la infraestructura creada"
value = {
id_unico = random_string.id_unico.result
us_east_1 = aws_s3_bucket.primario.id
us_west_2 = aws_s3_bucket.secundario.id
archivo = local_file.infraestructura.filename
}
}Para ejecutar el ejercicio:
# Paso 1: Inicializar (descarga los providers)
terraform init
# Verás:
# - Installing hashicorp/aws v5.x.x
# - Installing hashicorp/local v2.x.x
# - Installing hashicorp/random v3.x.x
# Paso 2: Ver el plan
terraform plan
# El plan mostrará:
# + random_string.id_unico (generar ID único)
# + aws_s3_bucket.primario (crear bucket us-east-1)
# + aws_s3_bucket.secundario (crear bucket us-west-2)
# + aws_s3_bucket_versioning.primario (habilitar versionado primario)
# + aws_s3_bucket_versioning.secundario (habilitar versionado secundario)
# + local_file.infraestructura (crear archivo JSON)
# Paso 3: Aplicar
terraform apply
# Paso 4: Ver los outputs
terraform output
terraform output resumen_completo
# Paso 5: Ver el archivo creado
cat infraestructura.json
# Paso 6: Ver los providers instalados
terraform providers
# Paso 7 (cleanup): Destruir si solo fue una prueba
terraform destroyErrores comunes con providers
Error 1: Olvidar ejecutar terraform init después de añadir un provider
# ERROR: Provider no encontrado
# Error: Required plugins are not installed
# SOLUCIÓN: ejecutar init después de cualquier cambio en required_providers
terraform initError 2: Conflicto de versiones
# INCORRECTO: restricciones incompatibles
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0, <= 4.0" # Error: no puede ser >= 5.0 Y <= 4.0
}
}Error 3: Usar un recurso sin el provider configurado
# INCORRECTO: azurerm_resource_group requiere el provider "azurerm"
# pero no está configurado ni en required_providers
resource "azurerm_resource_group" "mi_rg" {
name = "mi-grupo"
location = "East US"
# Error: Provider "azurerm" no configurado
}
# CORRECTO: declarar el provider primero
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}Error 4: Credenciales de AWS no configuradas
# ERROR al ejecutar plan/apply sin credenciales:
# Error: No valid credential sources found
# Please see https://www.terraform.io/docs/...
# SOLUCIÓN: configurar credenciales de una de estas formas:
# Opción A: Variables de entorno
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_DEFAULT_REGION="us-east-1"
# Opción B: AWS CLI
aws configure
# Opción C: AWS SSO
aws sso login --profile mi-perfilError 5: Dos providers sin alias cuando se necesitan dos
# INCORRECTO: dos bloques provider sin alias -> conflicto
provider "aws" {
region = "us-east-1"
}
provider "aws" {
region = "us-west-2"
# Error: solo puede haber un provider sin alias por tipo
}
# CORRECTO: el segundo debe tener alias
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "oeste"
region = "us-west-2"
}Resumen del Módulo 2
Has completado el Módulo 2 del curso. Esto es lo que has aprendido a lo largo de los cuatro temas:
Lo aprendido en este módulo
02-01: Sintaxis HCL
- Bloques, argumentos y expresiones son los componentes básicos de HCL.
- HCL soporta tipos primitivos (string, number, bool) y de colección (list, set, map, object).
- La interpolación
${}, expresiones condicionales y funciones built-in hacen el código dinámico.
02-02: Variables y Outputs
- Las variables de entrada parametrizan las configuraciones y las hacen reutilizables.
terraform.tfvarsorganiza los valores por entorno;TF_VAR_*para CI/CD.- Los outputs exponen información de los recursos y conectan módulos entre sí.
02-03: Bloques Resource y Data
resourcecrea infraestructura;datalee información existente.countyfor_eachmultiplican recursos;lifecyclecontrola su comportamiento.- Las dependencias implícitas (referencias) son preferibles a
depends_on.
02-04: Providers (este tema)
- Los providers son plugins que conectan Terraform con las APIs de los servicios.
- El bloque
required_providersgarantiza versiones correctas y reproducibles. - El operador
~>es el más usado para versioning: permite actualizaciones patch. - Los alias permiten usar múltiples configuraciones del mismo provider.
- La autenticación debe hacerse mediante variables de entorno o roles IAM, nunca hardcodeando credenciales.
Próximos pasos
En el Módulo 3 estudiaremos la Gestión del Estado de Terraform: cómo Terraform rastrea los recursos que gestiona, dónde almacena esa información, cómo trabajar en equipo con estado remoto y cómo evitar conflictos con el state locking. El estado es probablemente el aspecto más crítico de Terraform en entornos de producción, y entenderlo bien te diferenciará como profesional.
Curso de Terraform
Módulo 1: Introducción a Terraform
- ¿Qué es Terraform?
- Instalando Terraform
- Conceptos Básicos de Terraform
- Primera Configuración de Terraform
Módulo 2: Lenguaje de Configuración de Terraform
Módulo 3: Gestión del Estado
Módulo 4: Módulos de Terraform
Módulo 5: Aprovisionamiento de Recursos
- Conceptos Básicos de Aprovisionamiento
- Aprovisionamiento de Recursos AWS
- Aprovisionamiento de Recursos Azure
- Aprovisionamiento de Recursos GCP
Módulo 6: Funcionalidades Avanzadas de Terraform
Módulo 7: Mejores Prácticas de Terraform
- Organización del Código
- Control de Versiones
- Pruebas del Código de Terraform
- Mejores Prácticas de Seguridad
Módulo 8: Terraform en CI/CD
- Integración de Terraform con CI/CD
- Automatización de Terraform con Jenkins
- Uso de Terraform con GitHub Actions
- Terraform Cloud y Enterprise
