Three new baseline scripts that run during shopfloor imaging to clean up
the end-user Public Desktop. Before this, Azure AD users logged into a
shopfloor PC and saw 20+ loose shortcuts at the desktop root (Office
apps, OpenText sessions, WJ web portals, DNC utilities, Defect Tracker,
plus .url files for every intranet page) with no organization. End users
couldn't find anything.
06-OrganizeDesktop.ps1 - Single source of truth for Public Desktop layout
Phase 1: sweeps loose shortcuts at the desktop root into three category
folders - Office\, Shopfloor Tools\, Web Links\ - by filename regex,
extension, and .lnk target resolution. Allowlists eDNC.lnk and
NTLARS.lnk to stay at root since end users click them too often.
Unknown items are left at the root on purpose (never delete).
Phase 2: materializes specific app shortcuts into Shopfloor Tools\.
UDC / eDNC / NTLARS are built fresh from their .exe paths; WJ
Shopfloor and Defect_Tracker are MSI-advertised (empty TargetPath,
Darwin descriptor) so we copy the existing .lnk from wherever it
lives via a multi-location lookup. Each entry is conditional on its
source being present - script runs cleanly on PC types without DnC.
Phase 3: drops eDNC.lnk and NTLARS.lnk at desktop root from the
Shopfloor Tools\ copies, so end users have both a folder version
and a quick-access root version.
Phase 4: registers an "Organize Public Desktop" scheduled task that
re-runs phase 1 at every logon. Shortcuts added later by DSC /
Intune / msiexec get filed automatically without another imaging
pass. Admin check at the top, -ErrorAction Stop on Register-
ScheduledTask and directory creation so failures are caught
instead of printing false success.
07-TaskbarLayout.ps1 - Minimal taskbar pinner
Checks which .lnk files 06 created in Shopfloor Tools\, then writes
LayoutModification.xml to the Default User profile with taskbar pins
in order: Edge, WJ Shopfloor, UDC, eDNC, Defect_Tracker. No shortcut
creation in this script - all shortcut management lives in 06.
Missing .lnks are skipped (PC types without DnC just get fewer pins).
Applies on first logon of new user profiles (Azure AD users after
enrollment). Existing profiles don't re-read Default User - Windows
design limitation since 1703, no programmatic fix.
08-EdgeDefaultBrowser.ps1 - Edge as default browser + startup tabs
Motivated by the ppkg installing Chrome alongside Edge: new Azure AD
users hit a "Choose your default app" picker on first URL click
because nothing is marked default. Two layers:
1. dism /Online /Import-DefaultAppAssociations:<xml> writes an XML
with Edge ProgIds for http/https/.htm/.html/.pdf/.svg/.webp into
the Default User profile template. New profiles inherit.
2. HKLM:\SOFTWARE\Policies\Microsoft\Windows\System\
DefaultAssociationsConfiguration registry value (the "Set a
default associations configuration file" GPO) points at the same
XML so Windows re-applies on every logon, catching Windows-update
defaults-reset cases.
Leaves Chrome installed, just not the default URL handler.
Also sets Edge startup tabs via machine-wide policies under
HKLM:\SOFTWARE\Policies\Microsoft\Edge:
RestoreOnStartup = 4 (open specific URLs)
RestoreOnStartupURLs = Plant Apps, WJ Shop Floor Homepage, Shopfloor
Dashboard (tab order per spec)
HomepageLocation = first tab (Plant Apps)
HomepageIsNewTabPage = 0
ShowHomeButton = 1
URLs are resolved dynamically from the .url files on the Public
Desktop (or Web Links\ after the sweep), so if WJDT changes a URL
later the script picks it up without a code change. Fallbacks are
hardcoded for the two portals we have URLs memorized for; Plant Apps
has no fallback and will be skipped if the .url file is missing.
Test workflow: admin-check in all three scripts fails fast on non-
elevated runs instead of spamming half-successful Access Denied output
like the first draft did.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GE Aerospace PXE Boot Server
Automated, air-gapped PXE boot server for deploying GE Aerospace Windows images. Built on Ubuntu 24.04 Server with zero-touch provisioning via autoinstall and Ansible.
Overview
This project provides a complete, repeatable build process for a PXE boot server that serves Windows PE images to client machines on an isolated network. Everything runs offline after initial setup — no internet required on the target server.
Boot Chain
Client PXE boot (UEFI Secure Boot)
-> iPXE (TFTP, Broadcom-signed for Secure Boot)
-> iPXE boot menu (HTTP, port 4433)
-> User selects boot option:
├── Windows PE -> wimboot -> boot.wim -> startnet.cmd -> Samba share -> Image deployment
├── Clonezilla -> vmlinuz/initrd -> Disk cloning/imaging
├── Blancco -> Native kernel + initramfs -> NIST 800-88 drive erasure
└── Memtest86+ -> Memory diagnostics
Services
| Service | Port | Purpose |
|---|---|---|
| dnsmasq | 67/udp | DHCP (10.9.100.10-100, 12h lease) |
| dnsmasq | 69/udp | TFTP (serves ipxe.efi) |
| Apache | 80/tcp | HTTP (wimboot, WinPE boot files, proxy) |
| Apache | 4433/tcp | iPXE boot script (GetPxeScript.aspx) |
| Samba | 445/tcp | Deployment content + Clonezilla + Blancco |
| Flask Webapp | 9009/tcp | Web management interface |
Network
- PXE server IP:
10.9.100.1/24 - DHCP range:
10.9.100.10-10.9.100.100 - Firewall: UFW deny-by-default, only service ports open (22, 67, 69, 80, 445, 4433, 9009)
Quick Start
Prerequisites
On your workstation (internet-connected):
- Ubuntu 24.04 (or Linux Mint / similar) for downloading packages
- Ubuntu Server 24.04 ISO
- GE Aerospace Media Creator LITE (for WinPE images)
- USB drive >= 8 GB (32+ GB if bundling WinPE images)
GE Access Packages (MyAccess portal):
- EPM Rufus Exception Request
- EPM DT Functions
- DLP - Encrypted Removable (USB) Long Term Access
Step 1: Download Offline Packages
./download-packages.sh
Downloads all .deb packages and Python wheels for offline installation (~140 MB of debs, ~20 MB of wheels).
Step 2: Prepare Boot Tools (optional)
./prepare-boot-tools.sh /path/to/blancco.iso /path/to/clonezilla.zip /path/to/memtest.bin
Extracts and configures boot tool files (Blancco, Clonezilla, Memtest86+). Automatically patches Blancco's config.img to auto-save erasure reports to the PXE server's Samba share.
Step 3: Build the USB
sudo ./build-usb.sh /dev/sdX /path/to/ubuntu-24.04-live-server-amd64.iso
Creates a bootable USB with two partitions:
- Partition 1: Ubuntu Server installer
- Partition 2: CIDATA (autoinstall config, offline .debs, pip wheels, Ansible playbook, webapp, boot tools)
Step 4: Install on Target Server
- Insert USB into the target machine
- Press F12 and boot from USB
- Ubuntu auto-installs with no interaction
- After reboot, the first-boot script:
- Installs all offline .deb packages
- Runs the Ansible playbook (configures dnsmasq, Apache, Samba, UFW, webapp)
- Configures static IP
10.9.100.1/24
- Move the server's wired NIC to the isolated PXE switch
Step 5: Access the Web Interface
Open http://10.9.100.1:9009 from any machine on the isolated network.
Web Management Interface
The Flask webapp (port 9009) provides a browser-based management UI:
- Dashboard — Service status overview, disk usage, connected DHCP clients
- Image Import — Import WinPE deployment images from USB drives
- Unattend Editor — Edit Windows unattend.xml files per image type (XML syntax highlighting)
- startnet.cmd Editor — Modify the startnet.cmd inside boot.wim without Windows (uses wimtools)
- Clonezilla Backups — Upload, download, and manage disk backup images
- Blancco Reports — View, download, and manage drive erasure reports (auto-collected via Samba)
- Image Config — Per-image configuration editor (drivers, OS packages, hardware models)
- Enrollment — Upload, download, and manage GCCH bulk enrollment packages
- Audit Log — Activity history for all write operations (imports, edits, deletes)
Image Types Supported
| Image Type | Domain | Description |
|---|---|---|
| gea-standard | geaerospace.com | Standard desktop |
| gea-engineer | geaerospace.com | Engineering desktop |
| gea-shopfloor | geaerospace.com | Shop floor kiosk |
| ge-standard | ge.com | Standard desktop |
| ge-engineer | ge.com | Engineering desktop |
| ge-shopfloor-lockdown | ge.com | Shop floor (locked) |
| ge-shopfloor-mce | ge.com | Shop floor (MCE) |
Project Structure
pxe-server/
├── autoinstall/
│ ├── user-data # Cloud-init autoinstall config + first-boot script
│ └── meta-data # Cloud-init metadata (required, empty)
├── playbook/
│ ├── pxe_server_setup.yml # Ansible playbook: all server configuration
│ ├── inventory.ini # Ansible inventory
│ ├── startnet.cmd # WinPE startup script (injected into boot.wim)
│ ├── blancco-init.sh # Custom initramfs for Blancco PXE boot
│ ├── blancco-preferences.xml # Blancco auto-report preferences
│ ├── check-bios.cmd # Pre-imaging BIOS update checker
│ ├── FlatUnattendW10-shopfloor.xml # Shopfloor unattend.xml
│ └── shopfloor-setup/ # PC type setup scripts (Standard, CMM, Display, etc.)
├── webapp/
│ ├── app.py # Flask application (~1600 lines)
│ ├── requirements.txt # Python deps (flask, lxml)
│ ├── static/
│ │ ├── ge-aerospace-logo.svg # GE Aerospace branding
│ │ ├── favicon.ico # Browser favicon
│ │ ├── app.js # Frontend JavaScript
│ │ ├── bootstrap.min.css # Bootstrap 5 (bundled offline)
│ │ ├── bootstrap.bundle.min.js
│ │ ├── bootstrap-icons.min.css
│ │ └── fonts/ # Icon fonts (woff/woff2)
│ └── templates/
│ ├── base.html # Layout with GE branding and sidebar nav
│ ├── dashboard.html # Service status and overview
│ ├── import.html # USB image import wizard
│ ├── unattend_editor.html # XML editor for unattend files
│ ├── startnet_editor.html # startnet.cmd WIM editor
│ ├── image_config.html # Per-image driver and hardware config
│ ├── enrollment.html # GCCH bulk enrollment packages
│ ├── backups.html # Clonezilla backup management
│ ├── reports.html # Blancco erasure reports
│ └── audit.html # Activity audit log
├── docs/
│ └── shopfloor-display-imaging-guide.md # End-user imaging guide
├── unattend/
│ └── FlatUnattendW10.xml # Windows unattend.xml template
├── boot-tools/ # Extracted boot tool files (gitignored)
│ ├── blancco/ # Blancco Drive Eraser (Arch Linux-based)
│ ├── clonezilla/ # Clonezilla Live
│ └── memtest/ # Memtest86+
├── offline-packages/ # .deb files (gitignored, built by download-packages.sh)
├── pip-wheels/ # Python wheels (gitignored, built by download-packages.sh)
├── download-packages.sh # Downloads offline .debs + pip wheels
├── build-usb.sh # Builds the installer USB (2-partition)
├── prepare-boot-tools.sh # Extracts and patches boot tool files
├── build-proxmox-iso.sh # Builds self-contained Proxmox installer ISO
├── test-vm.sh # KVM test environment for validation
├── test-lab.sh # Full PXE lab with server + client VMs
├── startnet-template.cmd # startnet.cmd template (synced with playbook copy)
├── Download-Drivers.ps1 # Download hardware drivers from GE CDN (Windows)
├── Upload-Image.ps1 # Upload MCL cache to PXE server via SMB (Windows)
├── download-drivers.py # Download Dell drivers directly from dell.com
├── sync_hardware_models.py # Sync hardware model configs across images
├── SETUP.md # Detailed setup guide
└── setup-guide-original.txt # Original manual setup notes (reference)
Testing with KVM
A test VM script is included for validating the full provisioning pipeline without dedicated hardware:
# Download Ubuntu Server ISO
wget -O ~/Downloads/ubuntu-24.04.3-live-server-amd64.iso \
https://releases.ubuntu.com/noble/ubuntu-24.04.3-live-server-amd64.iso
# Launch test VM (requires libvirt/KVM)
sudo ./test-vm.sh ~/Downloads/ubuntu-24.04.3-live-server-amd64.iso
# Watch install progress
sudo virsh console pxe-test
# Clean up when done
sudo ./test-vm.sh --destroy
The test VM creates an isolated libvirt network (10.9.100.0/24) and runs the full autoinstall + Ansible provisioning.
Proxmox Deployment
A single ISO can be built for deploying the PXE server in a Proxmox VM:
Build the ISO
# Prerequisites (on build workstation)
sudo apt install xorriso p7zip-full
# Build the installer ISO
./build-proxmox-iso.sh /path/to/ubuntu-24.04-live-server-amd64.iso
This creates pxe-server-proxmox.iso containing the Ubuntu installer, autoinstall config, all offline packages, the Ansible playbook, webapp, and boot tools.
Deploy on Proxmox
- Upload
pxe-server-proxmox.isoto Proxmox storage (Datacenter -> Storage -> ISO Images) - Create a new VM:
- OS: Linux 6.x kernel
- BIOS: OVMF (UEFI) or SeaBIOS
- Memory: 4096 MB
- CPU: 2+ cores
- Disk: 40+ GB (VirtIO SCSI)
- Network: Bridge connected to your isolated PXE network
- Attach the ISO as CD-ROM and start the VM
- Ubuntu auto-installs with zero interaction (~10-15 minutes)
- After reboot, first-boot configures all PXE services automatically
- Access the web interface at
http://10.9.100.1:9009
Import WinPE Images
After the server is running, import deployment images via the web interface at http://10.9.100.1:9009/import or by mounting a USB drive with WinPE content.
Samba Shares
| Share | Path | Purpose |
|---|---|---|
| winpeapps | /srv/samba/winpeapps | WinPE deployment images |
| clonezilla | /srv/samba/clonezilla | Clonezilla disk backup images |
| blancco-reports | /srv/samba/blancco-reports | Blancco erasure reports (auto) |
| enrollment | /srv/samba/enrollment | GCCH bulk enrollment packages |
| image-upload | /home/pxe/image-upload | Image upload staging area |
All shares use guest access (no authentication) for ease of use on the isolated network.
Blancco Drive Erasure
Blancco Drive Eraser 7.15.1 boots via a native Ubuntu kernel with a custom initramfs (blancco-init.sh) that downloads and mounts the Blancco rootfs over HTTP. XML erasure reports are automatically saved to the PXE server's Samba share (blancco-reports). The server supports BMC cloud licensing for Blancco activation over WiFi.
Reports are viewable and downloadable from the web interface at http://10.9.100.1:9009/reports.
Notes
- Run
download-packages.shbefore building USB — it downloads all offline.debpackages including wimtools (needed for startnet.cmd editing) - The webapp uses session-based CSRF tokens on all POST forms and API endpoints
Commit History
| Commit | Description |
|---|---|
5791bd1 |
Initial project setup: automated PXE server provisioning |
cee4ecd |
Add web management UI, offline packages, WinPE consolidation |
f614596 |
Fix unattend.xml path to match actual image structure |
e7313c2 |
Add multi-boot PXE menu, Clonezilla backups, GE Aerospace branding |
89b5834 |
Add wimtools and startnet.cmd editor for boot.wim modification |
05dbb7e |
Add Blancco erasure reports Samba share and webapp viewer |
ef75839 |
Auto-patch Blancco config.img for network report storage |
92c9b0f |
Fix review findings: offline assets, security, audit logging |
725c8f4 |
Change webapp to port 9009, add test VM script |
f3a384f |
Add Proxmox ISO builder, CSRF protection, boot-files integration |
1a5c4f7 |
Eliminate USB requirement for WinPE PXE boot |
dd2fec5 |
Blancco PXE boot via Ubuntu kernel switch_root |
86660a0 |
Remove UEFI HTTP Boot config from dnsmasq |
6d0e6ee |
BIOS check fix, parallel downloads, shopfloor hardening |
7616549 |
Shopfloor PC type system, webapp enhancements |
b7cd097 |
Blancco 7.15.1 upgrade: native kernel boot, BMC cloud licensing |
163e58a |
Fix dnsmasq reboot cron: use /etc/cron.d/ instead of crontab |