Infrastructure DigitalOcean avec Terraform

VérifiéSûr

Compétence pour provisionner des ressources DigitalOcean (Droplets, VPC, Firewalls) avec OpenTofu/Terraform. Inclut la configuration du provider et des modèles courants.

Spar Skills Guide Bot
DevOpsIntermédiaire
63023/07/2026
Claude CodeCursorCodex
#digitalocean#terraform#opentofu#infrastructure-as-code#cloud-provisioning

Recommandé pour

Synthèse automatique

Guide la rédaction de configurations OpenTofu/Terraform pour provisionner des ressources DigitalOcean telles que Droplets, VPC, bases de données managées et pare-feux.

Points forts

  • Fournit des exemples HCL concrets pour VPC, Droplets, cloud-init, IP réservées et pare-feux
  • Inclut un tableau des tailles de Droplets courantes avec leurs cas d'usage
  • Montre des pratiques de sécurité utiles comme la surveillance, la restriction SSH et la désactivation de la connexion root
  • Les extraits sont directement adaptables à un projet existant

Limites

  • Ne couvre pas les produits DigitalOcean avancés comme Kubernetes, Spaces ou les load balancers
  • Ne donne pas de recommandations sur le backend d'état, les modules ou l'organisation du code Terraform
  • Le fichier fourni est tronqué dans la section sur la liste blanche d'IP dynamique
Quand l'utiliser

À utiliser lorsqu'il faut écrire ou étendre une infrastructure DigitalOcean en OpenTofu/Terraform avec des ressources de base.

Quand l'éviter

À éviter si vous ciblez un autre fournisseur cloud ou si vous avez besoin de ressources DigitalOcean non décrites comme DOKS, Spaces ou les load balancers.

Analyse de sécurité

Sûr
Score qualité90/100

The skill is instructional Terraform/OpenTofu content for DigitalOcean resources; it does not run destructive commands, exfiltrate secrets, or obfuscate payloads. The declared Bash/Write/Edit tools are powerful but used for legitimate infrastructure authoring, and the examples follow normal cloud provisioning patterns with only minor hardening considerations.

Points d'attention
  • •Cloud-init example grants the deploy user passwordless sudo and docker group membership, which is a deliberate but elevated privilege choice.
  • •Firewall example allows all outbound traffic and public HTTP/HTTPS; SSH is parameterized but no default restriction is shown.
  • •Provider token is read from DIGITALOCEAN_TOKEN environment variable, avoiding hardcoded secrets.
Comment cette analyse est faite

Exemples

Droplet with cloud-init
Write an OpenTofu configuration for a DigitalOcean Droplet in the nyc3 region using an s-2vcpu-4gb size, attached to a VPC, with cloud-init that installs Docker and creates a deploy user.
Restricted web firewall
Add a DigitalOcean firewall to my existing Droplet that allows SSH only from 203.0.113.0/24 and allows HTTP and HTTPS from anywhere.
Reserved IP assignment
Create a DigitalOcean reserved IP and assign it to my app Droplet, exposing the IP as a Terraform output.

name: digitalocean-coder description: This skill guides writing DigitalOcean infrastructure with OpenTofu/Terraform. Use when provisioning Droplets, VPCs, Managed Databases, Firewalls, or other DO resources. allowed-tools: Read, Write, Edit, Grep, Glob, Bash

DigitalOcean Coder

Overview

DigitalOcean provides simple, developer-friendly cloud infrastructure. This skill covers OpenTofu/Terraform patterns for DO resources.

Provider Setup

terraform {
  required_providers {
    digitalocean = {
      source  = "digitalocean/digitalocean"
      version = "~> 2.0"
    }
  }
}

provider "digitalocean" {
  # Token from environment: DIGITALOCEAN_TOKEN
}

VPC (Virtual Private Cloud)

resource "digitalocean_vpc" "main" {
  name     = "${var.project}-${var.environment}-vpc"
  region   = var.region
  ip_range = "10.10.0.0/16"

  description = "VPC for ${var.project} ${var.environment}"
}

Droplets (Compute)

Basic Droplet

resource "digitalocean_droplet" "app" {
  name     = "${var.project}-${var.environment}-app"
  region   = var.region
  size     = var.droplet_size  # s-1vcpu-1gb, s-2vcpu-4gb, etc.
  image    = "ubuntu-22-04-x64"
  vpc_uuid = digitalocean_vpc.main.id

  ssh_keys   = var.ssh_key_ids
  monitoring = true
  ipv6       = false

  tags = [var.project, var.environment]
}

Droplet with Cloud-Init

resource "digitalocean_droplet" "app" {
  name     = "${var.project}-app"
  region   = var.region
  size     = "s-1vcpu-2gb"
  image    = "ubuntu-22-04-x64"
  vpc_uuid = digitalocean_vpc.main.id

  ssh_keys   = var.ssh_key_ids
  monitoring = true

  user_data = <<-EOT
    #cloud-config
    package_update: true
    packages:
      - docker.io
      - docker-compose-plugin
    users:
      - name: deploy
        groups: docker
        sudo: ALL=(ALL) NOPASSWD:ALL
        shell: /bin/bash
        ssh_authorized_keys:
          - ${var.deploy_ssh_key}
    runcmd:
      - systemctl enable --now docker
      - sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
      - systemctl restart sshd
  EOT

  tags = [var.project]
}

Common Droplet Sizes

| Size | vCPUs | Memory | Use Case | |------|-------|--------|----------| | s-1vcpu-1gb | 1 | 1 GB | Testing, small apps | | s-1vcpu-2gb | 1 | 2 GB | Small production | | s-2vcpu-4gb | 2 | 4 GB | Medium apps | | s-4vcpu-8gb | 4 | 8 GB | Production workloads | | c-2 | 2 | 4 GB | CPU-intensive | | m-2vcpu-16gb | 2 | 16 GB | Memory-intensive |

Reserved IP (Static IP)

resource "digitalocean_reserved_ip" "app" {
  region = var.region
}

resource "digitalocean_reserved_ip_assignment" "app" {
  ip_address = digitalocean_reserved_ip.app.ip_address
  droplet_id = digitalocean_droplet.app.id
}

output "app_ip" {
  value = digitalocean_reserved_ip.app.ip_address
}

Firewall

Basic Web Server Firewall

resource "digitalocean_firewall" "web" {
  name = "${var.project}-web-firewall"

  droplet_ids = [digitalocean_droplet.app.id]

  # Inbound rules
  inbound_rule {
    protocol         = "tcp"
    port_range       = "22"
    source_addresses = var.ssh_allowed_ips  # Restrict SSH access
  }

  inbound_rule {
    protocol         = "tcp"
    port_range       = "80"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }

  inbound_rule {
    protocol         = "tcp"
    port_range       = "443"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }

  # Outbound rules
  outbound_rule {
    protocol              = "tcp"
    port_range            = "all"
    destination_addresses = ["0.0.0.0/0", "::/0"]
  }

  outbound_rule {
    protocol              = "udp"
    port_range            = "all"
    destination_addresses = ["0.0.0.0/0", "::/0"]
  }

  outbound_rule {
    protocol              = "icmp"
    destination_addresses = ["0.0.0.0/0", "::/0"]
  }
}

Dynamic IP Whitelist

variable "db_allowed_ips" {
  type        = list(string)
  default     = []
  description = "IPs allowed to access database directly"
}

resource "digitalocean_database_firewall" "postgres" {
  cluster_id = digitalocean_database_cluster.postgres.id

  # Always allow app droplet
  rule {
    type  = "droplet"
    value = digitalocean_droplet.app.id
  }

  # Dynamic IP whitelist for developers
  dynamic "rule" {
    for_each = var.db_allowed_ips
    content {
      type  = "ip_addr"
      value = rule.value
    }
  }
}

Managed Database

PostgreSQL Cluster

resource "digitalocean_database_cluster" "postgres" {
  name       = "${var.project}-${var.environment}-pg"
  engine     = "pg"
  version    = "16"
  size       = var.db_size  # db-s-1vcpu-1gb, db-s-2vcpu-4gb
  region     = var.region
  node_count = 1  # Increase for HA

  # CRITICAL: Use private network
  private_network_uuid = digitalocean_vpc.main.id

  tags = [var.project, var.environment]
}

# Database firewall - restrict access
resource "digitalocean_database_firewall" "postgres" {
  cluster_id = digitalocean_database_cluster.postgres.id

  rule {
    type  = "droplet"
    value = digitalocean_droplet.app.id
  }
}

output "database_uri" {
  value     = digitalocean_database_cluster.postgres.uri
  sensitive = true
}

output "database_private_uri" {
  value     = digitalocean_database_cluster.postgres.private_uri
  sensitive = true
}

Database Sizes

| Size | vCPUs | Memory | Storage | Use Case | |------|-------|--------|---------|----------| | db-s-1vcpu-1gb | 1 | 1 GB | 10 GB | Development | | db-s-1vcpu-2gb | 1 | 2 GB | 25 GB | Small production | | db-s-2vcpu-4gb | 2 | 4 GB | 38 GB | Production | | db-s-4vcpu-8gb | 4 | 8 GB | 115 GB | High traffic |

Redis Cluster

resource "digitalocean_database_cluster" "redis" {
  name       = "${var.project}-${var.environment}-redis"
  engine     = "redis"
  version    = "7"
  size       = "db-s-1vcpu-1gb"
  region     = var.region
  node_count = 1

  private_network_uuid = digitalocean_vpc.main.id
  tags                 = [var.project]
}

Spaces (Object Storage)

resource "digitalocean_spaces_bucket" "assets" {
  name   = "${var.project}-assets"
  region = var.spaces_region  # nyc3, sfo3, ams3, sgp1, fra1

  acl = "private"  # or "public-read"
}

resource "digitalocean_spaces_bucket_cors_configuration" "assets" {
  bucket = digitalocean_spaces_bucket.assets.id
  region = var.spaces_region

  cors_rule {
    allowed_headers = ["*"]
    allowed_methods = ["GET", "PUT", "POST"]
    allowed_origins = ["https://${var.domain}"]
    max_age_seconds = 3600
  }
}

output "spaces_endpoint" {
  value = digitalocean_spaces_bucket.assets.bucket_domain_name
}

DNS Records

resource "digitalocean_domain" "main" {
  name = var.domain
}

resource "digitalocean_record" "app" {
  domain = digitalocean_domain.main.id
  type   = "A"
  name   = "@"
  value  = digitalocean_reserved_ip.app.ip_address
  ttl    = 300
}

resource "digitalocean_record" "www" {
  domain = digitalocean_domain.main.id
  type   = "CNAME"
  name   = "www"
  value  = "@"
  ttl    = 300
}

SSH Keys

# Reference existing key
data "digitalocean_ssh_key" "deploy" {
  name = "deploy-key"
}

# Or create new key
resource "digitalocean_ssh_key" "deploy" {
  name       = "${var.project}-deploy"
  public_key = file("~/.ssh/deploy.pub")
}

Complete Production Stack

locals {
  name_prefix = "${var.project}-${var.environment}"
}

# VPC
resource "digitalocean_vpc" "main" {
  name     = "${local.name_prefix}-vpc"
  region   = var.region
  ip_range = "10.10.0.0/16"
}

# App Server
resource "digitalocean_droplet" "app" {
  name     = "${local.name_prefix}-app"
  region   = var.region
  size     = var.droplet_size
  image    = "ubuntu-22-04-x64"
  vpc_uuid = digitalocean_vpc.main.id

  ssh_keys   = [data.digitalocean_ssh_key.deploy.id]
  monitoring = true

  user_data = file("${path.module}/cloud-init.yaml")
  tags      = [var.project, var.environment]
}

# Static IP
resource "digitalocean_reserved_ip" "app" {
  region = var.region
}

resource "digitalocean_reserved_ip_assignment" "app" {
  ip_address = digitalocean_reserved_ip.app.ip_address
  droplet_id = digitalocean_droplet.app.id
}

# Firewall
resource "digitalocean_firewall" "app" {
  name        = "${local.name_prefix}-firewall"
  droplet_ids = [digitalocean_droplet.app.id]

  inbound_rule {
    protocol         = "tcp"
    port_range       = "22"
    source_addresses = var.ssh_allowed_ips
  }

  inbound_rule {
    protocol         = "tcp"
    port_range       = "80"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }

  inbound_rule {
    protocol         = "tcp"
    port_range       = "443"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }

  outbound_rule {
    protocol              = "tcp"
    port_range            = "all"
    destination_addresses = ["0.0.0.0/0", "::/0"]
  }

  outbound_rule {
    protocol              = "udp"
    port_range            = "all"
    destination_addresses = ["0.0.0.0/0", "::/0"]
  }
}

# Database
resource "digitalocean_database_cluster" "postgres" {
  name                 = "${local.name_prefix}-pg"
  engine               = "pg"
  version              = "16"
  size                 = var.db_size
  region               = var.region
  node_count           = 1
  private_network_uuid = digitalocean_vpc.main.id
  tags                 = [var.project]
}

resource "digitalocean_database_firewall" "postgres" {
  cluster_id = digitalocean_database_cluster.postgres.id

  rule {
    type  = "droplet"
    value = digitalocean_droplet.app.id
  }
}

# Outputs
output "app_ip" {
  value = digitalocean_reserved_ip.app.ip_address
}

output "database_uri" {
  value     = digitalocean_database_cluster.postgres.private_uri
  sensitive = true
}

Best Practices

Security

  • Always use VPC for private networking
  • Restrict database to droplet access only
  • Use reserved IPs for consistent addressing
  • Limit SSH to specific IPs via firewall
  • Use cloud-init to disable root login

Cost Optimization

  • Use appropriate droplet sizes
  • Single-node databases for non-critical workloads
  • Reserved IPs are free when assigned
  • Monitor with built-in metrics

Networking

  • Use private_uri for database connections
  • Place all resources in same VPC
  • Use firewall rules, not iptables
  • Enable monitoring on droplets
Skills similaires