Configuration de Terraform Scaleway/OVHcloud

Terraform

Terraform est un outil que j’utilise pour provisionner mes ressources hébergées chez cetains fournisseurs comme OVH, ScaleWay, Azure ou AWS. On parle dès lors d’infrastructure As Code (IaC) : cela permet d’automatiser certaines tâches. L’objectif est de provisionner des ressources chez un fournisseur. Plus d’informations : Browse Providers | Terraform Registry.

L’ensemble des commandes ci-dessous sont compatibles avec l’ensemble des clients (Windows, Mac, Linux). Nous allons dans notre exemple déployer une infrastructure chez Scaleway avec Terraform et executer des actions sur un serveur OVHcloud.

Installation de Terraform sur Mac OS

Il est possible d’installer manuellement le paquet sous Mac OS, ou via le gestionnaire de paquet Homebrew :

brew tap hashicorp/tap
Running `brew update --preinstall`...
==> Auto-updated Homebrew!
d 3 taps (hashicorp/tap, homebrew/core and homebrew/cask).
==> New Formulae
dbml-cli             dotdrop              [email protected]           goctl                libabw               lndir                octosql              release-it           sdl2_sound           sftpgo               xcode-kotlin
==> Updated Formulae
Updated 554 formulae.
==> New Casks
aethersx2                                       jquake                                          plus42-binary                                   ti-smartview-ce-for-the-ti-84-plus-family       vieb
bike                                            manila                                          plus42-decimal                                  tqsl                                            yandex-music-unofficial
ferdium                                         mega                                            podman-desktop                                  trivial
groestlcoin-core                                oxwu                                            protokol                                        universal-android-debloater
==> Updated Casks
Updated 731 casks.
==> Deleted Casks
entropy                 gloomhaven-helper       indigo                  kite                    megax                   nbtexplorer             shortery                tifig                   twitch                  xoctave
brew install hashicorp/tap/terraform
==> Downloading https://releases.hashicorp.com/terraform/1.1.9/terraform_1.1.9_darwin_arm64.zip
######################################################################## 100.0%
==> Installing terraform from hashicorp/tap
????  /opt/homebrew/Cellar/terraform/1.1.9: 3 files, 71.4MB, built in 2 seconds
==> Running `brew cleanup terraform`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

Je consulte ensuite le registre des fournisseurs existants et je recherche Scaleway : scaleway/scaleway | Terraform Registry et OVHcloud.

Configuration de Terraform

Pour démarrer, il est nécessaire de créer un dossier dédié à chaque projet Terraform. Puis il faut créer un fichier main.tf à l’intérieur. Il existe deux façons d’écrire notre configuration : via le language HashiCorp ou via le format structuré JSON. Dans mon cas, je préfère utiliser le formatage JSON.

Terraform avec Scaleway

Nous allons nous intéresser à une intégration chez le fournisseur Scaleway (les commandes init, plan, apply sont expliquées plus bas dans l’exemple avec le fournisseur OVH si besoin).

Voici un exemple de configuration chez Scaleway :

terraform {
  required_providers {
    scaleway = {
      source = "scaleway/scaleway"
    }
  }
  required_version = ">= 0.13"
}

provider "scaleway" {
  alias = "production"
  profile = "kassianoff-blog"
}

resource "scaleway_account_ssh_key" "jérémie" {
  provider = scaleway.production
  name = "Jérémie"
  public_key = "ssh-ed25519 AABBCCDDEEFFGGAABBCCDDEEFFGGAABBCCDDEEFFGGAABBCCDDEEFFGGAABBCCDDEEFF"
}

resource "scaleway_instance_ip" "public_ip" {
  provider = scaleway.production
}

locals {
  ip_source = [
    { ip = "mon_ip_routable_01/32", port = "22" },
    { ip = "mon_ip_routable_02/32", port = "22" },
  ]

  any_in = [
    { ip_range = "0.0.0.0/0", port = "80" },
    { ip_range = "0.0.0.0/0", port = "443" },
  ]

  any_out = [
    { ip_range = "0.0.0.0/0", port = "53", protocol = "TCP" },
    { ip_range = "0.0.0.0/0", port = "53", protocol = "UDP" },
    { ip_range = "0.0.0.0/0", port = "80", protocol = "TCP" },
    { ip_range = "0.0.0.0/0", port = "443", protocol = "TCP" },
  ]
}

resource "scaleway_instance_security_group" "prod" {
  provider = scaleway.production
  description = "scaleway-firewall"
  name = "Kassianoff scaleway firewall"
  inbound_default_policy = "drop"
  outbound_default_policy = "drop"
  enable_default_security = false

  dynamic "inbound_rule" {
    for_each = local.ip_source && local.any_in

    content {
      action = "accept"
      ip = inbound_rule.value.ip
      ip_range = inbound_rule.value.ip_range
      port = inbound_rule.value.port
    }
  }

  dynamic "outbound_rule" {
    for_each = local.any_out

    content {
      action = "accept"
      ip_range = outbound_rule.value.ip_range
      port = outbound_rule.value.port
      protocol = outbound_rule.value.protocol
    }
  }
}

resource "scaleway_instance_server" "blog" {
  provider = scaleway.production
  name = "kassianoff blog"
  type = "DEV1-L"
  image = "ubuntu_jammy"
  tags = [ "blog" ]
  ip_id = scaleway_instance_ip.public_ip.id
  security_group_id = scaleway_instance_security_group.blog.id
  private_network {
    pn_id = scaleway_vpc_private_network.blog.id
  }
}

resource "scaleway_rdb_database" "blog" {
  instance_id = scaleway_rdb_instance.blog.id
  name = "kassianoff-database"
  node_type = "DB-DEV-S"
  engine = "PostgreSQL-14"
  is_ha_cluster = true
  disable_backup = true
  user_name = "kassianoff"
  password = "thiZ_is_v&ry_s3cret"
  region= "fr-par"
  tags = [ "db", "blog" ]
  volume_type = "bssd"
  volume_size_in_gb = 5
  private_network { 
    ip_net = "192.168.1.254/24" 
    pn_id = "${scaleway_vpc_private_network.blog.id}" 
  }
}

resource scaleway_vpc_private_network blog { 
name = "production" 
}

N’oublions pas de configurer un profile pour notre provider Scaleway :

profiles:
  kassianoff-blog:
    access_key: XXXXXXXXXXXXXXXXXXXX
    secret_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    default_organization_id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    default_project_id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    default_zone: fr-par-1
    default_region: fr-par
    api_url: https://api.scaleway.com
    insecure: false
  1. Nous précisons le fournisseur avec qui l’on souhaite interagir, c’est à dire Scaleway.
  2. Nous avons défini un profil nommé « kassianoff-blog » pour contacter notre fournisseur. Cela fait référence à un fichier de configuration (qui n’est pas celui par défaut). Celui-ci contient nos secrets, notre zone de déploiement ainsi que le endpoint de Scaleway :
  3. Nous ajoutons une clé SSH à notre profil Scaleway afin de pouvoir nous connecter à notre prochaine instance.
  4. Nous ajoutons une ressource ip_publique afin de pouvoir obtenir une adresse IP sur notre prochaine instance.
  5. Nous déclarons une variable « locales » dans un array JSON avec plusieurs valeurs : ip_source, any_in, any_out, afin de pouvoir les réutiliser dans l’édition de la configuration du pare-feu Scaleway.
  6. Nous allons ensuite créer un groupe de sécurité Scaleway en y intégrant nos règles pare-feu dans une expression dynamique afin de réutiliser nos variables.
  7. Nous allons par la suite créer notre instance serveur en choisissant son nom, son type et ses caractéristiques. Nous remarquons que l’intégration avec l’ip publique et que le groupe de sécurité est bien visible.
  8. De plus, nous souhaitons créer une base de donnée managée par Scaleway afin de nous affranchir de sa gestion. Nous connectons en plus un réseau privé à notre base de données et configurons son interface dans le réseau privé.
  9. Un réseau privé sera créé afin de faire communiquer l’instance et la base de donnée dans un réseau privé (il vous faudra configuration votre interface ens5(eth1) sur le serveur linux pour accéder à la base de donnée).

Terraform avec OVHcloud

Voici un autre petit exemple pour basculer mon serveur kimsufi datant de 2013 en mode rescue et le redémarrer :

terraform {
  required_providers {
    ovh = {
      source  = "ovh/ovh"
    }
  }
}

provider "ovh" {
endpoint = "ovh-eu"
application_key = "namJkXXXXXXXXXXX"
application_secret = "rXXXXXXXXXXXX"
consumer_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

data ovh_dedicated_server_boots "rescue" {
service_name = "ksXXXXXXX.kimsufi.com"
boot_type = "rescue"
kernel = "rescue64-pro"
}

resource ovh_dedicated_server_update "server_on_rescue" {
service_name = "ksXXXXXXX.kimsufi.com"
boot_id = data.ovh_dedicated_server_boots.rescue.result[0]
monitoring = true
state = "ok"
}

resource ovh_dedicated_server_reboot_task "server_reboot" {
service_name = data.ovh_dedicated_server_boots.rescue.service_name
keepers = [
ovh_dedicated_server_update.server_on_rescue.boot_id,
]

}

L’une des premières commandes Terraform est « terraform init« . Elle est utilisée pour initialiser un répertoire de travail contenant les fichiers de configuration de Terraform. Il s’agit de la première commande à exécuter après l’écriture d’une nouvelle configuration Terraform. 

terraform init

Initializing the backend...

Initializing provider plugins...

- Finding ovh/ovh versions matching "~> 0.11.0"...

- Installing ovh/ovh v0.11.0...

- Installed ovh/ovh v0.11.0 (signed by a HashiCorp partner, key ID F56D1A6CBDAAADA5)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

La seconde commande est « terraform plan« . La commande crée un plan d’exécution, qui vous permet de prévisualiser les modifications que Terraform prévoit d’apporter à votre infrastructure.

Par défaut, lorsque Terraform crée un plan, il lit l’état actuel de tous les objets distants déjà existants pour s’assurer que l’état de Terraform est à jour. Il compare ensuite la configuration actuelle à l’état précédent et note toute différence. Il propose alors un ensemble d’actions de modification qui devraient, si elles sont appliquées, faire en sorte que les objets distants correspondent à la configuration actuelle :

terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:

  + create

Terraform will perform the following actions:

  # ovh_dedicated_server_reboot_task.server_reboot will be created

  + resource "ovh_dedicated_server_reboot_task" "server_reboot" {
      + comment      = (known after apply)
      + done_date    = (known after apply)
      + function     = (known after apply)
      + id           = (known after apply)
      + keepers      = [
          + "1122",
        ]

      + last_update  = (known after apply)
      + service_name = "ksxxxxxxx.kimsufi.com"
      + start_date   = (known after apply)
      + status       = (known after apply)

    }


  # ovh_dedicated_server_update.server_on_rescue will be created
  + resource "ovh_dedicated_server_update" "server_on_rescue" {

      + boot_id      = 1122
      + id           = (known after apply)
      + monitoring   = true
      + service_name = "ksxxxxxxx.kimsufi.com"

      + state        = "ok"

    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.
  Enter a value: yes

ovh_dedicated_server_update.server_on_rescue: Creating...
ovh_dedicated_server_update.server_on_rescue: Still creating... [10s elapsed]
ovh_dedicated_server_update.server_on_rescue: Creation complete after 11s [id=ksxxxxxxx.kimsufi.com]
ovh_dedicated_server_reboot_task.server_reboot: Creating...
ovh_dedicated_server_reboot_task.server_reboot: Still creating... [10s elapsed]
ovh_dedicated_server_reboot_task.server_reboot: Still creating... [20s elapsed]
ovh_dedicated_server_reboot_task.server_reboot: Still creating... [30s elapsed]
ovh_dedicated_server_reboot_task.server_reboot: Still creating... [40s elapsed]
ovh_dedicated_server_reboot_task.server_reboot: Still creating... [50s elapsed]
ovh_dedicated_server_reboot_task.server_reboot: Still creating... [1m0s elapsed]
ovh_dedicated_server_reboot_task.server_reboot: Still creating... [1m10s elapsed]
ovh_dedicated_server_reboot_task.server_reboot: Still creating... [1m20s elapsed]
ovh_dedicated_server_reboot_task.server_reboot: Still creating... [1m30s elapsed]
ovh_dedicated_server_reboot_task.server_reboot: Creation complete after 1m31s [id=251909160]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Un courriel d’OVH arrivera automatiquement dans votre boîte mail afin de vous transmettre les informations de connexion en mode rescue. Je vous invite à consulter la documentation OVH afin de déployer d’autres types de configuration et également la documentation Terraform pour en apprendre d’avantage.