ℹ️ À savoir · En bref
Le provider officiel citrix/citrix pilote un site Citrix DaaS ou CVAD on-premises (2311 et plus, avec Web Studio) comme du code : connexions d'hébergement, pools de ressources, catalogues MCS, groupes de mise à disposition, applications, stratégies…
Bénéfices : un environnement reproductible (recette = production), chaque changement passe par un plan relu, et la mise à jour d'image devient une simple modification de variable.
Objectifs de la fiche
- Configurer le provider pour Citrix Cloud ou un DDC on-premises.
- Comprendre la chaîne d'objets : zone → connexion → pool → catalogue → groupe de mise à disposition.
- Écrire un catalogue MCS et un groupe de mise à disposition, puis mettre à jour l'image.
- Reprendre un site existant sous Terraform sans tout recréer.
Le modèle d’objets

| Objet Studio | Ressource Terraform |
|---|---|
| Zone / emplacement de ressources | citrix_zone |
| Connexion d'hébergement | citrix_azure_hypervisor, citrix_vsphere_hypervisor, citrix_xenserver_hypervisor, citrix_nutanix_hypervisor… |
| Ressources (réseau, stockage) | citrix_azure_hypervisor_resource_pool (et équivalents par hyperviseur) |
| Catalogue de machines | citrix_machine_catalog (MCS, PVSStreaming ou Manual, Remote PC) |
| Groupe de mise à disposition | citrix_delivery_group |
| Compte de service (jonction AD / Entra) | citrix_service_account |
Configurer le provider
providers.tf
terraform {
required_providers {
citrix = {
source = "citrix/citrix"
version = ">= 1.0" # fige la version testée dans ton projet
}
}
}
# Citrix Cloud (DaaS) : client API sécurisé créé dans Citrix Cloud
provider "citrix" {
cvad_config = {
customer_id = var.customer_id # ou variable d'env. CITRIX_CUSTOMER_ID
client_id = var.client_id # ou CITRIX_CLIENT_ID
client_secret = var.client_secret # ou CITRIX_CLIENT_SECRET
}
}
# CVAD on-premises (≥ 2311, Web Studio installé)
# provider "citrix" {
# cvad_config = {
# hostname = "ddc01.corp.local" # ou CITRIX_HOSTNAME
# client_id = "CORP\\svc-terraform" # compte admin Citrix
# client_secret = var.ddc_password
# }
# }
⚠️ Piège
Ne mets jamais le client_secret dans un fichier commité : passe-le par les variables d'environnement CITRIX_* ou un coffre (Key Vault, variable secrète de pipeline). Et souviens-toi qu'il peut finir dans le state : backend chiffré obligatoire (voir TF-01).
Connexion d’hébergement et pool de ressources (Azure)
hosting.tf
resource "citrix_azure_hypervisor" "azure" {
name = "Azure-FranceCentral"
zone = var.zone_id
active_directory_id = var.azure_tenant_id
subscription_id = var.azure_subscription_id
authentication_mode = "SystemAssignedManagedIdentity" # pas de secret à gérer
proxy_hypervisor_traffic_through_connector = true
}
resource "citrix_azure_hypervisor_resource_pool" "vda" {
name = "RP-VDA-FranceCentral"
hypervisor = citrix_azure_hypervisor.azure.id
region = "France Central"
virtual_network_resource_group = "rg-prd-network"
virtual_network = "vnet-prd-citrix"
subnets = ["snet-vda"]
}
Le catalogue MCS
catalog.tf
resource "citrix_machine_catalog" "w2025_ms" {
name = "CAT-W2025-MS"
description = "Windows Server 2025 multi-session"
zone = var.zone_id
allocation_type = "Random" # obligatoire en MultiSession
session_support = "MultiSession"
provisioning_type = "MCS"
provisioning_scheme = {
hypervisor = citrix_azure_hypervisor.azure.id
hypervisor_resource_pool = citrix_azure_hypervisor_resource_pool.vda.id
identity_type = "ActiveDirectory" # ou AzureAD, HybridAzureAD
machine_domain_identity = {
domain = "corp.local"
domain_ou = "OU=VDA,OU=Citrix,DC=corp,DC=local"
service_account_id = citrix_service_account.ad_join.id # évite le mot de passe en clair
}
azure_machine_config = {
service_offering = "Standard_D8s_v5"
storage_type = "Premium_LRS"
use_managed_disks = true
license_type = "Windows_Server"
azure_master_image = {
resource_group = "rg-prd-images"
master_image = var.master_snapshot # la variable qu'on fait évoluer
}
}
number_of_total_machines = 4
machine_account_creation_rules = {
naming_scheme = "VDA-MS-##"
naming_scheme_type = "Numeric"
}
}
}
| Attribut | À retenir |
|---|---|
allocation_type / session_support | Random + MultiSession pour du partagé ; Static + SingleSession pour des bureaux attribués |
identity_type | ActiveDirectory, AzureAD (exige network_mapping et machine_profile) ou HybridAzureAD |
number_of_total_machines | Augmenter = ajouter des VM ; diminuer = suppression (sans session active, statiques non attribuées) |
enroll_in_intune | Inscription Intune des VDA, uniquement avec identity_type = "AzureAD" (voir M365-INT-01) |
delete_virtual_machines / delete_machine_accounts | Comportement au destroy : effectif seulement après un apply qui l'a enregistré |
Le groupe de mise à disposition
delivery_group.tf
resource "citrix_delivery_group" "bureaux" {
name = "DG-W2025-Bureaux"
associated_machine_catalogs = [{
machine_catalog = citrix_machine_catalog.w2025_ms.id
machine_count = 4
}]
desktops = [{
published_name = "Bureau Windows" # le renommer recrée le bureau !
enabled = true
restricted_access_users = {
allow_list = ["CORP\\GG-Citrix-Bureau"]
}
}]
autoscale_settings = {
autoscale_enabled = true
timezone = "Romance Standard Time"
peak_buffer_size_percent = 10
power_time_schemes = [{
display_name = "Semaine"
days_of_week = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
peak_time_ranges = ["08:00-18:00"]
pool_using_percentage = false
pool_size_schedules = [{ time_range = "07:30-18:30", pool_size = 2 }]
}]
}
reboot_schedules = [{
name = "Reboot hebdo"
reboot_schedule_enabled = true
frequency = "Weekly"
frequency_factor = 1
days_in_week = ["Sunday"]
start_date = "2026-11-01"
start_time = "03:00"
ignore_maintenance_mode = true
natural_reboot_schedule = false
reboot_duration_minutes = 60
}]
}
⚠️ Piège
restricted_access_users omis = tous les utilisateurs authentifiés ont accès ; défini à {} = personne n'a accès.
Changer published_name d'un bureau le recrée : les sessions déconnectées sur l'ancien bureau ne pourront pas être reprises. Fais-le hors des heures de production.
Mettre à jour l’image

- Préparer la nouvelle image (Ansible, Packer ou à la main), prendre un snapshot.
- Changer
master_snapshotdans le.tfvars, puisplan: on doit voir~ update in-placesur le catalogue, jamais-/+ replace. image_update_reboot_options(dansazure_machine_config) règle le redémarrage :reboot_duration = -1pour appliquer au prochain arrêt, sinon cycle de redémarrage avec message d'avertissement.- Retour arrière = remettre l'ancien nom de snapshot et ré-appliquer.
⚠️ Piège
apply_updates_to_existing_machines = true synchronise et redémarre toutes les VM allumées à chaque mise à jour du catalogue, sessions comprises. À activer pour un apply ponctuel, puis à remettre à false.
Reprendre un site existant
| Besoin | Solution |
|---|---|
| Générer le code d'un site existant | Script onboarding helper du dépôt GitHub du provider (scripts/onboarding-helper), filtrable par type et par nom |
| Importer un objet précis | terraform import citrix_delivery_group.bureaux <GUID> ou bloc import { } (Terraform ≥ 1.5) |
| Retrouver les GUID | Script object-ids-helper du même dépôt, ou les traces réseau de Web Studio |
| Vérifier l'import | terraform plan doit afficher No changes avant toute modification |
Pièges classiques
| Symptôme | Cause | Parade |
|---|---|---|
| Le plan veut recréer le catalogue | Changement d'un attribut non modifiable (identité, type, lettre du cache…) | Lire forces replacement ; créer un nouveau catalogue et basculer le groupe |
| Suppression de VM impossible | Sessions actives ou VM statiques attribuées | Mode maintenance, vider les sessions, désattribuer |
| Catalogue en échec à la création | Compte de service sans droits sur l'OU, quotas Azure, image non généralisée | Vérifier l'OU, les quotas de la souscription et le journal MCS dans Studio |
| Divergence entre Studio et le code | Modification manuelle dans Web Studio | Tout changement passe par Terraform ; le plan révèle la dérive |
| Erreur d'authentification | Client API expiré ou mauvais customer_id | Recréer le client sécurisé dans Citrix Cloud, vérifier les variables CITRIX_* |
🔷 En contexte Citrix
Chaîne complète : Terraform (azurerm) crée réseau et VM maître → Ansible prépare l'image (ANS-01) → Terraform (citrix) crée catalogue et groupe de mise à disposition.
Commence par une recette : un catalogue de 1 ou 2 VM et un groupe restreint à ton compte, pour valider l'image et l'autoscale avant la production.
Pour les VDA Linux (Ubuntu), le même citrix_machine_catalog s'applique ; seule l'image maître diffère.
À retenir
citrix/citrixcouvre DaaS et CVAD on-prem ≥ 2311 (avec Web Studio).- Zone → connexion → pool → catalogue → groupe : les références ordonnent la création.
- Secrets en variables d'environnement, jonction AD via
citrix_service_account. - Mise à jour d'image = changer une variable ; vérifier
update in-placedans le plan. - Site existant : onboarding helper + import, puis
plansans changement.
Auto-évaluation
Q1. Quelle version minimale de CVAD on-premises le provider supporte-t-il ?
→ 2311, avec Web Studio installé et configuré.
Q2. Que se passe-t-il si restricted_access_users = {} sur un groupe de mise à disposition ?
→ Plus personne n'a accès : listes d'autorisation et de blocage vides. Omis, l'attribut laisse l'accès à tous les utilisateurs authentifiés.
Q3. Comment déployer une nouvelle image sur un catalogue MCS avec Terraform ?
→ Changer la variable de snapshot (master_image), vérifier que le plan propose une mise à jour en place, appliquer, et régler le redémarrage avec image_update_reboot_options.
Q4. Pourquoi éviter de laisser apply_updates_to_existing_machines = true ?
→ Chaque mise à jour du catalogue redémarrerait toutes les VM allumées, sessions comprises.