Ansible for Windows: WinRM & Automation
Piloter Windows sans agent SSH : Configuration WinRM et modules natifs.
Le Défi de la Connexion : WinRM, pas SSH
Pourquoi WinRM ?
Sur Linux : Ansible utilise SSH (protocole standard, omniprésent).
Sur Windows : SSH n'est pas le standard natif. Windows utilise WinRM (Windows Remote Management).
┌──────────────────┐ WinRM (5985/5986) ┌──────────────────┐
│ Ansible Control │ ──────────────────────────────────────► │ Windows Host │
│ (Linux) │ HTTP/HTTPS │ (WinRM enabled)│
└──────────────────┘ └──────────────────┘
| Aspect | SSH (Linux) | WinRM (Windows) |
|---|---|---|
| Port | 22 | 5985 (HTTP), 5986 (HTTPS) |
| Transport | SSH | HTTP/HTTPS |
| Authentification | Clés SSH, mot de passe | NTLM, Kerberos, CredSSP, Certificate |
| Setup | Natif | Nécessite configuration |
Prérequis : PowerShell Remoting Activé
WinRM n'est pas activé par défaut sur Windows. Il faut le configurer manuellement.
Setup WinRM : Script de Configuration
Script PowerShell Standard Ansible
Fichier : ConfigureRemotingForAnsible.ps1
Ce script officiel d'Ansible configure WinRM pour la gestion à distance.
# ConfigureRemotingForAnsible.ps1
# Source: https://github.com/ansible/ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1
# Télécharger et exécuter le script officiel
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"
$file = "$env:temp\ConfigureRemotingForAnsible.ps1"
(New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)
powershell.exe -ExecutionPolicy ByPass -File $file
Ou manuellement :
# Exécuter en tant qu'Administrateur
# Activer PowerShell Remoting
Enable-PSRemoting -Force
# Configurer WinRM
winrm quickconfig -quiet
# Autoriser l'authentification Basic (NTLM)
Set-Item -Path WSMan:\localhost\Service\Auth\Basic -Value $true
# Autoriser les connexions non chiffrées (LAB ONLY !)
Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $true
# Créer un listener HTTP
New-NetFirewallRule -DisplayName "WinRM HTTP" `
-Direction Inbound `
-LocalPort 5985 `
-Protocol TCP `
-Action Allow
# Vérifier la configuration
winrm get winrm/config
# Tester localement
Test-WSMan -ComputerName localhost
Sécurité : Production vs Lab
Pour un LAB : Le script ci-dessus fonctionne (HTTP non chiffré).
Pour la PRODUCTION :
- ✅ Utilisez HTTPS (port 5986) avec un certificat valide
- ✅ Utilisez Kerberos (Active Directory)
- ✅ Désactivez AllowUnencrypted
- ✅ Activez CertificateThumbprintAuthentication si possible
Configuration HTTPS (Production) :
# Générer un certificat self-signed (ou utiliser un CA)
$cert = New-SelfSignedCertificate -DnsName "winhost.example.com" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddYears(5)
# Créer un listener HTTPS
New-Item -Path WSMan:\localhost\Listener `
-Transport HTTPS `
-Address * `
-CertificateThumbPrint $cert.Thumbprint -Force
# Firewall pour HTTPS
New-NetFirewallRule -DisplayName "WinRM HTTPS" `
-Direction Inbound `
-LocalPort 5986 `
-Protocol TCP `
-Action Allow
Configuration Inventaire Ansible
Variables Obligatoires (Côté Linux)
Fichier : inventory/windows_hosts.yml
---
all:
children:
windows:
hosts:
win01.example.com:
ansible_host: 192.168.1.100
win02.example.com:
ansible_host: 192.168.1.101
vars:
# === CONNEXION ===
ansible_connection: winrm # Utiliser WinRM au lieu de SSH
# === AUTHENTIFICATION ===
ansible_user: Administrator # Ou utilisateur local/domaine
ansible_password: "{{ vault_win_password }}" # Utiliser Ansible Vault !
# === TRANSPORT ===
ansible_winrm_transport: ntlm # Options : ntlm, kerberos, credssp, certificate
# Port WinRM
ansible_port: 5985 # HTTP (lab)
# ansible_port: 5986 # HTTPS (production)
# === VALIDATION CERTIFICAT ===
ansible_winrm_server_cert_validation: ignore # LAB ONLY !
# ansible_winrm_server_cert_validation: validate # Production
# === TIMEOUT ===
ansible_winrm_connection_timeout: 60
ansible_winrm_operation_timeout: 60
ansible_winrm_read_timeout: 70
Configuration pour Active Directory (Kerberos)
Prérequis : Installer pywinrm[kerberos] sur le contrôleur Ansible.
# Sur le contrôleur Linux
pip install "pywinrm[kerberos]"
# Installer krb5
sudo apt install krb5-user # Debian/Ubuntu
sudo yum install krb5-workstation # RHEL/CentOS
Fichier : /etc/krb5.conf
[libdefaults]
default_realm = EXAMPLE.COM
dns_lookup_realm = false
dns_lookup_kdc = true
[realms]
EXAMPLE.COM = {
kdc = dc01.example.com
admin_server = dc01.example.com
}
[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM
Inventaire avec Kerberos :
---
all:
children:
windows_domain:
hosts:
win-server.example.com:
vars:
ansible_connection: winrm
ansible_user: Administrator@EXAMPLE.COM # Format UPN
ansible_password: "{{ vault_ad_password }}"
ansible_winrm_transport: kerberos # Kerberos (AD)
ansible_port: 5986 # HTTPS obligatoire
ansible_winrm_server_cert_validation: validate
Tester la Connexion
# Ping Windows depuis Ansible
ansible windows -i inventory/windows_hosts.yml -m win_ping
# Output attendu :
# win01.example.com | SUCCESS => {
# "changed": false,
# "ping": "pong"
# }
# Test avec variable (gather_facts)
ansible windows -i inventory/windows_hosts.yml -m setup
# Test avec commande PowerShell
ansible windows -i inventory/windows_hosts.yml \
-m win_shell -a "Get-ComputerInfo | Select-Object CsName, OsName"
Les Modules win_ Indispensables
Ne PAS utiliser les modules Linux !
- ❌
file→ ✅win_file - ❌
copy→ ✅win_copy - ❌
shell→ ✅win_shell - ❌
service→ ✅win_service - ❌
apt/yum→ ✅win_package/win_chocolatey
win_feature : Gérer les Rôles Windows
Installer IIS (Web Server) :
---
- name: Install IIS Web Server
hosts: windows
tasks:
- name: Install IIS feature
ansible.windows.win_feature:
name: Web-Server
state: present
include_management_tools: yes
register: iis_install
- name: Reboot if required
ansible.windows.win_reboot:
msg: "Reboot initiated by Ansible for IIS installation"
when: iis_install.reboot_required
Installer Remote Server Administration Tools (RSAT) :
---
- name: Install RSAT tools
hosts: windows
tasks:
- name: Install RSAT AD DS tools
ansible.windows.win_feature:
name:
- RSAT-AD-Tools
- RSAT-AD-PowerShell
- RSAT-ADDS
state: present
include_management_tools: yes
Lister les features disponibles :
---
- name: List all Windows features
hosts: windows
tasks:
- name: Get all features
ansible.windows.win_shell: Get-WindowsFeature | Where-Object {$_.InstallState -eq "Available"}
register: features
- name: Display features
ansible.builtin.debug:
var: features.stdout_lines
win_service : Gérer les Services
---
- name: Manage Windows services
hosts: windows
tasks:
# Démarrer un service
- name: Ensure W3SVC (IIS) is running
ansible.windows.win_service:
name: W3SVC
state: started
start_mode: auto
# Arrêter un service
- name: Stop Windows Update service
ansible.windows.win_service:
name: wuauserv
state: stopped
start_mode: disabled
# Redémarrer un service
- name: Restart DNS Client
ansible.windows.win_service:
name: Dnscache
state: restarted
# Vérifier l'état d'un service
- name: Check service status
ansible.windows.win_service_info:
name: W3SVC
register: service_info
- name: Display service info
ansible.builtin.debug:
msg: "Service {{ service_info.services[0].display_name }} is {{ service_info.services[0].state }}"
win_package : Installer des Logiciels
---
- name: Install software via MSI
hosts: windows
tasks:
# Installer depuis un MSI local
- name: Install 7-Zip
ansible.windows.win_package:
path: C:\Temp\7z-x64.msi
state: present
arguments: /quiet /norestart
# Installer depuis une URL
- name: Install Notepad++
ansible.windows.win_package:
path: https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v8.5.4/npp.8.5.4.Installer.x64.exe
product_id: Notepad++
arguments: /S
state: present
# Désinstaller
- name: Uninstall software
ansible.windows.win_package:
product_id: '{GUID-OF-PRODUCT}'
state: absent
win_chocolatey : Package Manager pour Windows
Chocolatey : Le APT de Windows
Chocolatey est un package manager pour Windows. Beaucoup plus simple que MSI !
Installer Chocolatey d'abord :
---
- name: Setup Chocolatey
hosts: windows
tasks:
- name: Install Chocolatey
ansible.windows.win_chocolatey:
name: chocolatey
state: present
Installer des packages :
---
- name: Install packages with Chocolatey
hosts: windows
tasks:
- name: Install common tools
ansible.windows.win_chocolatey:
name:
- git
- vscode
- googlechrome
- 7zip
- python
state: present
- name: Install specific version
ansible.windows.win_chocolatey:
name: nodejs
version: 18.17.1
state: present
- name: Upgrade all packages
ansible.windows.win_chocolatey:
name: all
state: latest
win_file / win_copy : Gestion de Fichiers
---
- name: File management
hosts: windows
tasks:
# Créer un dossier
- name: Create directory
ansible.windows.win_file:
path: C:\MyApp\Config
state: directory
# Copier un fichier depuis le contrôleur
- name: Copy configuration file
ansible.windows.win_copy:
src: files/config.ini
dest: C:\MyApp\Config\config.ini
# Copier un dossier complet
- name: Copy entire directory
ansible.windows.win_copy:
src: files/webapp/
dest: C:\inetpub\wwwroot\
remote_src: no
# Supprimer un fichier
- name: Delete file
ansible.windows.win_file:
path: C:\Temp\old_file.txt
state: absent
# Créer un lien symbolique
- name: Create symlink
ansible.windows.win_file:
src: C:\MyApp\Data
dest: C:\Data
state: link
PowerShell & Ansible
win_shell vs win_command
| Module | Usage | Quand l'utiliser |
|---|---|---|
win_command |
Exécute une commande (pas de shell) | Binaires directs (exe, cmd) |
win_shell |
Exécute via PowerShell | Scripts PS, pipes, variables |
---
- name: Command vs Shell
hosts: windows
tasks:
# win_command : Simple, pas de shell
- name: Run executable
ansible.windows.win_command: ipconfig /all
register: ipconfig
# win_shell : PowerShell complet
- name: Run PowerShell command
ansible.windows.win_shell: |
Get-Process | Where-Object {$_.CPU -gt 100} | Select-Object Name, CPU
register: high_cpu_processes
- name: Display results
ansible.builtin.debug:
var: high_cpu_processes.stdout_lines
Exécuter un Script PowerShell Complexe
Scénario : Créer un utilisateur Active Directory avec Ansible.
---
- name: Create AD User
hosts: domain_controller
tasks:
- name: Create AD user with PowerShell
ansible.windows.win_shell: |
Import-Module ActiveDirectory
$username = "{{ ad_username }}"
$password = ConvertTo-SecureString "{{ ad_password }}" -AsPlainText -Force
$ou = "OU=Users,OU=IT,DC=example,DC=com"
# Vérifier si l'utilisateur existe
$user = Get-ADUser -Filter "SamAccountName -eq '$username'" -ErrorAction SilentlyContinue
if (-not $user) {
New-ADUser -Name $username `
-GivenName "{{ first_name }}" `
-Surname "{{ last_name }}" `
-SamAccountName $username `
-UserPrincipalName "$username@example.com" `
-Path $ou `
-AccountPassword $password `
-Enabled $true `
-ChangePasswordAtLogon $false `
-PasswordNeverExpires $true
Write-Output "User $username created successfully"
} else {
Write-Output "User $username already exists"
}
register: ad_user_creation
- name: Display result
ansible.builtin.debug:
var: ad_user_creation.stdout_lines
Avec un fichier de script externe :
---
- name: Run external PowerShell script
hosts: windows
tasks:
- name: Copy PowerShell script
ansible.windows.win_copy:
src: scripts/CreateADUser.ps1
dest: C:\Temp\CreateADUser.ps1
- name: Execute script
ansible.windows.win_shell: |
C:\Temp\CreateADUser.ps1 -Username "{{ username }}" -Password "{{ password }}"
register: script_output
- name: Show output
ansible.builtin.debug:
var: script_output.stdout
Cas d'Usage "Production"
Scénario 1 : Déployer IIS avec Page Custom
Playbook complet :
---
- name: Deploy IIS Web Server with custom page
hosts: webservers
vars:
website_name: "MyWebsite"
website_path: "C:\\inetpub\\wwwroot\\{{ website_name }}"
website_port: 80
tasks:
# Étape 1 : Installer IIS
- name: Install IIS features
ansible.windows.win_feature:
name:
- Web-Server
- Web-Mgmt-Console
- Web-Static-Content
- Web-Default-Doc
state: present
include_management_tools: yes
register: iis_install
# Étape 2 : Reboot si nécessaire
- name: Reboot after IIS installation
ansible.windows.win_reboot:
msg: "Reboot for IIS"
when: iis_install.reboot_required
# Étape 3 : Créer le dossier du site
- name: Create website directory
ansible.windows.win_file:
path: "{{ website_path }}"
state: directory
# Étape 4 : Déployer le contenu
- name: Copy index.html
ansible.windows.win_copy:
content: |
<!DOCTYPE html>
<html>
<head>
<title>{{ website_name }}</title>
</head>
<body>
<h1>Welcome to {{ website_name }}</h1>
<p>Deployed by Ansible on {{ ansible_date_time.iso8601 }}</p>
</body>
</html>
dest: "{{ website_path }}\\index.html"
# Étape 5 : Créer le site IIS
- name: Create IIS website
community.windows.win_iis_website:
name: "{{ website_name }}"
state: started
physical_path: "{{ website_path }}"
port: "{{ website_port }}"
# Étape 6 : Configurer le firewall
- name: Allow HTTP traffic
community.windows.win_firewall_rule:
name: "Allow HTTP"
localport: "{{ website_port }}"
action: allow
direction: in
protocol: tcp
state: present
enabled: yes
# Étape 7 : Vérifier le service
- name: Ensure IIS service is running
ansible.windows.win_service:
name: W3SVC
state: started
start_mode: auto
# Étape 8 : Test HTTP
- name: Test website
ansible.windows.win_uri:
url: "http://localhost:{{ website_port }}"
return_content: yes
register: website_test
- name: Display website content
ansible.builtin.debug:
msg: "Website is accessible: {{ website_test.status_code }}"
Scénario 2 : Joindre une Machine au Domaine AD
Crucial pour l'entreprise !
---
- name: Join Windows server to Active Directory domain
hosts: new_servers
vars:
domain_name: "example.com"
domain_admin_user: "Administrator@{{ domain_name }}"
domain_admin_password: "{{ vault_domain_admin_password }}"
domain_ou: "OU=Servers,OU=IT,DC=example,DC=com"
tasks:
# Étape 1 : Configurer DNS
- name: Set DNS server to domain controller
ansible.windows.win_dns_client:
adapter_names: '*'
dns_servers:
- 192.168.1.10 # IP du DC
# Étape 2 : Joindre le domaine
- name: Join domain
ansible.windows.win_domain_membership:
dns_domain_name: "{{ domain_name }}"
domain_admin_user: "{{ domain_admin_user }}"
domain_admin_password: "{{ domain_admin_password }}"
domain_ou_path: "{{ domain_ou }}"
state: domain
register: domain_join
# Étape 3 : Reboot (obligatoire)
- name: Reboot after domain join
ansible.windows.win_reboot:
msg: "Reboot for domain join"
pre_reboot_delay: 15
when: domain_join.reboot_required
# Étape 4 : Vérifier l'appartenance au domaine
- name: Verify domain membership
ansible.windows.win_shell: |
(Get-WmiObject -Class Win32_ComputerSystem).Domain
register: domain_check
- name: Display domain
ansible.builtin.debug:
msg: "Server is now member of: {{ domain_check.stdout | trim }}"
Scénario 3 : Windows Updates (Le Cauchemar Géré)
---
- name: Manage Windows Updates
hosts: windows
tasks:
# Rechercher les mises à jour
- name: Search for Windows updates
ansible.windows.win_updates:
category_names:
- SecurityUpdates
- CriticalUpdates
state: searched
register: updates_available
- name: Display updates count
ansible.builtin.debug:
msg: "{{ updates_available.found_update_count }} updates available"
# Installer les mises à jour critiques
- name: Install critical updates
ansible.windows.win_updates:
category_names:
- SecurityUpdates
- CriticalUpdates
reboot: yes
reboot_timeout: 3600
register: updates_result
- name: Display updates installed
ansible.builtin.debug:
msg: "{{ updates_result.installed_update_count }} updates installed"
# Installer TOUTES les mises à jour (attention !)
- name: Install all updates (use with caution)
ansible.windows.win_updates:
category_names:
- '*'
state: installed
reboot: yes
when: install_all_updates | default(false)
# Blacklist certaines mises à jour
- name: Install updates except blacklisted
ansible.windows.win_updates:
category_names:
- SecurityUpdates
reject_list:
- KB4056892 # Exemple de KB problématique
reboot: yes
Référence Rapide
Comparaison Linux vs Windows
| Opération | Linux (Module) | Windows (Module) |
|---|---|---|
| Installer package | apt, yum |
win_chocolatey, win_package |
| Gérer service | service, systemd |
win_service |
| Copier fichier | copy |
win_copy |
| Créer dossier | file |
win_file |
| Exécuter commande | shell, command |
win_shell, win_command |
| Reboot | reboot |
win_reboot |
| Mises à jour | apt upgrade, yum update |
win_updates |
| Utilisateurs | user |
win_user |
| Groupes | group |
win_group |
| Firewall | ufw, firewalld |
win_firewall_rule |
| Cron / Tasks | cron |
win_scheduled_task |
Modules Windows Essentiels
| Module | Usage |
|---|---|
win_ping |
Tester la connexion |
win_feature |
Gérer les rôles/features Windows |
win_service |
Gérer les services |
win_package |
Installer/désinstaller software |
win_chocolatey |
Package manager Chocolatey |
win_copy |
Copier des fichiers |
win_file |
Gérer fichiers/dossiers |
win_shell |
Exécuter PowerShell |
win_command |
Exécuter une commande |
win_reboot |
Redémarrer |
win_updates |
Gérer Windows Updates |
win_user |
Gérer utilisateurs locaux |
win_domain_membership |
Joindre/quitter domaine AD |
win_iis_website |
Gérer sites IIS |
win_firewall_rule |
Gérer règles firewall |
win_scheduled_task |
Gérer tâches planifiées |
Collections Windows
# Installer les collections Windows
ansible-galaxy collection install ansible.windows
ansible-galaxy collection install community.windows
# Vérifier
ansible-galaxy collection list | grep windows
Ressources Complémentaires
- Ansible Windows Docs : https://docs.ansible.com/ansible/latest/os_guide/windows.html
- WinRM Setup Script : https://github.com/ansible/ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1
- PyWinRM Documentation : https://github.com/diyan/pywinrm
- Chocolatey Packages : https://community.chocolatey.org/packages
Intégration avec les autres guides
Combinez avec :
- PowerShell Foundations - Maîtriser PowerShell pour les scripts complexes
- Active Directory - Gérer AD avec PowerShell (peut être piloté par Ansible)
- Windows Security - Hardening Windows avec Ansible
- Ansible Advanced Patterns - block/rescue, serial, performance
Parcours Recommandé
Nouveau sur Ansible Windows ?
- Configurer WinRM sur une VM de test
- Tester
win_pingdepuis Ansible - Installer IIS avec
win_feature - Déployer un site web simple
- Joindre au domaine AD
- Gérer Windows Updates
Projet réel : Déployer une ferme IIS (3+ serveurs) avec load balancer, Active Directory, et monitoring.