04/12/2025

Définir les ressources dans le fichier main.tf

Maintenant que nous avons configuré notre provider Azure, déclaré nos variables et défini leurs valeurs, nous pouvons passer à l’étape centrale de notre projet : la création des ressources. Dans Terraform, le fichier main.tf fait souvent office de point d’entrée principal. C’est dans ce dernier que l’on décrit les ressources à provisionner en s’appuyant sur les variables définies dans variables.tf.

Pour rappel, tous les fichiers avec l’extension .tf sont automatiquement pris en compte par Terraform, quel que soit leur nom. Nous avons toutefois choisi de suivre les conventions et meilleures pratiques de la communauté (main.tf, variables.tf, providers.tf, etc.) pour structurer le projet de manière plus universelle. En ce qui concerne les fichiers de variables (.tfvars), l’extension doit impérativement être conservée, mais le nom peut varier.

I. Comprendre le fonctionnement du fichier main.tf

Lorsque Terraform exécute une commande comme terraform apply, il commence par analyser l’ensemble des fichiers *.tf présents dans le répertoire, dont main.tf. Il interprète alors la configuration déclarative définie dans ces fichiers, identifie le provider à utiliser (azurerm dans notre cas) et il effectue les appels nécessaires vers l’API Azure Resource Manager (ARM).

Terraform compare ensuite l’état actuel de l’infrastructure (stocké dans le fichier terraform.tfsate) avec l’état souhaité tel que défini dans la configuration. À partir de cette analyse, il génère un plan d’exécution et applique les modifications nécessaires pour synchroniser l’infrastructure réelle avec la configuration décrite dans main.tf.

II. Utilisation des variables dans main.tf

Dans le fichier main.tf, les valeurs dynamiques comme le nom de la machine virtuelle, la région ou la taille de disque ne sont pas codées en dur. À la place, elles sont appelées sous forme de variables déclarées dans variables.tf et alimentées via terraform.tfvars. Pour les utiliser, il faut faire précéder leur nom de var. dans le code HCL.

Par exemple, si nous avons une variable nommée location, on l’utilisera ainsi dans main.tf :

location = var.location

Ce mécanisme rend la configuration plus souple, réutilisable et facile à adapter à différents environnements sans modifier le fichier principal.

Dans les prochaines sections, nous allons construire le fichier main.tf ressource par ressource afin de bien comprendre le rôle de chaque bloc.
Gardez en tête que toutes ces ressources formeront un seul fichier complet à la fin et qu’elles seront interprétées ensemble par Terraform lors de l’exécution.

III. Création du groupe de ressources

Nous allons commencer notre fichier main.tf par la création d'un groupe de ressources qui servira de conteneur logique pour regrouper l’ensemble des composants de notre projet. Dans Azure, un groupe de ressources (ou resource group) est l’unité d’organisation de base pour tout composant d'infrastructure. Il permet de rassembler plusieurs ressources liées (comme des machines virtuelles, des réseaux, des disques ou des bases de données) sous une même entité qui peut ensuite être gérée de manière centralisée.

# Groupe de ressources principal
resource "azurerm_resource_group" "rg" {
  name     = var.resource_group_name
  location = var.location
}

Ce bloc crée un groupe de ressources dans la région spécifiée (voir la valeur correspondante dans terraform.tfvars). Toutes les ressources que nous allons déployer dans les prochaines sections seront contenues dans ce groupe. Cette organisation simplifie non seulement la gestion des ressources, mais aussi leur facturation et leur suppression en une seule opération si nécessaire. En somme, le groupe de ressources agit comme une unité logique de déploiement et d’administration au sein de l’environnement Azure.

IV. Configuration du réseau

Avant de pouvoir déployer notre machine virtuelle, nous devons préparer l’environnement réseau auquel elle sera rattachée. Dans Azure, comme dans la plupart des fournisseurs cloud, les ressources communiquent entre elles à l’intérieur d’un réseau virtuel (ou Virtual Network, abrégé vNet). Ce vNet est une infrastructure réseau privée, isolée du reste du cloud, que nous pouvons configurer selon nos besoins.

Chaque vNet possède une plage d’adresses IP définie au moment de sa création et il peut contenir un ou plusieurs sous-réseaux (subnets). Ceux-ci permettent de segmenter le vNet en blocs logiques distincts, chacun pouvant héberger des types de ressources différents ou encore appliquer des règles de sécurité spécifiques. Cette segmentation est essentielle pour structurer une architecture réseau propre, notamment lorsque plusieurs machines virtuelles, bases de données ou services doivent coexister dans un projet.

Le bloc suivant crée un réseau virtuel dont la plage d’adresses est définie dans nos variables. Ce réseau sera isolé des autres réseaux Azure, sauf si nous établissons explicitement des connexions externes (VPN, peering, etc.).

# Réseau virtuel principal
resource "azurerm_virtual_network" "vnet" {
  name                = "${var.vm_name}-vnet"
  address_space       = var.vnet_address_space
  location            = var.location
  resource_group_name = azurerm_resource_group.rg.name
}

Le sous-réseau défini dans le prochain bloc constitue une subdivision logique du vNet. Il fournit un espace d’adressage IP distinct dans lequel nous allons déployer la machine virtuelle. En séparant les ressources dans des sous-réseaux, nous pouvons mieux contrôler le trafic réseau, appliquer des groupes de sécurité ciblés ou isoler des charges de travail sensibles :

# Sous-réseau associé
resource "azurerm_subnet" "subnet" {
  name                 = "${var.vm_name}-subnet"
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = var.subnet_prefix
  location             = var.location
  resource_group_name  = var.resource_group_name
}

Enfin, la ressource azurerm_public_ip nous permet de réserver une adresse IP publique statique. Elle sera associée à l’interface réseau de la machine virtuelle, de manière à rendre cette dernière accessible depuis l’extérieur, notamment pour établir une connexion SSH. Cette IP restera la même jusqu’à ce que la ressource soit explicitement supprimée, ce qui évite les changements d’adresse à chaque redéploiement.

# Adresse IP publique pour accéder à la VM
resource "azurerm_public_ip" "public_ip" {
  name                = "${var.vm_name}-ip"
  location            = var.location
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method   = "Static"
  sku                 = "Standard"
}

V. Sécurité réseau (NSG)

Dans un environnement cloud, il est particulièrement important de sécuriser les flux réseau. Azure propose un mécanisme appelé Network Security Group (NSG), qui agit comme un pare-feu à l’échelle de la machine virtuelle ou du sous-réseau. Il permet de contrôler le trafic entrant et sortant en définissant des règles explicites fondées sur des critères comme le port, le protocole ou l’adresse IP source ou destination.

Chaque règle peut autoriser (Allow) ou bloquer (Deny) un type de trafic. Les règles sont évaluées par priorité, du plus bas au plus élevé. Il est donc important de bien choisir l’ordre dans lequel elles sont définies.

Voici un exemple de NSG adapté à notre projet :

# Groupe de sécurité réseau
resource "azurerm_network_security_group" "nsg" {
  name                = "${var.vm_name}-nsg"
  location            = var.location
  resource_group_name = azurerm_resource_group.rg.name

  security_rule {
    name                       = "Allow_SSH"
    priority                   = 1001
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = "0.0.0.0/0"
    destination_address_prefix = "*"
  }

  security_rule {
    name                       = "Allow_HTTP"
    priority                   = 1002
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "80"
    source_address_prefix      = "0.0.0.0/0"
    destination_address_prefix = "*"
  }

  security_rule {
    name                       = "Allow_HTTPS"
    priority                   = 1003
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "443"
    source_address_prefix      = "0.0.0.0/0"
    destination_address_prefix = "*"
  }

  security_rule {
    name                       = "Allow_All_Outbound"
    priority                   = 1004
    direction                  = "Outbound"
    access                     = "Allow"
    protocol                   = "*"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

Ces règles permettent à la machine d’être joignable en SSH (port 22) pour l’administration, tout en assurant la possibilité de naviguer vers l’extérieur ou de communiquer avec d’autres services (par les ports 80 et 443, entre autres). La dernière règle sortante évite tout blocage involontaire des connexions nécessaires aux mises à jour ou aux communications avec des APIs.

Nous allons maintenant définir l’interface réseau de la machine virtuelle. Celle-ci constitue un élément central du déploiement, puisqu’elle assure le lien entre la VM, le sous-réseau, l’adresse IP publique et les règles de sécurité que nous venons de créer.

VI. Interface réseau de la VM

Dans Azure, une machine virtuelle ne peut pas fonctionner sans une interface réseau (ou network interface), car c’est elle qui établit le lien entre la machine virtuelle et le réseau virtuel. C’est cette ressource qui porte la configuration IP, les règles de sécurité associées (via le NSG) et les connexions aux sous-réseaux.

# Interface réseau
resource "azurerm_network_interface" "nic" {
  name                = "${var.vm_name}-nic"
  location            = var.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "ipconfig1"
    subnet_id                     = azurerm_subnet.subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.public_ip.id
  }
}

Le bloc ip_configuration permet de définir une configuration réseau propre à cette interface. Nous lui attribuons une adresse IP privée dynamique (générée automatiquement) et nous lui associons l’adresse IP publique créée plus tôt. C’est cette combinaison qui permettra d’accéder à la VM depuis Internet, tout en lui donnant aussi une identité réseau interne au sein du sous-réseau.

VII. Association du NSG à l’interface réseau

Une fois notre groupe de sécurité réseau (NSG) défini, il faut l’associer à une ressource réseau pour qu’il devienne actif. Azure permet d’attacher un NSG à un sous-réseau ou directement à une interface réseau. Ici, nous allons l’associer à l’interface réseau de la machine virtuelle créée dans la section précédente. Cette opération garantit que les règles de sécurité que nous avons définies (comme l’ouverture du port SSH) vont s’appliquer à la machine.

# Association du NSG avec la carte réseau
resource "azurerm_network_interface_security_group_association" "nsg_assoc" {
  network_interface_id      = azurerm_network_interface.nic.id
  network_security_group_id = azurerm_network_security_group.nsg.id
}

La ressource azurerm_network_interface_security_group_association agit comme un lien entre le NSG et la carte réseau de la VM. Sans cette association explicite, les règles du NSG resteraient sans effet, même si elles ont été correctement définies.

Prenez note que l’ordre dans lequel les blocs de ressources apparaissent dans les fichiers .tf n’a pas d’incidence directe sur l’exécution. Terraform analyse automatiquement les dépendances entre les ressources grâce aux références internes (resource.name.attribute) et il applique les étapes dans le bon ordre. Par exemple, nous aurions pu déclarer le groupe de ressources (resource_group) à la fin du fichier, même s’il est utilisé par d’autres ressources définies plus haut. Terraform saura dans quel ordre les créer.

VIII. Déploiement de la machine virtuelle

Nous pouvons maintenant passer à la ressource principale de ce projet : la machine virtuelle Ubuntu que nous allons créer à l’aide du bloc azurerm_linux_virtual_machine. Dans Azure, une machine virtuelle est composée de plusieurs éléments essentiels qu’il est important de comprendre avant d’en lancer le déploiement.

D’abord, la taille de la VM (vm_size) détermine ses ressources matérielles (CPU, mémoire, bande passante, etc.) et elle doit être choisie en fonction de l’usage prévu. Il existe une vaste gamme de tailles adaptées à différents scénarios (bureautique, base de données, machine d'entraînement IA, etc.). Voici des exemples en mode graphique :

Ensuite, le disque système est défini dans un bloc os_disk où nous précisons notamment la taille (disk_size_gb), le type de stockage (storage_account_type) tel que Standard_LRS pour des disques classiques (non SSD) ainsi que le mode de cache (caching). Ces options influencent directement la performance et le coût de la VM.

L’image utilisée pour le système d’exploitation est spécifiée dans un bloc source_image_reference. Nous y retrouvons quatre champs : publisher, offer, sku et version. Ce découpage permet de cibler précisément une image parmi les nombreuses versions disponibles sur Azure. Par exemple, le publisher "Canonical" propose l’offre "0001-com-ubuntu-server-noble" avec le SKU "24_04-lts-gen2", ce qui permet de déployer la dernière version LTS d’Ubuntu (24.04) sur un système compatible Gen2. Si vous lancez l'assistant de création d'une VM dans Azure, vous pourrez voir plusieurs exemples d'images et SKU :

Chaque paramètre est défini dans un bloc dédié, ce qui permet de structurer proprement la configuration de la machine virtuelle tout en conservant une grande souplesse pour faire évoluer ses caractéristiques selon les besoins. Nous allons voir maintenant comment ces différents blocs s’imbriquent dans notre fichier main.tf.

# Machine virtuelle Linux
resource "azurerm_linux_virtual_machine" "vm" {
  name                  = var.vm_name
  location              = var.location
  resource_group_name   = azurerm_resource_group.rg.name
  size                  = var.vm_size
  network_interface_ids = [azurerm_network_interface.nic.id]
  admin_username        = var.admin_username

  os_disk {
    name                 = "${var.vm_name}-osdisk"
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
    disk_size_gb         = var.os_disk_size_gb
  }

#--- Expliqué dans la prochaine section ---#

  provisioner "remote-exec" {
    inline = [
      "echo 'Bienvenue dans votre première VM provisionnée avec Terraform !' > /home/${var.admin_username}/bienvenue.txt",
      "chmod 644 /home/${var.admin_username}/bienvenue.txt",
      "sudo apt-get update -y",
      "sudo apt-get install -y curl unzip",
      "curl -fsSL https://releases.hashicorp.com/terraform/1.6.6/terraform_1.6.6_linux_amd64.zip -o terraform.zip",
      "unzip terraform.zip",
      "sudo mv terraform /usr/local/bin/",
      "terraform --version"
    ]

    connection {
      type        = "ssh"
      user        = var.admin_username
      private_key = var.ssh_private_key
      host        = azurerm_public_ip.public_ip.ip_address
    }
  
#------------------------------------------#

  source_image_reference {
    publisher = var.image_publisher
    offer     = var.image_offer
    sku       = var.image_sku
    version   = var.image_version
  }

  disable_password_authentication = true

  admin_ssh_key {
    username   = var.admin_username
    public_key = var.ssh_public_key
  }

  tags = {
    environment = var.environment_tag
  }

  depends_on = [
    azurerm_network_interface.nic,
    azurerm_network_security_group.nsg
  ]
}

Ce bloc crée une VM basée sur une image Ubuntu officielle. Plusieurs éléments méritent une attention particulière :

  • network_interface_ids est une liste qui relie la machine à l’interface réseau créée précédemment. Même s’il n’y en a qu’une, la syntaxe impose l’usage des crochets ([]) pour désigner une liste.
  • Le disque système est configuré dans le bloc os_disk dans lequel nous définissons son nom, sa taille et son type de stockage.
  • L’image utilisée est référencée selon le modèle publisher / offer / sku / version. Il est conseillé de valider la disponibilité de l’image avec l’outil Azure CLI comme nous l'avons montré plus haut.
  • L’authentification par mot de passe est désactivée pour renforcer la sécurité. Nous utilisons une clé SSH publique déclarée dans nos variables pour se connecter.
  • Le bloc tags permet d’associer des métadonnées utiles pour l’organisation ou la facturation.
  • depends_on garantit que la VM sera créée après l’interface réseau et les règles de sécurité.

Attention : Le paramètre storage_account_type permet de définir le type de stockage utilisé pour le disque du système d’exploitation. Dans l’exemple ci-dessus, nous utilisons Standard_LRS (stockage standard à redondance locale), mais certaines images ou régions pourraient exiger un autre type, comme Premium_LRS pour des disques SSD haute performance. Si une erreur survient lors du déploiement, vérifiez la compatibilité de ce paramètre avec la taille de machine virtuelle (vm_size) et la région choisies.

Comme nous utiliserons une clé SSH publique, il ne faut pas oublier d'en exporter la valeur sous forme de variable d'environnement pour qu'elle puisse être déposée par Terraform dans la machine virtuelle :

# Bash ou Git Bash
export TF_VAR_ssh_public_key="$(< ~/.ssh/id_rsa.pub)"
export TF_VAR_ssh_public_key="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC..."

# PowerShell
$env:TF_VAR_ssh_public_key  = Get-Content -Raw "$HOME\.ssh\id_rsa.pub"
$env:TF_VAR_ssh_public_key  = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC..."

Au besoin, vous pouvez consulter les tutoriels suivants pour apprendre à créer une paire de clés SSH :

IX. Variante : déployer une VM Windows Server

Si vous préférez tester avec une machine Windows, vous pouvez simplement modifier la ressource azurerm_linux_virtual_machine dans main.tf et la remplacer par le bloc suivant :

resource "azurerm_windows_virtual_machine" "vm" {
  name                  = var.vm_name
  location              = var.location
  resource_group_name   = azurerm_resource_group.rg.name
  network_interface_ids = [azurerm_network_interface.nic.id]
  size                  = var.vm_size
  admin_username        = var.admin_username
  admin_password        = var.admin_password

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
    disk_size_gb         = var.os_disk_size_gb
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2022-datacenter-smalldisk"
    version   = "latest"
  }

  provision_vm_agent        = true
  enable_automatic_updates  = true

  tags = {
    environment = var.environment_tag
  }

  depends_on = [
    azurerm_subnet.subnet,
    azurerm_network_security_group.nsg
  ]
}

Cette variante nécessite d’ajouter une variable admin_password dans variables.tf (de type sensitive = true) et de lui attribuer une valeur forte dans terraform.tfvars. Encore une fois, n'oubliez pas de vérifier si la taille, le type de stockage et la version du système d'exploitation existent bien dans votre région.

X. Provisionnement avec remote-exec

Terraform ne se limite pas à la création de ressources : il peut aussi exécuter des commandes sur une machine distante juste après sa création. Pour effectuer cette tâche, il s’appuie sur un provisionneur, un composant qui permet d’automatiser certaines tâches de configuration ou d’installation dans les ressources nouvellement déployées. Dans notre exemple, nous allons utiliser le provisionneur remote-exec qui se connecte à la VM via SSH pour lancer une série de commandes.

Voici un exemple simple, mais pédagogique. Nous allons créer un fichier de bienvenue (bienvenue.txt) dans le répertoire personnel de l’utilisateur, puis télécharger et installer Terraform directement dans la machine virtuelle. Ce scénario permet de tester facilement la connectivité et de valider l’installation en exécutant la commande terraform --version.

Le bloc suivant doit être intercalé dans le bloc ressource azurerm_linux_virtual_machine à l'emplacement indiqué dans la section de création de la VM Ubuntu :

# remote-exec provisioner
provisioner "remote-exec" {
    inline = [
      "echo 'Bienvenue dans votre première VM provisionnée avec Terraform !' > /home/${var.admin_username}/bienvenue.txt",
      "chmod 644 /home/${var.admin_username}/bienvenue.txt",
      "sudo apt-get update -y",
      "sudo apt-get install -y curl unzip",
      "curl -fsSL https://releases.hashicorp.com/terraform/1.6.6/terraform_1.6.6_linux_amd64.zip -o terraform.zip",
      "unzip terraform.zip",
      "sudo mv terraform /usr/local/bin/",
      "terraform --version"
    ]

    connection {
      type        = "ssh"
      user        = var.admin_username
      private_key = var.ssh_private_key
      host        = azurerm_public_ip.public_ip.ip_address
    }
  }

Le provisionneur remote-exec permet de finaliser automatiquement l’environnement une fois la machine virtuelle déployée. Dans notre exemple, il utilise une connexion SSH sécurisée par clé privée pour se connecter à la VM et exécuter une série de commandes à distance. Ces instructions créent un fichier de bienvenue dans le répertoire de l’utilisateur, installent Terraform, puis affichent sa version. L’utilisateur peut ensuite se connecter à la machine pour vérifier la présence du fichier bienvenue.txt ou exécuter la commande terraform --version afin de s’assurer que l’installation s’est déroulée correctement.

Comme nous aurons besoin d'une clé privée, voici les commandes pour en exporter la valeur sous forme de variables d'environnement :

# Bash ou Git Bash
export TF_VAR_ssh_private_key="$(< ~/.ssh/id_rsa)"
export TF_VAR_ssh_private_key="-----BEGIN OPENSSH PRIVATE KEY-----..."

# PowerShell
$env:TF_VAR_ssh_private_key = Get-Content -Raw "$HOME\.ssh\id_rsa"
$env:TF_VAR_ssh_private_key = "-----BEGIN OPENSSH PRIVATE KEY-----..."

XI. Variante : provisionnement avec remote-exec pour Windows

Si la machine virtuelle cible est une VM Windows, la connexion à distance ne se fait pas en SSH, mais via WinRM (Windows Remote Management). Le provisionneur remote-exec fonctionne également dans ce contexte, à condition d’adapter la configuration de la connexion et les commandes exécutées. Dans ce cas, nous pouvons retirer les paramètres liés à SSH et remplacer les commandes Linux par des instructions PowerShell adaptées à Windows :

provisioner "remote-exec" {
  inline = [
    "New-Item -Path \"C:\\Users\\${var.admin_username}\\Desktop\\bienvenue.txt\" -ItemType File -Value 'Bienvenue dans votre première VM provisionnée avec Terraform !'",
    "Set-ItemProperty -Path \"C:\\Users\\${var.admin_username}\\Desktop\\bienvenue.txt\" -Name IsReadOnly -Value $false",
    "Invoke-WebRequest -Uri \"https://releases.hashicorp.com/terraform/1.6.6/terraform_1.6.6_windows_amd64.zip\" -OutFile terraform.zip",
    "Expand-Archive terraform.zip -DestinationPath . -Force",
    "Move-Item -Path .\\terraform.exe -Destination \"C:\\Windows\\System32\" -Force",
    "terraform --version"
  ]

  connection {
    type     = "winrm"
    user     = var.admin_username
    password = var.admin_password
    host     = azurerm_public_ip.public_ip.ip_address
    port     = 5986
    https    = true
    timeout  = "5m"
    insecure = true
  }
}

Pour que la connexion WinRM fonctionne correctement, le port TCP 5986 (WinRM sur HTTPS) doit être ouvert dans le groupe de sécurité réseau (NSG) associé à la VM. Assurez-vous d’ajouter une règle entrante autorisant ce port depuis votre adresse IP ou plage d’adresses source autorisée dans le bloc azurerm_network_security_group pour autoriser WinRM (HTTPS) via le port 5986 :

security_rule {
  name                       = "allow_winrm_https"
  priority                   = 1010
  direction                  = "Inbound"
  access                     = "Allow"
  protocol                   = "Tcp"
  source_port_range          = "*"
  destination_port_range     = "5986"
  source_address_prefix      = "<votre_adresse_ip_publique>"
  destination_address_prefix = "*"
}

Remplacez <votre_adresse_ip_publique> par votre adresse IP réelle ou par une plage CIDR (par exemple 203.0.113.0/24) afin de limiter l’accès au strict nécessaire. La valeur de priorité (1010) doit être unique dans le groupe de sécurité réseau.

Adaptez-la si besoin pour éviter tout conflit avec d’autres règles existantes. Vous pouvez également utiliser "Internet" ou "*" comme valeur de source_address_prefix pour effectuer des tests rapides, mais cette pratique est fortement déconseillée en environnement de production.

Vous pouvez passer au prochain chapitre pour continuer la configuration.

author avatar
Luc BRETON Administrateur système et cloud
Administrateur système et cloud avec une orientation DevOps pour une grande chaîne de pharmacies québécoise. Je suis plutôt généraliste avec une forte expérience côté virtualisation, stockage, cloud hybride et un intérêt particulier pour l'automatisation. J'aime le transfert de connaissances et il me fait plaisir d'être la première voix nord-américaine d'IT-Connect !
Partagez cet article Partager sur Twitter Partager sur Facebook Partager sur Linkedin Envoyer par mail

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.