Salta el contingut

Providers i Recursos: Parlant amb el Cloud

5. Providers i Recursos: Parlant amb el Cloud

Els providers i recursos són els blocs de construcció fonamentals de qualsevol configuració de Terraform. Aprofundim en com funcionan.

Providers en Detall

Cada provider té la seva pròpia configuració i opcions. Vegem els providers més comuns en detall:

AWS Provider:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"  # Versió 5.x, compatible amb patches
    }
  }
}

provider "aws" {
  region = "eu-west-1"

  # Autenticació (millor via variables d'entorn o IAM roles)
  # access_key = "YOUR_ACCESS_KEY"  # No fer això!
  # secret_key = "YOUR_SECRET_KEY"  # No fer això!

  # Opcions avançades
  default_tags {
    tags = {
      Environment = "Production"
      ManagedBy   = "Terraform"
      Project     = "MyApp"
    }
  }

  # Assumir un rol (útil per multi-account)
  assume_role {
    role_arn = "arn:aws:iam::123456789:role/TerraformRole"
  }
}

# Proveïdor secundari per una altra regió
provider "aws" {
  alias  = "us"
  region = "us-east-1"
}

# Utilitzar el provider secundari
resource "aws_instance" "us_server" {
  provider      = aws.us
  ami           = "ami-0123456789"
  instance_type = "t3.micro"
}

Referència oficial: Documentació completa del provider d'AWS: https://registry.terraform.io/providers/hashicorp/aws/latest/docs

Azure Provider:

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }
}

provider "azurerm" {
  features {
    # Comportament al destruir recursos
    resource_group {
      prevent_deletion_if_contains_resources = false
    }

    key_vault {
      purge_soft_delete_on_destroy = true
    }
  }

  subscription_id = var.azure_subscription_id
  tenant_id       = var.azure_tenant_id
}

Referència oficial: Documentació del provider d'Azure: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs

Google Cloud Provider:

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

provider "google" {
  project = "my-project-id"
  region  = "europe-west1"
  zone    = "europe-west1-b"

  # Autenticació via service account key (millor via ADC)
  # credentials = file("path/to/service-account-key.json")
}

Referència oficial: Documentació del provider de Google Cloud: https://registry.terraform.io/providers/hashicorp/google/latest/docs

Explorant Recursos: AWS com a Exemple

Cada provider té desenes o centenars de recursos. Vegem alguns exemples d'AWS per entendre la varietat:

Compute (Servidors):

# Instància EC2 bàsica
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  # User data per inicialització
  user_data = <<-EOF
    #!/bin/bash
    apt-get update
    apt-get install -y nginx
    systemctl start nginx
  EOF

  # Volums addicionals
  ebs_block_device {
    device_name = "/dev/sdf"
    volume_size = 100
    volume_type = "gp3"
    encrypted   = true
  }

  # Metadata options
  metadata_options {
    http_tokens = "required"  # Requerir IMDSv2 per seguretat
  }

  tags = {
    Name = "Web Server"
  }
}

# Auto Scaling Group
resource "aws_autoscaling_group" "web" {
  name                = "web-asg"
  min_size            = 2
  max_size            = 10
  desired_capacity    = 3
  health_check_type   = "ELB"
  vpc_zone_identifier = [aws_subnet.private_a.id, aws_subnet.private_b.id]

  launch_template {
    id      = aws_launch_template.web.id
    version = "$Latest"
  }

  tag {
    key                 = "Name"
    value               = "web-server"
    propagate_at_launch = true
  }
}

Networking (Xarxes):

# VPC (Virtual Private Cloud)
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "Main VPC"
  }
}

# Subnets
resource "aws_subnet" "public" {
  count             = 2
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)
  availability_zone = data.aws_availability_zones.available.names[count.index]

  map_public_ip_on_launch = true

  tags = {
    Name = "Public Subnet ${count.index + 1}"
    Type = "Public"
  }
}

# Internet Gateway
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "Main IGW"
  }
}

# Route Table
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name = "Public Route Table"
  }
}

# Security Group
resource "aws_security_group" "web" {
  name        = "web-sg"
  description = "Security group for web servers"
  vpc_id      = aws_vpc.main.id

  ingress {
    description = "HTTP from anywhere"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTPS from anywhere"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    description = "All traffic out"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Web Security Group"
  }
}

Databases:

# RDS (PostgreSQL)
resource "aws_db_instance" "main" {
  identifier        = "myapp-db"
  engine            = "postgres"
  engine_version    = "15.3"
  instance_class    = "db.t3.medium"
  allocated_storage = 100
  storage_type      = "gp3"
  storage_encrypted = true

  db_name  = "myapp"
  username = "dbadmin"
  password = var.db_password  # Mai hardcodejar passwords!

  # Multi-AZ per alta disponibilitat
  multi_az = true

  # Backups
  backup_retention_period = 7
  backup_window          = "03:00-04:00"
  maintenance_window     = "mon:04:00-mon:05:00"

  # Networking
  db_subnet_group_name   = aws_db_subnet_group.main.name
  vpc_security_group_ids = [aws_security_group.database.id]
  publicly_accessible    = false

  # Protecció contra eliminació
  deletion_protection = true
  skip_final_snapshot = false
  final_snapshot_identifier = "myapp-db-final-snapshot"

  tags = {
    Name = "Main Database"
  }
}

# DynamoDB (NoSQL)
resource "aws_dynamodb_table" "sessions" {
  name           = "user-sessions"
  billing_mode   = "PAY_PER_REQUEST"
  hash_key       = "session_id"
  range_key      = "timestamp"

  attribute {
    name = "session_id"
    type = "S"
  }

  attribute {
    name = "timestamp"
    type = "N"
  }

  # TTL per expiració automàtica
  ttl {
    attribute_name = "expiration_time"
    enabled        = true
  }

  # Encriptació
  server_side_encryption {
    enabled = true
  }

  # Point-in-time recovery
  point_in_time_recovery {
    enabled = true
  }

  tags = {
    Name = "User Sessions Table"
  }
}

Load Balancing:

# Application Load Balancer
resource "aws_lb" "main" {
  name               = "web-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = aws_subnet.public[*].id

  enable_deletion_protection = true
  enable_http2              = true
  enable_cross_zone_load_balancing = true

  tags = {
    Name = "Web ALB"
  }
}

# Target Group
resource "aws_lb_target_group" "web" {
  name     = "web-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id

  health_check {
    enabled             = true
    healthy_threshold   = 2
    interval            = 30
    matcher             = "200"
    path                = "/health"
    port                = "traffic-port"
    protocol            = "HTTP"
    timeout             = 5
    unhealthy_threshold = 3
  }

  stickiness {
    type            = "lb_cookie"
    cookie_duration = 86400
    enabled         = true
  }
}

# Listener
resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.main.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS-1-2-2017-01"
  certificate_arn   = aws_acm_certificate.main.arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.web.arn
  }
}

Data Sources: Consultant el Món Existent

Els data sources són increïblement útils per fer la teva configuració dinàmica i adaptable:

# Obtenir informació del compte AWS actual
data "aws_caller_identity" "current" {}

output "account_id" {
  value = data.aws_caller_identity.current.account_id
}

# Obtenir totes les zones de disponibilitat
data "aws_availability_zones" "available" {
  state = "available"
}

# Obtenir una AMI específica
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

# Obtenir informació d'una VPC existent
data "aws_vpc" "selected" {
  tags = {
    Name = "Production VPC"
  }
}

# Obtenir subnets dins d'una VPC
data "aws_subnets" "private" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.selected.id]
  }

  tags = {
    Tier = "Private"
  }
}

# Obtenir un secret des de Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "production/database/password"
}

# Utilitzar els data sources
resource "aws_instance" "app" {
  ami               = data.aws_ami.amazon_linux.id
  instance_type     = "t3.micro"
  availability_zone = data.aws_availability_zones.available.names[0]
  subnet_id         = data.aws_subnets.private.ids[0]
}

Multi-Provider: Combinant Diferents Clouds i Serveis

Una de les grans fortaleses de Terraform és la capacitat de gestionar múltiples providers simultàniament:

# Configurar AWS
provider "aws" {
  region = "eu-west-1"
}

# Configurar Cloudflare per DNS
provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

# Configurar Datadog per monitorització
provider "datadog" {
  api_key = var.datadog_api_key
  app_key = var.datadog_app_key
}

# Crear infraestructura a AWS
resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"

  tags = {
    Name = "Web Server"
  }
}

# Configurar DNS a Cloudflare
resource "cloudflare_record" "web" {
  zone_id = var.cloudflare_zone_id
  name    = "www"
  value   = aws_instance.web.public_ip
  type    = "A"
  ttl     = 3600
}

# Configurar monitorització a Datadog
resource "datadog_monitor" "web_down" {
  name    = "Web Server Down"
  type    = "service check"
  message = "Web server is down! @team-devops"

  query = "\"http.can_connect\".over(\"instance:${aws_instance.web.id}\").by(\"*\").last(2).count_by_status()"

  thresholds = {
    critical = 2
    warning  = 1
  }
}

Aquest exemple mostra com Terraform pot orquestrar recursos en múltiples plataformes, creant una infraestructura completament integrada amb un sol flux de treball.