Introduction

La sauvegarde régulière des configurations réseau est une bonne pratique essentielle. Perdre la configuration d’un switch peut entraîner des heures de reconfiguration manuelle.

Cet article décrit une architecture Ansible fiable pour :

  • déployer une VM Windows depuis un template VMware
  • récupérer automatiquement son IP DHCP
  • établir la connexion WinRM
  • basculer vers une IP statique
  • reconstruire l’inventaire
  • poursuivre la configuration système

Explication rapide du workflow

  1. Déploiement de la VM
  2. Récupération IP DHCP via VMware Tools
  3. Connexion WinRM initiale
  4. Bascule IP statique
  5. Reconstruction inventaire
  6. Reconnexion WinRM
  7. Configuration finale

Prérequis

1
2
3
4
5
python3.11 -m venv venv
source venv/bin/activate
pip install ansible pyvmomi pywinrm requests-ntlm
ansible-galaxy collection install community.vmware
ansible-galaxy collection install ansible.windows

Architecture du projet

Arborescence recommandée :

ansible_vmware_winserver2/ ├── group_vars/ │ └── vault.yml ├── roles/ │ ├── deploy_vm_windows/ │ │ └── tasks/ │ │ ├── main.yml │ │ └── deploy_one_vm.yml │ ├── configure_network/ │ │ └── tasks/ │ │ └── main.yml │ └── baseline_windows/ │ └── tasks/ │ └── main.yml ├── vars/ │ └── vms.yml └── site.yml


Fichiers à modifier

vars/vms.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
vms:
  - name: SRV-TEST-2025
    template: TEMPLATE-WIN2022
    datacenter: DC01
    datastore: DATASTORE01
    network: VLAN_SERVERS
    ip: 192.168.1.10
    gateway: 192.168.1.1
    dns:
      - 192.168.1.254
      - 8.8.8.8

group_vars/vault.yml

1
2
3
4
vcenter_hostname: vcenter.domain.local
vcenter_username: [email protected]
vcenter_password: changeme
windows_admin_password: changeme

roles/configure_network/tasks/main.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- name: Configure static IP
  ansible.windows.win_shell: |
    $iface = Get-NetIPConfiguration |
      Where-Object { $_.IPv4DefaultGateway -ne $null } |
      Select-Object -First 1

    $ifIndex = $iface.InterfaceIndex

    Set-NetIPInterface -InterfaceIndex $ifIndex -Dhcp Disabled

    Get-NetIPAddress -InterfaceIndex $ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue |
      Remove-NetIPAddress -Confirm:$false

    New-NetIPAddress `
      -InterfaceIndex $ifIndex `
      -IPAddress "{{ vm_ip }}" `
      -PrefixLength 23 `
      -DefaultGateway "{{ vm_gateway }}"

    Set-DnsClientServerAddress `
      -InterfaceIndex $ifIndex `
      -ServerAddresses {{ vm_dns }}

    Set-NetConnectionProfile -InterfaceIndex $ifIndex -NetworkCategory Private
    Enable-PSRemoting -Force
  ignore_errors: true

site.yml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# PHASE 0 : Déploiement
- name: Deploy Windows VMs on VMware
  hosts: localhost
  gather_facts: no
  vars_files:
    - vars/vms.yml
    - group_vars/vault.yml
  roles:
    - deploy_vm_windows

# PHASE 0.5 : Récupération IP DHCP + inventaire dynamique
- name: Get DHCP IP and build inventory
  hosts: localhost
  gather_facts: no
  vars_files:
    - vars/vms.yml
    - group_vars/vault.yml
  tasks:
    - name: Wait for VMware Tools + retrieve IP
      community.vmware.vmware_guest_info:
        hostname: "{{ vcenter_hostname }}"
        username: "{{ vcenter_username }}"
        password: "{{ vcenter_password }}"
        validate_certs: false
        name: "{{ item.name }}"
        datacenter: "{{ item.datacenter }}"
      loop: "{{ vms }}"
      loop_control:
        loop_var: item
      register: vm_info
      until: >
        vm_info.instance is defined and
        vm_info.instance.ipv4 is defined and
        vm_info.instance.ipv4 != ''
      retries: 30
      delay: 15

    - name: Pause for Windows stabilization
      ansible.builtin.pause:
        seconds: 30

    - name: Build dynamic inventory (DHCP IP)
      ansible.builtin.add_host:
        name: "{{ item.item.name }}"
        groups: windows_vms
        ansible_host: "{{ item.instance.ipv4 }}"
        ansible_user: Administrator
        ansible_password: "{{ windows_admin_password }}"
        ansible_connection: winrm
        ansible_port: 5985
        ansible_winrm_transport: ntlm
        ansible_winrm_server_cert_validation: ignore
        vm_ip: "{{ item.item.ip }}"
        vm_gateway: "{{ item.item.gateway }}"
        vm_dns: "{{ item.item.dns | join(',') }}"
      loop: "{{ vm_info.results }}"

# PHASE 1 : WinRM DHCP
- name: Wait for WinRM (DHCP IP)
  hosts: windows_vms
  gather_facts: no
  tasks:
    - name: Wait for WinRM ready
      ansible.windows.win_ping:
      register: result
      until: result is success
      retries: 40
      delay: 10

# PHASE 2 : Changement IP
- name: Configure network (switch to static IP)
  hosts: windows_vms
  gather_facts: no
  roles:
    - configure_network

# PHASE 3 : Rebuild inventory
- name: Rebuild inventory with static IP
  hosts: localhost
  gather_facts: no
  vars_files:
    - vars/vms.yml
    - group_vars/vault.yml
  tasks:
    - name: Update hosts with static IP
      ansible.builtin.add_host:
        name: "{{ item.name }}"
        groups: windows_vms
        ansible_host: "{{ item.ip }}"
        ansible_user: Administrator
        ansible_password: "{{ windows_admin_password }}"
        ansible_connection: winrm
        ansible_port: 5985
        ansible_winrm_transport: ntlm
        ansible_winrm_server_cert_validation: ignore
      loop: "{{ vms }}"

# PHASE 4 : WinRM statique
- name: Wait for WinRM (static IP)
  hosts: windows_vms
  gather_facts: no
  tasks:
    - name: Wait for WinRM after IP change
      ansible.windows.win_ping:
      register: result
      until: result is success
      retries: 40
      delay: 10

# PHASE 5 : Configuration finale
- name: Configure Windows (post network)
  hosts: windows_vms
  gather_facts: no
  roles:
    - baseline_windows