Chapitre 1015 minIntermédiaire
Gérer Windows Server avec Ansible (WinRM, SSH)
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- dire ce qui change quand Ansible gère Windows ;
- choisir entre WinRM et SSH ;
- préparer Windows Server 2025 pour WinRM en HTTPS ;
- écrire les variables de connexion d'un groupe Windows avec Kerberos ;
- diagnostiquer une connexion qui échoue.
2 exercices corrigés
Ansible gère Windows Server sans agent, comme Linux : il s'y connecte en WinRM ou en SSH et exécute des modules écrits en PowerShell, rassemblés dans les collections ansible.windows, community.windows et microsoft.ad. Ce chapitre explique ce qui change avec Windows, compare WinRM et SSH, prépare Windows Server 2025, écrit les variables de connexion avec Kerberos et présente les modules à connaître ; le chapitre 11 s'en sert pour automatiser le serveur de fichiers, le DHCP, le DNS et les comptes Active Directory de Sorbier.
Ce qui change avec Windows
| Linux | Windows | |
|---|---|---|
| Connexion | SSH | WinRM (HTTP 5985, HTTPS 5986) ou SSH |
| Langage des modules | Python, exécuté par le Python du serveur | PowerShell 5.1, fourni avec Windows ; pas de Python à installer |
| Modules | ansible.builtin.*, ansible.posix.*… | ansible.windows.win_*, community.windows.*, microsoft.ad.* |
| Élévation de privilèges | become_method: sudo | become_method: runas (rarement utile : on se connecte avec un compte administrateur) |
| Chemins | /etc/chrony/chrony.conf | D:\Partages\Compta : entre apostrophes en YAML, ou avec des / que Windows accepte |
| Nœud de contrôle | Possible | Impossible (sauf à travers WSL) : Windows est seulement un nœud géré |
Les modules Linux ne fonctionnent pas sur Windows : ansible.builtin.copy ne copie pas vers un serveur Windows, il faut ansible.windows.win_copy. Quelques modules font exception et marchent des deux côtés (ansible.builtin.setup, ansible.builtin.debug, ansible.builtin.set_fact, ansible.builtin.include_tasks), parce qu'ils s'exécutent sur le nœud de contrôle ou ont une version PowerShell.
WinRM ou SSH ?
Ansible propose deux façons de joindre un serveur Windows :
| WinRM | SSH | |
|---|---|---|
| Principe | Le service de gestion à distance de Windows (WS-Management), sur lequel repose aussi PowerShell Remoting | Le serveur OpenSSH fourni par Microsoft |
| Ports | 5985 (HTTP), 5986 (HTTPS) | 22 |
| Présence sur Windows Server 2025 | Service actif par défaut, écouteur HTTP configuré | Installé mais désactivé par défaut |
| Authentification | Kerberos, NTLM, certificat, CredSSP, Basic | Clé, mot de passe, Kerberos (GSSAPI) |
| Bibliothèque côté nœud de contrôle | pywinrm (et pywinrm[kerberos] pour Kerberos) | Aucune |
| Statut dans Ansible | Historique, le plus documenté | Officiellement pris en charge depuis ansible-core 2.18, à partir de Windows Server 2022 |
WinRM reste le choix le plus courant dans un domaine Active Directory, avec Kerberos ; c'est celui de Sorbier. SSH est une alternative simple, surtout hors domaine : mêmes clés que pour Linux, un seul port à ouvrir. Ansible propose aussi le plugin psrp, qui passe par le même service que WinRM avec le protocole de PowerShell Remoting.
Préparer Windows Server 2025 pour WinRM en HTTPS
Sur Windows Server, le service WinRM tourne et un écouteur HTTP (5985) existe déjà. En HTTP, Kerberos chiffre les échanges ; mais un écouteur HTTPS chiffre tout le canal, quel que soit le mode d'authentification, et c'est la configuration recommandée. Il lui faut un certificat de machine : idéalement délivré par l'autorité de certification interne de Sorbier (inscription automatique par GPO, cours Cybersécurité des infrastructures) ; en laboratoire, un certificat auto-signé.
Sur chaque serveur Windows, dans une console PowerShell en administrateur, la procédure de la documentation d'Ansible, adaptée pour n'accepter que le bastion :
# Certificat auto-signé pour le laboratoire (en production : un certificat de l'AC interne)
$certParams = @{
CertStoreLocation = 'Cert:\LocalMachine\My'
DnsName = "$env:COMPUTERNAME.ad.sorbier.example"
NotAfter = (Get-Date).AddYears(1)
Provider = 'Microsoft Software Key Storage Provider'
Subject = "CN=$env:COMPUTERNAME.ad.sorbier.example"
}
$cert = New-SelfSignedCertificate @certParams
# Écouteur WinRM en HTTPS sur le port 5986
$httpsParams = @{
Path = 'WSMan:\localhost\Listener'
Address = '*'
CertificateThumbprint = $cert.Thumbprint
Enabled = $true
Port = 5986
Transport = 'HTTPS'
Force = $true
}
New-Item @httpsParams
# Pare-feu Windows : 5986 ouvert au seul bastion, sur le profil Domaine
$firewallParams = @{
Action = 'Allow'
Description = 'WinRM HTTPS depuis le bastion Ansible'
Direction = 'Inbound'
DisplayName = 'WinRM HTTPS (bastion Ansible)'
LocalPort = 5986
Profile = 'Domain'
Protocol = 'TCP'
RemoteAddress = '192.168.10.210'
}
New-NetFirewallRule @firewallParams
# Vérifier les écouteurs
winrm enumerate winrm/config/Listener
RemoteAddress limite la règle au bastion : même si un poste du réseau découvre le port, il ne peut pas s'y connecter. Il faut aussi la règle du pare-feu réseau, du bastion vers les serveurs Windows en TCP 5986 (chapitre 2). Sur un parc de serveurs, la configuration de WinRM se déploie plutôt par GPO (écouteur, règle de pare-feu, inscription du certificat), comme le montre le cours Windows Server.
Les variables de connexion avec Kerberos
Le fichier inventaire/group_vars/windows/vars.yml :
---
ansible_connection: winrm
ansible_port: 5986
ansible_winrm_transport: kerberos
ansible_user: svc-ansible@AD.SORBIER.EXAMPLE
ansible_password: "{{ vault_mdp_svc_ansible }}"
et inventaire/group_vars/windows/vault.yml, chiffré par Ansible Vault (chapitre 9), contient vault_mdp_svc_ansible. Le port 5986 implique HTTPS. Le compte s'écrit avec le royaume Kerberos en majuscules, AD.SORBIER.EXAMPLE.
Kerberos impose trois choses au nœud de contrôle :
1. Les bibliothèques Kerberos et pywinrm avec sa prise en charge de Kerberos :
sudo apt install krb5-user libkrb5-dev python3-dev
pipx inject ansible-core "pywinrm[kerberos]"
2. Le fichier /etc/krb5.conf, qui dit où trouver les contrôleurs du royaume :
[libdefaults]
default_realm = AD.SORBIER.EXAMPLE
[realms]
AD.SORBIER.EXAMPLE = {
kdc = srv-ad.ad.sorbier.example
kdc = srv-ad2.ad.sorbier.example
}
[domain_realm]
.ad.sorbier.example = AD.SORBIER.EXAMPLE
ad.sorbier.example = AD.SORBIER.EXAMPLE
3. Des noms, pas des adresses. Un ticket Kerberos est délivré pour un nom de service (HTTP/srv-fichiers.ad.sorbier.example) : avec une adresse IP, l'authentification échoue. Le bastion doit donc utiliser le DNS de l'Active Directory (192.168.10.177 et .181), et l'inventaire doit désigner les serveurs Windows par leur nom complet. Dans hosts.yml, la partie Windows devient :
windows:
children:
controleurs:
hosts:
srv-ad:
ansible_host: srv-ad.ad.sorbier.example
srv-ad2:
ansible_host: srv-ad2.ad.sorbier.example
fichiers:
hosts:
srv-fichiers:
ansible_host: srv-fichiers.ad.sorbier.example
Avec ansible_user et ansible_password dans les variables, le plugin WinRM obtient lui-même le ticket Kerberos ; pas besoin de lancer kinit. Il faut aussi que l'heure du bastion soit synchronisée avec celle des contrôleurs : Kerberos refuse un écart de plus de cinq minutes (le rôle socle y veille).
La variante SSH. Sur Windows Server 2025, OpenSSH Server est déjà installé : active-le dans le Gestionnaire de serveur (page Serveur local, propriété d'accès SSH à distance, Remote SSH Access sur un système en anglais) ou en PowerShell avec Set-Service -Name sshd -StartupType Automatic -Status Running. Règle PowerShell comme shell par défaut (valeur DefaultShell de la clé HKLM:\SOFTWARE\OpenSSH), puis, dans l'inventaire :
ansible_connection: ssh
ansible_shell_type: powershell
Pour un compte administrateur, la clé publique se dépose dans C:\ProgramData\ssh\administrators_authorized_keys, pas dans le profil de l'utilisateur. Limite à connaître : avec une authentification par clé, la session n'a pas d'identité réseau (problème du « double saut ») et ne peut pas accéder à un partage d'un autre serveur.
Le premier contact
ansible windows -m ansible.windows.win_ping
srv-ad | SUCCESS => {
"changed": false,
"ping": "pong"
}
srv-ad2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
srv-fichiers | SUCCESS => {
"changed": false,
"ping": "pong"
}
Les erreurs les plus fréquentes :
| Message (extrait) | Cause probable |
|---|---|
winrm or requests is not installed | pywinrm absent de l'environnement d'Ansible : pipx inject ansible-core pywinrm |
Server not found in Kerberos database | Connexion par adresse IP, ou nom DNS qui ne correspond pas au compte d'ordinateur |
Clock skew too great | Plus de cinq minutes d'écart entre le bastion et le contrôleur de domaine |
certificate verify failed | Certificat auto-signé ou d'une AC que le bastion ne connaît pas : installer le certificat de l'AC interne sur le bastion, ou ansible_winrm_ca_trust_path |
connect timeout ou Connection refused sur 5986 | Écouteur HTTPS absent, ou flux bloqué par le pare-feu Windows ou réseau |
the specified credentials were rejected by the server | Mauvais mot de passe, compte sans droits d'administration, ou royaume mal écrit |
Les modules Windows à connaître
| Besoin | Module |
|---|---|
| Rôles et fonctionnalités (AD DS, DHCP, serveur de fichiers) | ansible.windows.win_feature |
| Services | ansible.windows.win_service |
| Mises à jour Windows | ansible.windows.win_updates |
| Redémarrer et attendre le retour | ansible.windows.win_reboot |
| Installer un logiciel (MSI, EXE) | ansible.windows.win_package |
| Copier un fichier, générer un fichier depuis un modèle | ansible.windows.win_copy, ansible.windows.win_template |
| Dossiers et fichiers | ansible.windows.win_file |
| Partages SMB | ansible.windows.win_share |
| Droits NTFS | ansible.windows.win_acl |
| Registre | ansible.windows.win_regedit |
| Pare-feu Windows | ansible.windows.win_firewall_rule |
| Comptes et groupes locaux | ansible.windows.win_user, ansible.windows.win_group, ansible.windows.win_group_membership |
| DNS (enregistrements, zones) | ansible.windows.win_dns_record, ansible.windows.win_dns_zone |
| DHCP (réservations, baux) | ansible.windows.win_dhcp_lease |
| Active Directory : OU, groupes, utilisateurs, ordinateurs, jonction au domaine | microsoft.ad.ou, microsoft.ad.group, microsoft.ad.user, microsoft.ad.computer, microsoft.ad.membership |
| Créer une forêt, promouvoir un contrôleur | microsoft.ad.domain, microsoft.ad.domain_controller |
| Exécuter du PowerShell | ansible.windows.win_powershell, ansible.windows.win_command, ansible.windows.win_shell |
Exercices
Exercice 1 sur 2 Les variables d'un laboratoire Réussi À revoir
Dans ton laboratoire, un serveur srv-test (192.168.56.20) n'est pas dans un domaine. Il a un compte administrateur local ansible-local et l'écouteur HTTPS avec un certificat auto-signé. Écris le fichier host_vars/srv-test.yml, en rangeant le mot de passe dans le coffre. Dis pourquoi cette configuration ne doit pas aller en production.
Voir la réponseMasquer la réponse de l'exercice 1
---
ansible_host: 192.168.56.20
ansible_connection: winrm
ansible_port: 5986
ansible_winrm_transport: ntlm
ansible_winrm_server_cert_validation: ignore
ansible_user: ansible-local
ansible_password: "{{ vault_mdp_ansible_local }}"
avec vault_mdp_ansible_local dans un fichier chiffré par Ansible Vault. Kerberos n'est pas possible hors domaine, d'où NTLM, qui passe dans le canal HTTPS. En production : NTLM est un protocole ancien et affaibli, que Microsoft abandonne progressivement ; cert_validation: ignore accepte n'importe quel certificat, donc ne protège pas d'une interception de l'homme du milieu. Dans un domaine, on utilise Kerberos et un certificat de l'AC interne, que le bastion sait vérifier.
Tu avais trouvé ?
Exercice 2 sur 2 Diagnostiquer une connexion Réussi À revoir
Moussa ajoute srv-fichiers avec ansible_host: 192.168.10.178 et la configuration Kerberos du chapitre. win_ping échoue : « Server not found in Kerberos database ». Explique et corrige.
Voir la réponseMasquer la réponse de l'exercice 2
Kerberos délivre un ticket pour un nom de service. En se connectant par l'adresse IP, le client demande un ticket pour HTTP/192.168.10.178, qui n'existe pas dans l'annuaire, d'où l'erreur. Il faut ansible_host: srv-fichiers.ad.sorbier.example, et que le bastion résolve ce nom par le DNS de l'Active Directory. On vérifie avec getent hosts srv-fichiers.ad.sorbier.example sur le bastion, puis on relance ansible srv-fichiers -m ansible.windows.win_ping.
Tu avais trouvé ?