Initial project setup: automated PXE server provisioning
Reorganized from OneDrive export into a clean project structure: - autoinstall/: cloud-init user-data and meta-data for Ubuntu 24.04 autoinstall - playbook/: Ansible playbook for PXE server config (dnsmasq, Apache, Samba, iPXE) - unattend/: Windows unattend.xml sample for image deployment - build-usb.sh: builds a bootable USB with Ubuntu installer + CIDATA partition - download-packages.sh: downloads all offline .deb dependencies via Docker Key improvements over original: - Fully air-gapped: all packages bundled offline, no WiFi needed - Hardware-agnostic network config (wildcard NIC matching) - Removed plaintext WiFi credentials - Single USB build process (was 15+ manual steps) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Large binary files — download/build these, don't commit them
|
||||
*.deb
|
||||
*.zip
|
||||
*.wim
|
||||
*.iso
|
||||
*.efi
|
||||
*.sdi
|
||||
|
||||
# OneDrive download artifacts
|
||||
OneDrive_*/
|
||||
|
||||
# Error folders from OneDrive download
|
||||
__*/
|
||||
___*.txt
|
||||
|
||||
# Original OneDrive folder structure (reorganized into autoinstall/ and playbook/)
|
||||
WestJeff*/
|
||||
|
||||
# Duplicate at root (canonical copy in unattend/)
|
||||
/FlatUnattendW10.xml
|
||||
|
||||
# Offline packages (built by download-packages.sh)
|
||||
offline-packages/
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Secrets
|
||||
secrets.md
|
||||
0
autoinstall/meta-data
Normal file
0
autoinstall/meta-data
Normal file
106
autoinstall/user-data
Normal file
106
autoinstall/user-data
Normal file
@@ -0,0 +1,106 @@
|
||||
#cloud-config
|
||||
autoinstall:
|
||||
version: 1
|
||||
|
||||
# Locale, keyboard, timezone
|
||||
locale: en_US.UTF-8
|
||||
keyboard:
|
||||
layout: us
|
||||
variant: ""
|
||||
timezone: America/New_York
|
||||
|
||||
# Network configuration
|
||||
# Uses a broad match so any wired NIC gets the static PXE address.
|
||||
# No WiFi needed — all packages are on the CIDATA partition.
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
any-eth:
|
||||
match:
|
||||
name: "en*"
|
||||
addresses:
|
||||
- 10.9.100.1/24
|
||||
dhcp4: false
|
||||
dhcp6: false
|
||||
optional: true
|
||||
|
||||
# Storage configuration
|
||||
storage:
|
||||
layout:
|
||||
name: lvm
|
||||
match:
|
||||
size: largest
|
||||
swap:
|
||||
size: 0
|
||||
|
||||
# User identity
|
||||
identity:
|
||||
hostname: pxeserver
|
||||
username: pxe
|
||||
password: "$6$rounds=656000$TpsuBw0N85085mpx$KtKsCwFlowg4NY41gUqx5ljef8cJ8uPFfgg43MyCPWByfXkhM5XushcdtkNps6lKeQFQZtli/QU.s52AUc7XC."
|
||||
|
||||
# Installer-stage late commands
|
||||
late-commands:
|
||||
# Install deb packages from CIDATA USB
|
||||
- |
|
||||
curtin in-target --target=/target -- bash -c '
|
||||
mkdir -p /mnt/cidata
|
||||
CIDATA_DEV=$(blkid -L CIDATA)
|
||||
if [ -n "$CIDATA_DEV" ]; then
|
||||
mount "$CIDATA_DEV" /mnt/cidata
|
||||
if compgen -G "/mnt/cidata/packages/*.deb" > /dev/null; then
|
||||
cp /mnt/cidata/packages/*.deb /tmp/
|
||||
dpkg -i /tmp/*.deb 2>/dev/null || true
|
||||
dpkg -i /tmp/*.deb 2>/dev/null || true
|
||||
if command -v nmcli >/dev/null; then
|
||||
systemctl enable NetworkManager
|
||||
fi
|
||||
fi
|
||||
umount /mnt/cidata
|
||||
fi
|
||||
'
|
||||
|
||||
# Create first-boot.sh
|
||||
- |
|
||||
curtin in-target --target=/target -- bash -c '
|
||||
cat <<"EOF" > /opt/first-boot.sh
|
||||
#!/bin/bash
|
||||
CIDATA_DEV=$(blkid -L CIDATA)
|
||||
if [ -n "$CIDATA_DEV" ]; then
|
||||
mkdir -p /mnt/usb
|
||||
mount "$CIDATA_DEV" /mnt/usb
|
||||
# Install all offline .deb packages (ansible, dnsmasq, apache2, samba, etc.)
|
||||
if compgen -G "/mnt/usb/packages/*.deb" > /dev/null; then
|
||||
dpkg -i /mnt/usb/packages/*.deb 2>/dev/null || true
|
||||
dpkg -i /mnt/usb/packages/*.deb 2>/dev/null || true
|
||||
fi
|
||||
# Run the Ansible playbook
|
||||
if [ -f /mnt/usb/playbook/pxe_server_setup.yml ]; then
|
||||
cd /mnt/usb/playbook
|
||||
ansible-playbook -i localhost, -c local pxe_server_setup.yml
|
||||
fi
|
||||
umount /mnt/usb
|
||||
fi
|
||||
# Disable rc.local to prevent rerunning
|
||||
sed -i "s|^/opt/first-boot.sh.*|# &|" /etc/rc.local
|
||||
lvextend -r -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv || true
|
||||
EOF
|
||||
'
|
||||
- curtin in-target --target=/target -- chmod +x /opt/first-boot.sh
|
||||
|
||||
# Create rc.local without unintended indentation
|
||||
- |
|
||||
curtin in-target --target=/target -- bash -c '
|
||||
cat <<"EOF" > /etc/rc.local
|
||||
#!/bin/bash
|
||||
/opt/first-boot.sh > /var/log/first-boot.log 2>&1 &
|
||||
exit 0
|
||||
EOF
|
||||
'
|
||||
- curtin in-target --target=/target -- chmod +x /etc/rc.local
|
||||
|
||||
user-data:
|
||||
disable_root: false
|
||||
|
||||
refresh-installer:
|
||||
update: yes
|
||||
196
build-usb.sh
Executable file
196
build-usb.sh
Executable file
@@ -0,0 +1,196 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# build-usb.sh — Build a bootable PXE-server installer USB
|
||||
#
|
||||
# Creates a two-partition USB:
|
||||
# Partition 1: Ubuntu Server 24.04 installer (ISO contents)
|
||||
# Partition 2: CIDATA volume (autoinstall config, .debs, playbook)
|
||||
#
|
||||
# The target machine boots from this USB, Ubuntu auto-installs with
|
||||
# cloud-init (user-data/meta-data from CIDATA), installs offline .debs,
|
||||
# and on first boot runs the Ansible playbook to configure PXE services.
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./build-usb.sh /dev/sdX /path/to/ubuntu-24.04-live-server-amd64.iso
|
||||
#
|
||||
# WARNING: This will ERASE the target USB device.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
AUTOINSTALL_DIR="$SCRIPT_DIR/autoinstall"
|
||||
PLAYBOOK_DIR="$SCRIPT_DIR/playbook"
|
||||
OFFLINE_PKG_DIR="$SCRIPT_DIR/offline-packages"
|
||||
|
||||
# --- Validate arguments ---
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: sudo $0 /dev/sdX /path/to/ubuntu-24.04.iso"
|
||||
echo ""
|
||||
echo "Available removable devices:"
|
||||
lsblk -d -o NAME,SIZE,TRAN,RM | grep -E '^\S+\s+\S+\s+(usb)\s+1'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USB_DEV="$1"
|
||||
ISO_PATH="$2"
|
||||
|
||||
# Safety checks
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "ERROR: Must run as root (sudo)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -b "$USB_DEV" ]; then
|
||||
echo "ERROR: $USB_DEV is not a block device."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$ISO_PATH" ]; then
|
||||
echo "ERROR: ISO not found at $ISO_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify it's a removable device (safety against wiping system disks)
|
||||
REMOVABLE=$(lsblk -nd -o RM "$USB_DEV" 2>/dev/null || echo "0")
|
||||
if [ "$REMOVABLE" != "1" ]; then
|
||||
echo "WARNING: $USB_DEV does not appear to be a removable device."
|
||||
read -rp "Are you SURE you want to erase $USB_DEV? (type YES): " CONFIRM
|
||||
if [ "$CONFIRM" != "YES" ]; then
|
||||
echo "Aborted."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify required source files exist
|
||||
if [ ! -f "$AUTOINSTALL_DIR/user-data" ]; then
|
||||
echo "ERROR: user-data not found at $AUTOINSTALL_DIR/user-data"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$AUTOINSTALL_DIR/meta-data" ]; then
|
||||
echo "ERROR: meta-data not found at $AUTOINSTALL_DIR/meta-data"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$PLAYBOOK_DIR/pxe_server_setup.yml" ]; then
|
||||
echo "ERROR: pxe_server_setup.yml not found at $PLAYBOOK_DIR/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "============================================"
|
||||
echo "PXE Server USB Builder"
|
||||
echo "============================================"
|
||||
echo "USB Device : $USB_DEV"
|
||||
echo "ISO : $ISO_PATH"
|
||||
echo "Source Dir : $SCRIPT_DIR"
|
||||
echo ""
|
||||
echo "This will ERASE all data on $USB_DEV."
|
||||
read -rp "Continue? (y/N): " PROCEED
|
||||
if [[ ! "$PROCEED" =~ ^[Yy]$ ]]; then
|
||||
echo "Aborted."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Unmount any existing partitions ---
|
||||
echo ""
|
||||
echo "[1/6] Unmounting existing partitions on $USB_DEV..."
|
||||
for part in "${USB_DEV}"*; do
|
||||
umount "$part" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# --- Write ISO to USB ---
|
||||
echo "[2/6] Writing Ubuntu ISO to $USB_DEV (this may take several minutes)..."
|
||||
dd if="$ISO_PATH" of="$USB_DEV" bs=4M status=progress oflag=sync
|
||||
sync
|
||||
|
||||
# --- Find the end of the ISO to create CIDATA partition ---
|
||||
echo "[3/6] Creating CIDATA partition after ISO data..."
|
||||
|
||||
# Get ISO size in bytes and calculate the start sector for the new partition
|
||||
ISO_SIZE=$(stat -c%s "$ISO_PATH")
|
||||
SECTOR_SIZE=512
|
||||
# Start the CIDATA partition 1MB after the ISO ends (alignment)
|
||||
START_SECTOR=$(( (ISO_SIZE / SECTOR_SIZE) + 2048 ))
|
||||
|
||||
# Use sfdisk to append a new partition
|
||||
echo " ISO size: $((ISO_SIZE / 1024 / 1024)) MB"
|
||||
echo " CIDATA partition starts at sector $START_SECTOR"
|
||||
|
||||
# Add a new partition using sfdisk --append
|
||||
echo "${START_SECTOR},+,L" | sfdisk --append "$USB_DEV" --no-reread 2>/dev/null || true
|
||||
partprobe "$USB_DEV"
|
||||
sleep 2
|
||||
|
||||
# Determine the new partition name (could be sdX3, sdX4, etc.)
|
||||
CIDATA_PART=""
|
||||
for part in "${USB_DEV}"[0-9]*; do
|
||||
# Find the partition that starts at or after our start sector
|
||||
PART_START=$(sfdisk -d "$USB_DEV" 2>/dev/null | grep "$part" | grep -o 'start=[[:space:]]*[0-9]*' | grep -o '[0-9]*')
|
||||
if [ -n "$PART_START" ] && [ "$PART_START" -ge "$START_SECTOR" ]; then
|
||||
CIDATA_PART="$part"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Fallback: use the last partition
|
||||
if [ -z "$CIDATA_PART" ]; then
|
||||
CIDATA_PART=$(lsblk -ln -o NAME "$USB_DEV" | tail -1)
|
||||
CIDATA_PART="/dev/$CIDATA_PART"
|
||||
fi
|
||||
|
||||
echo " CIDATA partition: $CIDATA_PART"
|
||||
|
||||
# --- Format CIDATA partition ---
|
||||
echo "[4/6] Formatting $CIDATA_PART as FAT32 (label: CIDATA)..."
|
||||
mkfs.vfat -F 32 -n CIDATA "$CIDATA_PART"
|
||||
|
||||
# --- Mount and copy files ---
|
||||
echo "[5/6] Copying autoinstall config, packages, and playbook to CIDATA..."
|
||||
MOUNT_POINT=$(mktemp -d)
|
||||
mount "$CIDATA_PART" "$MOUNT_POINT"
|
||||
|
||||
# Copy cloud-init files
|
||||
cp "$AUTOINSTALL_DIR/user-data" "$MOUNT_POINT/"
|
||||
cp "$AUTOINSTALL_DIR/meta-data" "$MOUNT_POINT/"
|
||||
|
||||
# Copy offline .deb packages into packages/ subdirectory
|
||||
mkdir -p "$MOUNT_POINT/packages"
|
||||
DEB_COUNT=0
|
||||
|
||||
if [ -d "$OFFLINE_PKG_DIR" ]; then
|
||||
for deb in "$OFFLINE_PKG_DIR"/*.deb; do
|
||||
if [ -f "$deb" ]; then
|
||||
cp "$deb" "$MOUNT_POINT/packages/"
|
||||
DEB_COUNT=$((DEB_COUNT + 1))
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo " Copied $DEB_COUNT .deb packages to packages/"
|
||||
|
||||
# Copy playbook directory
|
||||
cp -r "$PLAYBOOK_DIR" "$MOUNT_POINT/playbook"
|
||||
echo " Copied playbook/"
|
||||
|
||||
# List what's on CIDATA
|
||||
echo ""
|
||||
echo " CIDATA contents:"
|
||||
ls -lh "$MOUNT_POINT/" | sed 's/^/ /'
|
||||
|
||||
# --- Cleanup ---
|
||||
echo ""
|
||||
echo "[6/6] Syncing and unmounting..."
|
||||
sync
|
||||
umount "$MOUNT_POINT"
|
||||
rmdir "$MOUNT_POINT"
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo "USB build complete!"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Insert USB into target machine"
|
||||
echo " 2. Boot from USB (F12 / boot menu)"
|
||||
echo " 3. Ubuntu will auto-install and configure the PXE server"
|
||||
echo " 4. After reboot, move the NIC to the isolated PXE network"
|
||||
echo ""
|
||||
83
download-packages.sh
Executable file
83
download-packages.sh
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# download-packages.sh — Download all .deb packages needed for offline PXE server setup
|
||||
#
|
||||
# Run this on a machine with internet access running Ubuntu 24.04 (Noble).
|
||||
# It downloads every .deb needed by the Ansible playbook into a local directory,
|
||||
# which then gets bundled onto the installer USB.
|
||||
#
|
||||
# Usage:
|
||||
# ./download-packages.sh [output_directory]
|
||||
#
|
||||
# Default output: ./offline-packages/
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
OUT_DIR="${1:-./offline-packages}"
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
# Packages installed by the Ansible playbook (pxe_server_setup.yml)
|
||||
PLAYBOOK_PACKAGES=(
|
||||
ansible
|
||||
dnsmasq
|
||||
apache2
|
||||
samba
|
||||
unzip
|
||||
ufw
|
||||
cron
|
||||
)
|
||||
|
||||
# Packages installed during autoinstall late-commands (NetworkManager, WiFi, etc.)
|
||||
# These are already in your ubuntu_playbook/*.deb files, but we can refresh them here too.
|
||||
AUTOINSTALL_PACKAGES=(
|
||||
network-manager
|
||||
wpasupplicant
|
||||
wireless-tools
|
||||
linux-firmware
|
||||
firmware-sof-signed
|
||||
)
|
||||
|
||||
ALL_PACKAGES=("${PLAYBOOK_PACKAGES[@]}" "${AUTOINSTALL_PACKAGES[@]}")
|
||||
|
||||
echo "============================================"
|
||||
echo "Offline Package Downloader"
|
||||
echo "============================================"
|
||||
echo "Output directory: $OUT_DIR"
|
||||
echo ""
|
||||
echo "Packages to resolve:"
|
||||
printf ' - %s\n' "${ALL_PACKAGES[@]}"
|
||||
echo ""
|
||||
|
||||
# Update package cache
|
||||
echo "[1/3] Updating package cache..."
|
||||
sudo apt-get update -qq
|
||||
|
||||
# Simulate install to find all dependencies
|
||||
echo "[2/3] Resolving dependencies..."
|
||||
DEPS=$(apt-get install --simulate "${ALL_PACKAGES[@]}" 2>&1 \
|
||||
| grep "^Inst " \
|
||||
| awk '{print $2}' \
|
||||
| sort -u)
|
||||
|
||||
DEP_COUNT=$(echo "$DEPS" | wc -l)
|
||||
echo " Found $DEP_COUNT packages (including dependencies)"
|
||||
|
||||
# Download all packages
|
||||
echo "[3/3] Downloading packages to $OUT_DIR..."
|
||||
cd "$OUT_DIR"
|
||||
apt-get download $DEPS 2>&1 | tail -5
|
||||
|
||||
DEB_COUNT=$(ls -1 *.deb 2>/dev/null | wc -l)
|
||||
TOTAL_SIZE=$(du -sh . | cut -f1)
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo "Download complete!"
|
||||
echo "============================================"
|
||||
echo " Packages: $DEB_COUNT"
|
||||
echo " Total size: $TOTAL_SIZE"
|
||||
echo " Location: $OUT_DIR/"
|
||||
echo ""
|
||||
echo "Next: copy these into your ubuntu_playbook/ directory"
|
||||
echo " cp $OUT_DIR/*.deb /path/to/ubuntu_playbook/"
|
||||
echo ""
|
||||
2
playbook/inventory.ini
Normal file
2
playbook/inventory.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
[pxe_servers]
|
||||
localhost ansible_connection=local
|
||||
272
playbook/pxe_server_setup.yml
Normal file
272
playbook/pxe_server_setup.yml
Normal file
@@ -0,0 +1,272 @@
|
||||
---
|
||||
- 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: |
|
||||
<VirtualHost *:4433>
|
||||
DocumentRoot {{ web_root }}
|
||||
<Directory "{{ web_root }}/Altiris/iPXE">
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
AddType text/plain .aspx
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
- 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
|
||||
129
setup-guide-original.txt
Normal file
129
setup-guide-original.txt
Normal file
@@ -0,0 +1,129 @@
|
||||
Purpose
|
||||
Document a repeatable, “build-from-scratch” procedure for deploying an Ubuntu-based PXE boot server that can host GE Aerospace Windows PE images.
|
||||
|
||||
Prerequisites
|
||||
Hardware: Server or PC with ≥ 8 GB RAM, ≥ 250 GB disk, and one NIC (one for build / Internet, one for isolated PXE LAN)
|
||||
|
||||
https://myaccess.microsoft.us/@ge.onmicrosoft.us#/access-packages/active
|
||||
|
||||
EPM Rufus Exception Request
|
||||
EPM DT Functions
|
||||
DLP - Encrypted Removable (USB) Long Term Access
|
||||
|
||||
Software:
|
||||
|
||||
Ubuntu Server 24.04 ISO
|
||||
|
||||
Rufus (latest)
|
||||
|
||||
playbook folder containing pxe_server_setup.yml and supporting files
|
||||
|
||||
GE Aerospace Media Creator LITE (for caching WinPE images)
|
||||
|
||||
Two USB thumb drives (one ≥ 8 GB for Ubuntu install; one ≥ 32 GB for WinPE media)
|
||||
|
||||
Step-by-Step Procedure
|
||||
Create the Ubuntu Server installer USB
|
||||
1.1 Download Ubuntu Server 24.04 from https://ubuntu.com/download/server.
|
||||
1.2 Download and run Rufus (https://rufus.ie/en/).
|
||||
1.3 Insert an empty USB, select it in Rufus.
|
||||
1.4 Click Select, browse to the Ubuntu ISO, then click Start.
|
||||
1.5 When Rufus finishes, copy your playbook folder to the root of that same USB, then eject it safely.
|
||||
|
||||
Install Ubuntu on the PXE server
|
||||
2.1 Insert the USB into the target machine and power on.
|
||||
2.2 Press F12 (or the vendor’s one-time boot key) and choose the USB device.
|
||||
2.3 Follow Ubuntu’s installer;
|
||||
Network configuration screen.
|
||||
Select the fist option select give it random network and IPv4 address
|
||||
Then select WiFi and choose the guest network.
|
||||
Follow the prompts and enter the information for your network.
|
||||
Click done.
|
||||
|
||||
You do not need a proxy hit done.
|
||||
For mirror address add nothing and hit done. The download should start.
|
||||
|
||||
After that select next
|
||||
You'll be in file system summary: Hit done, box will pop up "confirm destructive action" select "continue"
|
||||
|
||||
Configure your profile. Done
|
||||
Skip the upgrade to ubuntu pro
|
||||
No ssh
|
||||
Don't select featured server snaps just select done
|
||||
|
||||
Ubuntu will install…..then reboot your system
|
||||
2.4 Create a user (e.g., pxe) with a simple, temporary password (change later).
|
||||
|
||||
Prepare the OS
|
||||
3.1 Log in as the user you created.
|
||||
|
||||
3.2 Update the system:
|
||||
|
||||
bash
|
||||
Copy
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
|
||||
3.3 Install Ansible:
|
||||
|
||||
bash
|
||||
Copy
|
||||
sudo apt install ansible -y
|
||||
Mount the installer USB and run the playbook
|
||||
|
||||
4.1 Identify the USB device:
|
||||
|
||||
bash
|
||||
Copy
|
||||
lsblk
|
||||
Note the device (e.g., /dev/sda1).
|
||||
|
||||
4.2 Mount it and run the playbook:
|
||||
|
||||
bash
|
||||
Copy
|
||||
sudo mkdir -p /mnt/usb
|
||||
sudo mount /dev/sda1 /mnt/usb
|
||||
cd /mnt/usb/playbook
|
||||
ansible-playbook pxe_server_setup.yml
|
||||
|
||||
|
||||
4.3 When Ansible finishes, umount the USB:
|
||||
|
||||
bash
|
||||
Copy
|
||||
cd ~
|
||||
sudo umount /mnt/usb
|
||||
|
||||
Cache Windows PE images
|
||||
5.1 On a separate workstation, use GE Aerospace Media Creator LITE to cache all desired images (or start with one).
|
||||
5.2 Create a WinPE USB using the same tool and eject it safely.
|
||||
|
||||
Import WinPE content to the PXE share
|
||||
6.1 Insert the WinPE USB into the PXE server.
|
||||
6.2 Find the new device (e.g., /dev/sdb2) with lsblk.
|
||||
6.3 Mount it and copy files:
|
||||
|
||||
bash
|
||||
Copy
|
||||
sudo mkdir -p /mnt/usb2
|
||||
sudo mount /dev/sdb2 /mnt/usb2
|
||||
sudo cp -r /mnt/usb2/. /srv/samba/winpeapps/standard
|
||||
sudo umount /mnt/usb2
|
||||
Finalise and isolate
|
||||
|
||||
7.1 Reboot the server:
|
||||
|
||||
bash
|
||||
Copy
|
||||
sudo reboot
|
||||
|
||||
7.2 After it comes back up, move the primary NIC from the Internet-enabled network to the isolated switch that will serve PXE clients.
|
||||
|
||||
6. Verification
|
||||
Connect a test workstation to the isolated switch.
|
||||
|
||||
In BIOS/UEFI, set Network Boot (PXE) as first boot, then boot.
|
||||
|
||||
Confirm the client pulls an IP from the PXE server and sees the WinPE menu.
|
||||
|
||||
Launch a WinPE image to ensure TFTP, HTTP (NBD), and SMB shares respond correctly.
|
||||
222
unattend/FlatUnattendW10.xml
Normal file
222
unattend/FlatUnattendW10.xml
Normal file
@@ -0,0 +1,222 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<unattend xmlns="urn:schemas-microsoft-com:unattend"
|
||||
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<!-- 1. windowsPE is intentionally empty -->
|
||||
<settings pass="windowsPE" />
|
||||
|
||||
<!-- 2. Offline servicing (drivers) -->
|
||||
<settings pass="offlineServicing">
|
||||
<component name="Microsoft-Windows-PnpCustomizationsNonWinPE"
|
||||
processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35"
|
||||
language="neutral"
|
||||
versionScope="nonSxS">
|
||||
<DriverPaths>
|
||||
<PathAndCredentials wcm:action="add" wcm:keyValue="1">
|
||||
<Path>W:\Drivers</Path>
|
||||
</PathAndCredentials>
|
||||
<PathAndCredentials wcm:action="add" wcm:keyValue="2">
|
||||
<Path>W:\Deploy\Applications\extra\printdrivers\BROTHER\UNIV-PS-01181\PS\64</Path>
|
||||
</PathAndCredentials>
|
||||
<PathAndCredentials wcm:action="add" wcm:keyValue="3">
|
||||
<Path>W:\Deploy\Applications\extra\printdrivers\HP</Path>
|
||||
</PathAndCredentials>
|
||||
<PathAndCredentials wcm:action="add" wcm:keyValue="4">
|
||||
<Path>W:\Deploy\Applications\extra\printdrivers\XEROX\UNIV_5.1035.2.0_PS_x64_Driver</Path>
|
||||
</PathAndCredentials>
|
||||
</DriverPaths>
|
||||
</component>
|
||||
</settings>
|
||||
|
||||
<!-- 3. specialize: computer naming + RunSynchronous all in ONE component -->
|
||||
<settings pass="specialize">
|
||||
<!-- 3a. Shell-Setup for naming/owner/org -->
|
||||
<component name="Microsoft-Windows-Shell-Setup"
|
||||
processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35"
|
||||
language="neutral"
|
||||
versionScope="nonSxS">
|
||||
<ComputerName>H%serialnumber%</ComputerName>
|
||||
<RegisteredOrganization>GE Aerospace</RegisteredOrganization>
|
||||
<RegisteredOwner>GE</RegisteredOwner>
|
||||
<TimeZone>Eastern Standard Time</TimeZone>
|
||||
</component>
|
||||
|
||||
<!-- 3b. RunSynchronous for all of your installers, copies, etc. -->
|
||||
<component name="Microsoft-Windows-Deployment"
|
||||
processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35"
|
||||
language="neutral"
|
||||
versionScope="nonSxS">
|
||||
<RunSynchronous>
|
||||
<!-- EAP-PEAP MSI -->
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>1</Order>
|
||||
<Path>msiexec /i "C:\Deploy\Applications\extra\wireless\EAP-PEAP.msi" /quiet /norestart</Path>
|
||||
<Description>Install EAP-PEAP</Description>
|
||||
</RunSynchronousCommand>
|
||||
|
||||
<!-- Wi-Fi profiles -->
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>2</Order>
|
||||
<Path>cmd /c netsh wlan add profile filename="C:\Deploy\Applications\extra\wireless\BLUESSO.xml" user=all</Path>
|
||||
<Description>Add BLUESSO WiFi profile</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>3</Order>
|
||||
<Path>cmd /c netsh wlan add profile filename="C:\Deploy\Applications\extra\wireless\WiFi-Profile.xml" user=all</Path>
|
||||
<Description>Add generic WiFi profile</Description>
|
||||
</RunSynchronousCommand>
|
||||
|
||||
<!-- Certificates -->
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>4</Order>
|
||||
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\GE_External_Root_CA_2_1.cer"</Path>
|
||||
<Description>Install External Root Certificate</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>5</Order>
|
||||
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_External_Intermediate_CA_2_1.cer"</Path>
|
||||
<Description>Install External Intermediate Certificate</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>6</Order>
|
||||
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\GE_Enterprise_Root_CA_2_1.cer"</Path>
|
||||
<Description>Install Enterprise Root Certificate</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>7</Order>
|
||||
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_Device_Issuing_CA_2_1.cer"</Path>
|
||||
<Description>Install Enterprise Device Issuing Certificate</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>8</Order>
|
||||
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_Server_Issuing_CA_2_1.cer"</Path>
|
||||
<Description>Install Enterprise Server Issuing Certificate</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>9</Order>
|
||||
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_Smart_Card_Issuing_CA_2_1.cer"</Path>
|
||||
<Description>Install SmartCard Issuing Certificate</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>10</Order>
|
||||
<Path>cmd /c certutil -addstore CA "C:\Deploy\Applications\GE_Enterprise_User_Issuing_CA_2_1.cer"</Path>
|
||||
<Description>Install User Issuing Certificate</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>11</Order>
|
||||
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\GE_Aerospace_Enterprise_Root_CA_1.cer"</Path>
|
||||
<Description>Install Aerospace Enterprise Root CA</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>12</Order>
|
||||
<Path>cmd /c certutil -addstore Root "C:\Deploy\Applications\ZscalerCommercialCertificate-2048-SHA256.crt"</Path>
|
||||
<Description>Install Zscaler Certificate</Description>
|
||||
</RunSynchronousCommand>
|
||||
|
||||
<!-- Fonts -->
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>13</Order>
|
||||
<Path>cmd /c copy "C:\Deploy\Applications\extra\fonts\3OF9.TTF" "%WINDIR%\Fonts\" /Y</Path>
|
||||
<Description>Copy 3OF9 Font</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>14</Order>
|
||||
<Path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "3 of 9 Barcode" /t REG_SZ /d "3OF9.TTF" /f</Path>
|
||||
<Description>Register 3OF9 Barcode Font</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>15</Order>
|
||||
<Path>cmd /c copy "C:\Deploy\Applications\extra\fonts\Code39AzaleaNarrow3.ttf" "%WINDIR%\Fonts\" /Y</Path>
|
||||
<Description>Copy Code39 Azalea Narrow Font</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>16</Order>
|
||||
<Path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "Code39AzaleaNarrow3" /t REG_SZ /d "Code39AzaleaNarrow3.ttf" /f</Path>
|
||||
<Description>Register Code39 Azalea Narrow Font</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>17</Order>
|
||||
<Path>cmd /c copy "C:\Deploy\Applications\extra\fonts\Code39Azalea.ttf" "%WINDIR%\Fonts\" /Y</Path>
|
||||
<Description>Copy Code39 Azalea Font</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>18</Order>
|
||||
<Path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "Code39Azalea" /t REG_SZ /d "Code39Azalea.ttf" /f</Path>
|
||||
<Description>Register Code39 Azalea Font</Description>
|
||||
</RunSynchronousCommand>
|
||||
|
||||
<!-- OpenText installers -->
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>19</Order>
|
||||
<Path>C:\Deploy\Applications\extra\opentext\opentext_hostexplorer_sp1_15.0_v01.exe /quiet /norestart</Path>
|
||||
<Description>Install OpenText HostExplorer SP1</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>20</Order>
|
||||
<Path>C:\Deploy\Applications\extra\opentext\J2SE_Runtime_Environment_1.6.0_22_Static_Config_V2_Co-Exist.EXE /silent /norestart</Path>
|
||||
<Description>Install J2SE Runtime Environment 1.6.0_22</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>21</Order>
|
||||
<Path>C:\Deploy\Applications\extra\opentext\unattended.bat</Path>
|
||||
<Description>Install J2SE Runtime Environment 1.6.0_22</Description>
|
||||
</RunSynchronousCommand>
|
||||
|
||||
<!-- Adobe -->
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>22</Order>
|
||||
<Path>msiexec /i "C:\Deploy\Applications\extra\adobe\AcroRead.msi" TRANSFORMS="C:\Deploy\Applications\extra\adobe\AcroRead.mst" /quiet /norestart</Path>
|
||||
<Description>Install Adobe</Description>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>23</Order>
|
||||
<Path>msiexec /p "C:\Deploy\Applications\extra\adobe\AcroRdrDCUpd2500120531.msp" /quiet /norestart</Path>
|
||||
<Description>Apply Adobe Reader Update</Description>
|
||||
</RunSynchronousCommand>
|
||||
</RunSynchronous>
|
||||
</component>
|
||||
</settings>
|
||||
|
||||
<!-- 4. oobeSystem: hide OEM/EULA screens -->
|
||||
<settings pass="oobeSystem">
|
||||
<component name="Microsoft-Windows-Shell-Setup"
|
||||
processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35"
|
||||
language="neutral"
|
||||
versionScope="nonSxS">
|
||||
<OOBE>
|
||||
<HideEULAPage>true</HideEULAPage>
|
||||
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
|
||||
<HideOnlineAccountScreens>false</HideOnlineAccountScreens>
|
||||
<HideWirelessSetupInOOBE>false</HideWirelessSetupInOOBE>
|
||||
<HideLocalAccountScreen>true</HideLocalAccountScreen>
|
||||
<NetworkLocation>Work</NetworkLocation>
|
||||
<ProtectYourPC>3</ProtectYourPC>
|
||||
<SkipUserOOBE>false</SkipUserOOBE>
|
||||
<SkipMachineOOBE>false</SkipMachineOOBE>
|
||||
</OOBE>
|
||||
<FirstLogonCommands>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<Order>1</Order>
|
||||
<CommandLine>C:\Deploy\Applications\extra\zscaler\zscaler.bat</CommandLine>
|
||||
<Description>Install Zscaler Client Connector</Description>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<Order>2</Order>
|
||||
<CommandLine>shutdown -a</CommandLine>
|
||||
<Description>Cancel any scheduled shutdown from Office installation</Description>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<Order>3</Order>
|
||||
<CommandLine>cmd /c cd C:\Deploy\Applications\extra\office && install.bat</CommandLine>
|
||||
<Description>Install Office</Description>
|
||||
</SynchronousCommand>
|
||||
</FirstLogonCommands>
|
||||
</component>
|
||||
</settings>
|
||||
|
||||
</unattend>
|
||||
Reference in New Issue
Block a user