--- - name: PXE Server Setup (Ubuntu with dnsmasq) hosts: localhost connection: local become: yes gather_facts: yes pre_tasks: - name: "Verify required packages are installed (pre-installed from offline .debs)" command: dpkg -s {{ item }} loop: - dnsmasq - apache2 - samba - unzip - ufw - cron - ansible register: pkg_check failed_when: false changed_when: false - name: "Warn about missing packages" debug: msg: "WARNING: {{ item.item }} is not installed! Install offline .debs first." loop: "{{ pkg_check.results }}" when: item.rc != 0 vars: tftp_dir: "/srv/tftp" web_root: "/var/www/html" samba_share: "/srv/samba/winpeapps" usb_mount: "/mnt/usb/playbook" # where your USB is mounted image_types: - geastandardpbr - geaengineerpbr - geashopfloorpbr - gestandardlegacy - geengineerlegacy - geshopfloorlegacy deploy_subdirs: - Applications - Control - "Operating Systems" - "Out-of-box Drivers" - Packages - Tools tasks: - name: "Gather minimal network facts" ansible.builtin.setup: filter: - ansible_interfaces - ansible_default_ipv4 - name: "Bring up all ethernet-like interfaces" command: ip link set dev {{ item }} up loop: "{{ ansible_interfaces | select('match','^e(th|n)') | list }}" ignore_errors: yes - name: "Determine PXE interface" set_fact: pxe_iface: >- {{ (ansible_interfaces | select('match','^e(th|n)') | reject('equalto','lo') | reject('equalto', ansible_default_ipv4.interface) | list ) | first | default(ansible_default_ipv4.interface) }} - name: "Debug: final pxe_iface choice" debug: msg: "Using {{ pxe_iface }} for DHCP/TFTP" - name: "Configure dnsmasq for DHCP and TFTP" copy: dest: /etc/dnsmasq.conf backup: yes content: | port=0 interface={{ pxe_iface }} bind-interfaces dhcp-range=10.9.100.10,10.9.100.100,12h dhcp-option=3,10.9.100.1 dhcp-option=6,8.8.8.8 enable-tftp tftp-root={{ tftp_dir }} dhcp-boot=ipxe.efi - name: "Create TFTP directory" file: path: "{{ tftp_dir }}" state: directory mode: '0755' owner: nobody group: nogroup - name: "Create Win11 directory structure" file: path: "{{ web_root }}/win11/{{ item }}" state: directory mode: '0755' loop: - "EFI/Boot" - "EFI/Microsoft/Boot" - "Boot" - "sources" - name: "Create Altiris iPXE directory" file: path: "{{ web_root }}/Altiris/iPXE" state: directory mode: '0755' - name: "Create GetPxeScript.aspx" copy: dest: "{{ web_root }}/Altiris/iPXE/GetPxeScript.aspx" backup: yes content: | #!ipxe set server 10.9.100.1 kernel http://${server}/win11/wimboot gui initrd http://${server}/win11/EFI/Microsoft/Boot/boot.stl EFI/Microsoft/Boot/Boot.stl initrd http://${server}/win11/EFI/Microsoft/Boot/BCD EFI/Microsoft/Boot/BCD initrd http://${server}/win11/EFI/Boot/bootx64.efi EFI/Boot/bootx64.efi initrd http://${server}/win11/Boot/boot.sdi Boot/boot.sdi initrd http://${server}/win11/sources/boot.wim sources/boot.wim boot - name: "Ensure Apache listens on port 4433" lineinfile: path: /etc/apache2/ports.conf line: "Listen 4433" backup: yes state: present - name: "Create VirtualHost for Altiris iPXE on 4433" copy: dest: /etc/apache2/sites-available/altiris-ipxe.conf backup: yes content: | DocumentRoot {{ web_root }} Options Indexes FollowSymLinks AllowOverride None Require all granted AddType text/plain .aspx - name: "Enable Altiris iPXE site" command: a2ensite altiris-ipxe.conf args: creates: /etc/apache2/sites-enabled/altiris-ipxe.conf - name: "Reload Apache to apply changes" systemd: name: apache2 state: reloaded - name: "Create Samba share root" file: path: "{{ samba_share }}" state: directory mode: '0777' - name: "Configure Samba share" blockinfile: path: /etc/samba/smb.conf backup: yes block: | [winpeapps] path = {{ samba_share }} browseable = yes read only = no guest ok = yes - name: "Create image-type top-level directories" file: path: "{{ samba_share }}/{{ item }}" state: directory mode: '0777' loop: "{{ image_types }}" - name: "Create Deploy subdirectories for each image type" file: path: "{{ samba_share }}/{{ item.0 }}/Deploy/{{ item.1 }}" state: directory mode: '0777' with_nested: - "{{ image_types }}" - "{{ deploy_subdirs }}" - name: "Copy WinPE & boot files from USB" copy: src: "{{ usb_mount }}/{{ item.src }}" dest: "{{ web_root }}/win11/{{ item.dest }}" mode: '0644' loop: - { src: "wimboot", dest: "wimboot" } - { src: "boot.stl", dest: "EFI/Microsoft/Boot/boot.stl" } - { src: "BCD", dest: "EFI/Microsoft/Boot/BCD" } - { src: "bootx64.efi", dest: "EFI/Boot/bootx64.efi" } - { src: "boot.sdi", dest: "Boot/boot.sdi" } - { src: "boot.wim", dest: "sources/boot.wim" } - name: "Copy iPXE binaries from USB" copy: src: "{{ usb_mount }}/{{ item }}" dest: "{{ tftp_dir }}/{{ item }}" mode: '0755' loop: - ipxe.efi - name: "Restart and enable services" systemd: name: "{{ item }}" state: restarted enabled: yes loop: - dnsmasq - apache2 - smbd - name: "Allow necessary firewall ports (UFW)" ufw: rule: allow port: "{{ item }}" proto: "{{ 'udp' if item in ['67','69'] else 'tcp' }}" loop: - 67 - 69 - 80 - 4433 - 445 - name: "Enable UFW firewall" ufw: state: enabled policy: allow - name: "Schedule dnsmasq restart 15s after reboot" cron: name: "Restart dnsmasq after reboot" user: root special_time: "reboot" job: "/bin/sleep 15 && /usr/bin/systemctl restart dnsmasq.service" - name: "Configure static IP for PXE interface" copy: dest: /etc/netplan/50-cloud-init.yaml backup: yes content: | network: version: 2 renderer: networkd ethernets: {{ pxe_iface }}: dhcp4: no addresses: [10.9.100.1/24] notify: "Apply netplan" handlers: - name: "Apply netplan" command: netplan apply