Generic build for other GE sites: no WJ data, one shopfloor type, Windows USB builder

Everything West Jefferson specific moved out to a separate repo
(pxe-site-wj): NTLARS/UDC/heat-treat backups keyed by WJ machine numbers,
CMM and Wax/Trace bay lists, WJ OpenText profiles, site-config.json, the
WJ preinstall list, and the WJ shopfloor PC types (Collections,
NoCollections, Common, Keyence, CMM, Genspect, HeatTreat, Wax/Trace,
Display, PartMarker) with GE-Enforce, machine numbers and the desktop
layout. The part-marker bench tools moved to their own repo. Dead
scripts removed: build-proxmox-iso, deploy-bios, sync_hardware_models,
mirror-from-gold, pull-bios, startnet-template.cmd.

Shopfloor is one generic type, gea-shopfloor-standard, with today's
timing:
- PPKG at first logon (unattend Order 10, unchanged).
- Install-AppList.ps1 runs C:\PreInstall\preinstall.json during setup.
- A new SYSTEM task, GE Shopfloor Post-PPKG, runs postppkg.json once
  after the post-PPKG reboot. It also clears AutoLogonCount after the
  ShopFloor handover (the Users-level monitor can be denied that HKLM
  write) and removes the Intune Sync task when done.
- Both lists ship empty. A site adds entries and installers.
- WinPE choice 3 goes straight to the PPKG menu. Staging, fetch and heal
  carry only the generic payload. The webapp's shopfloor sub-menu editor
  is gone, and the imaging tiles name the new stages.
- The playbook deploys the empty lists only if missing. It now deploys
  the PowerShell 7 MSI the shopfloor unattend installs, which it never
  did before.

Blancco site settings move into the webapp (Updates > Blancco releases):
business location, licensing Wi-Fi SSID and password. They are rendered
into the served preferences.xml and applied to every staged release, and
are seeded from site-vars (site_name, blancco_wifi_ssid,
blancco_wifi_password) only while none exist. The repo XML carries no
site values; the plaintext Wi-Fi password is gone from it.

New site_name and site_timezone settings: report and evidence-pack site
name, server clock and webapp time zone. No more West Jefferson defaults
in webapp/config.py or the dashboard title. Sync-BPRT.ps1 gets the
server address filled in at download instead of WJ's GE address.

USB on Windows:
- scripts/Build-PxeUsb.ps1 runs the same checks as build-usb.sh. It
  writes one FAT32 CIDATA partition (ISO files plus patched grub.cfg plus
  payload) with diskpart and reads every file back by SHA-256.
- Tested with Windows PowerShell 5.1 in the win11 VM, and the layout
  installs and first-boots in the pxetest VM.
- scripts/stage-usb-share.sh stages the build folder on a share.
- site-content/ is optional for both builders.

KB docs/kb/How_To_Deploy_PXE_Server_New_Site.html: new-site deployment,
Windows only, with a site-vars.yml guide.

Checks: webapp tests pass (5 suites); the unattend lint and playbook
syntax checks pass; every .ps1 parses; there are no WJ hosts, IPs, names
or PC types in playbook/, webapp/, scripts/ or autoinstall/.
This commit is contained in:
cproudlock
2026-10-16 20:37:22 -04:00
parent b11e678297
commit 04db6d6605
710 changed files with 3113 additions and 82414 deletions

View File

@@ -69,9 +69,8 @@
# Zscaler Root CA: GE's Zscaler re-signs GitHub TLS, so the iPXE update
# check fails without it. Repo copy first, else the copy on the image share.
zscaler_ca_src: "{{ playbook_dir }}/certs/zscaler-root-ca.crt"
# _media/<pctype> links the base image's entries, so also look in the
# image roots (on a rebuild the _media views may not exist yet).
zscaler_ca_share_glob: "{{ samba_share }}/_media/*/Deploy/Applications/ZscalerCommercialCertificate-2048-SHA256.crt {{ samba_share }}/gea-*/Deploy/Applications/ZscalerCommercialCertificate-2048-SHA256.crt"
# The copy the playbook puts in every image (Deploy/Applications).
zscaler_ca_share_glob: "{{ samba_share }}/gea-*/Deploy/Applications/ZscalerCommercialCertificate-2048-SHA256.crt"
# --- The two NICs (site standard) ---
# The imaging NIC (isolated imaging LAN, static pxe_server_ip, the ONLY
# NIC that serves DHCP/TFTP/HTTP boot/SMB) is pinned by MAC. Leave
@@ -119,7 +118,7 @@
pxe_imaging_mac: "{{ pxe_nic_mac }}"
pxe_ge_mac: "{{ corp_nic_mac }}"
# A foreign DHCP server handing out one of these domains (whole labels:
# wjs.geaerospace.net matches, notgeaerospace.net does not) = GE network.
# site.geaerospace.net matches, notgeaerospace.net does not) = GE network.
pxe_corp_domain_suffixes: "geaerospace.net, ge.com"
# Only if the IMAGING switch is itself managed and sends LLDP/CDP: its
# LLDP system name / CDP device id / chassis MAC, comma separated, so
@@ -145,7 +144,7 @@
# Who may reach SSH and the webapp (9009, no login, runs as root) on the
# GE corporate NIC. Empty (the default) = all of GE, as before, with a
# loud warning every run. Set to the admin subnets/hosts, e.g.
# ["10.134.48.0/24", "3.12.34.56/32"]: UFW then allows 22 and 9009 on
# ["10.12.34.0/24", "10.12.40.5/32"]: UFW then allows 22 and 9009 on
# the corp NIC from these only. The play refuses to restrict if your own
# SSH session is not inside one of them. See docs/SITE-CUSTOMIZATION.md.
corp_admin_cidrs: []
@@ -200,6 +199,19 @@
# letters, digits . _ ~ - (it sits in a URI, a kernel command line and
# a mount option list), 8 to 64 of them.
clonezilla_smb_password: "clonezilla-backup"
# The site, as stamped on Blancco erasure reports and the monthly CMMC
# evidence packs (webapp BLANCCO_SITE). Empty: "Unnamed site", so a
# missing name shows instead of another site's. Letters, digits, spaces
# and . , - ( ) & / only (it goes into a systemd unit).
site_name: ""
# Time zone of the server clock and of every time the webapp shows
# (evidence-pack month boundaries included). A tz database name.
site_timezone: "America/New_York"
# Wi-Fi Blancco joins to license erasures in the GE Blancco cloud (the
# imaging LAN has no internet). The SSID is GE-wide; the password can
# differ per site. Empty password: Blancco cannot license (warned).
blancco_wifi_ssid: "INTERNETACCESS"
blancco_wifi_password: ""
image_types:
- gea-standard
- gea-engineer
@@ -318,6 +330,32 @@
Fix playbook/site-vars.yml and re-run.
quiet: yes
- name: "Site: check site_name and site_timezone"
assert:
that:
- site_name is string
- site_name | length == 0 or site_name is match('^[A-Za-z0-9 .,()&/-]{1,60}$')
- site_timezone is match('^[A-Za-z0-9_+/-]{1,64}$')
- ('/usr/share/zoneinfo/' ~ site_timezone) is file
fail_msg: >-
site_name must be up to 60 characters from letters, digits, spaces
and . , - ( ) & / (no quotes); site_timezone a time zone name such
as America/New_York, Europe/London or Asia/Kolkata. Now:
site_name='{{ site_name }}' site_timezone='{{ site_timezone }}'.
quiet: yes
- name: "Site: WARNING, site_name is not set"
debug:
msg:
- "!!! site_name is empty: Blancco erasure reports and the monthly evidence"
- "!!! packs will say 'Unnamed site'. Set site_name in playbook/site-vars.yml."
when: site_name | length == 0
- name: "Site: set the time zone"
command: timedatectl set-timezone {{ site_timezone }}
register: tz_set
changed_when: false
- name: "Clonezilla: check the clonezilla Samba password"
assert:
that:
@@ -1040,8 +1078,6 @@
- pre-install
- pre-install/bios
- pre-install/installers
- installers-post
- installers-post/cmm
- blancco
- logs
@@ -1054,10 +1090,9 @@
Single SMB share mounted by WinPE as Y: during imaging. Subdir layout:
- ppkgs/ GCCH bulk-enrollment PPKGs
- scripts/ run-enrollment.ps1, wait-for-internet.ps1, migrate-to-wifi.ps1
- config/ site-config.json, FlatUnattendW10*.xml, per-site overrides
- shopfloor-setup/ Per-PC-type post-imaging scripts
- pre-install/ WinPE-phase content (bios/, installers/, preinstall.json)
- installers-post/ Post-OOBE app installers (cmm/PCDMIS, etc.)
- config/ FlatUnattendW10*.xml, per-site overrides
- shopfloor-setup/ Shopfloor setup scripts (gea-shopfloor-standard)
- pre-install/ preinstall.json + postppkg.json (site app lists) and installers/
- blancco/ Blancco custom images / configs
- logs/ Client log uploads
@@ -1089,13 +1124,6 @@
- { src: "{{ usb_mount }}/winpe-status-push.ps1", dest: "winpe-status-push.ps1" }
ignore_errors: yes
- name: "Deploy site-config.json to config/"
copy:
src: "{{ usb_mount }}/shopfloor-setup/site-config.json"
dest: /srv/samba/enrollment/config/site-config.json
mode: '0644'
ignore_errors: yes
- name: "Deploy Dell driver packs to shared Out-of-box Drivers"
args:
executable: /bin/bash
@@ -1138,54 +1166,31 @@
register: shopfloor_rsync
changed_when: shopfloor_rsync.stdout | trim | length > 0 and 'SKIP:' not in shopfloor_rsync.stdout
- name: "Deploy preinstall.json to pre-install/"
# The site app lists (both empty in the generic build). force: no - once
# a site has filled in its lists on the share, a playbook re-run must not
# put the empty templates back.
- name: "Deploy the site app lists to pre-install/ (only if missing)"
copy:
src: "{{ usb_mount }}/preinstall/preinstall.json"
dest: /srv/samba/enrollment/pre-install/preinstall.json
src: "{{ usb_mount }}/preinstall/{{ item }}"
dest: "/srv/samba/enrollment/pre-install/{{ item }}"
mode: '0644'
ignore_errors: yes
force: no
loop: [preinstall.json, postppkg.json]
# Oracle Client 11.2 is the first app the preinstall runner executes, so it
# has to be present on-disk before the first client images. The 686 MB
# Oracle_OracleDatabase_11r2_V03.zip doesn't belong in git; it rides on the
# USB under oracle/ alongside the BIOS exes. Install-Oracle11r2.cmd is
# tracked at playbook/preinstall/oracle/ and arrives via usb_mount.
- name: "Create pre-install/installers/oracle/ staging dir"
file:
path: /srv/samba/enrollment/pre-install/installers/oracle
state: directory
mode: '0755'
ignore_errors: yes
- name: "Deploy Install-Oracle11r2.cmd to pre-install/installers/oracle/"
copy:
src: "{{ usb_mount }}/preinstall/oracle/Install-Oracle11r2.cmd"
dest: /srv/samba/enrollment/pre-install/installers/oracle/Install-Oracle11r2.cmd
mode: '0755'
ignore_errors: yes
# Per-file overrides for the .ora config dropped by the wrapper post-install.
# The zip's client/ora/ contains dated defaults; shipping an updated
# tnsnames.ora alongside the wrapper lets us refresh the DB catalog without
# repackaging the 686 MB zip. sqlnet.ora and ldap.ora can be added here the
# same way if they ever need to diverge from the zip's bundled copies.
- name: "Deploy updated tnsnames.ora override to pre-install/installers/oracle/"
copy:
src: "{{ usb_mount }}/preinstall/oracle/tnsnames.ora"
dest: /srv/samba/enrollment/pre-install/installers/oracle/tnsnames.ora
mode: '0644'
ignore_errors: yes
- name: "Deploy Oracle 11.2 zip (686 MB) from USB to pre-install/installers/oracle/"
shell: >
if [ -f "{{ usb_root }}/oracle/Oracle_OracleDatabase_11r2_V03.zip" ]; then
cp -f "{{ usb_root }}/oracle/Oracle_OracleDatabase_11r2_V03.zip" \
/srv/samba/enrollment/pre-install/installers/oracle/
echo "Deployed Oracle 11.2 zip ($(stat -c %s /srv/samba/enrollment/pre-install/installers/oracle/Oracle_OracleDatabase_11r2_V03.zip) bytes)"
else
echo "WARNING: {{ usb_root }}/oracle/Oracle_OracleDatabase_11r2_V03.zip not on USB - preinstall runner will fail on Oracle step until this file is staged at /srv/samba/enrollment/pre-install/installers/oracle/"
fi
ignore_errors: yes
# Installers the imaging flow needs (PowerShell 7, which the shopfloor
# unattend installs before the PPKG) plus any the site adds. Merge, no
# delete: a site's own installers on the share stay.
- name: "Deploy pre-install installers (PowerShell 7 MSI)"
shell: |
set -e
SRC="{{ usb_mount }}/preinstall/installers/"
[ -d "$SRC" ] || { echo "SKIP: no preinstall/installers on the USB"; exit 0; }
mkdir -p /srv/samba/enrollment/pre-install/installers
rsync -rlt -i --chmod=D0755,F0644 "$SRC" /srv/samba/enrollment/pre-install/installers/
args:
executable: /bin/bash
register: preinstall_rsync
changed_when: preinstall_rsync.stdout | trim | length > 0 and 'SKIP:' not in preinstall_rsync.stdout
- name: "Ensure winpeapps/_shared/BIOS directory exists"
file:
@@ -1977,10 +1982,15 @@
# Idempotent file copy (was bundled into the previous shell task with a
# 'creates:' gate, so playbook re-runs never picked up edits to the
# source XML - stale prefs survived deploys).
- name: "Deploy Blancco preferences.xml from playbook source"
# The repo's preferences carry no site values. They are installed as
# preferences.repo.xml; the served preferences.xml is rendered from it
# plus the site settings (business location, licensing Wi-Fi) by the
# webapp's services/blancco_prefs.py, after the webapp is installed
# below. The settings are edited on Updates > Blancco releases.
- name: "Deploy Blancco preferences (repo copy, no site values)"
copy:
src: "{{ usb_root }}/playbook/blancco-preferences.xml"
dest: "{{ web_root }}/blancco/preferences.xml"
dest: "{{ web_root }}/blancco/preferences.repo.xml"
mode: '0644'
# blancco-init.sh fetches this at boot and runs it as a service in the
@@ -1991,18 +2001,6 @@
dest: "{{ web_root }}/blancco/blancco-progress.py"
mode: '0644'
# Hard gate: blancco-init.sh in initramfs drops to shell if the served
# preferences.xml is malformed or missing the SMB target. Fail the
# playbook here too so the problem surfaces at deploy time, not at the
# next erasure attempt.
- name: "Validate deployed preferences.xml is well-formed + targets the right SMB share"
shell: |
set -e
python3 -c 'import xml.etree.ElementTree as ET; ET.parse("{{ web_root }}/blancco/preferences.xml")'
grep -q '<hostname>{{ pxe_server_ip }}</hostname>' "{{ web_root }}/blancco/preferences.xml"
grep -q '<path>blancco-reports</path>' "{{ web_root }}/blancco/preferences.xml"
changed_when: false
- name: "Ensure Samba user for Blancco reports exists (idempotent)"
shell: |
id blancco >/dev/null 2>&1 || useradd -r -s /usr/sbin/nologin blancco
@@ -2404,6 +2402,39 @@
register: webapp_wheels
changed_when: webapp_wheels.stdout_lines | last | default('') == 'CHANGED'
# Blancco site settings: seeded from site-vars only while none exist
# (after that the webapp page owns them), then the served preferences.xml
# is rendered from preferences.repo.xml. Password via env, not argv.
- name: "Blancco: render preferences.xml with the site settings"
command: python3 -m services.blancco_prefs render --seed
args:
chdir: /opt/pxe-webapp
environment:
WEB_ROOT: "{{ web_root }}"
BLANCCO_SEED_LOCATION: "{{ site_name }}"
BLANCCO_SEED_SSID: "{{ blancco_wifi_ssid }}"
BLANCCO_SEED_PASSWORD: "{{ blancco_wifi_password }}"
no_log: true
register: blancco_render
changed_when: "'CHANGED' in blancco_render.stdout_lines"
- name: "Blancco: render result"
debug:
msg: "{{ blancco_render.stdout_lines | select('search', 'CHANGED|WARNING|seeded') | list }}"
# Hard gate: blancco-init.sh in initramfs drops to shell if the served
# preferences.xml is malformed or missing the SMB target. Fail the
# playbook here too so the problem surfaces at deploy time, not at the
# next erasure attempt.
- name: "Validate deployed preferences.xml is well-formed + targets the right SMB share"
shell: |
set -e
python3 -c 'import xml.etree.ElementTree as ET; ET.parse("{{ web_root }}/blancco/preferences.xml")'
grep -q '<hostname>{{ pxe_server_ip }}</hostname>' "{{ web_root }}/blancco/preferences.xml"
grep -q '<path>blancco-reports</path>' "{{ web_root }}/blancco/preferences.xml"
grep -q '<business_location>[^<]' "{{ web_root }}/blancco/preferences.xml"
changed_when: false
- name: "Create systemd service for PXE webapp"
copy:
dest: /etc/systemd/system/pxe-webapp.service
@@ -2423,10 +2454,12 @@
Environment=ENROLLMENT_SHARE=/srv/samba/enrollment
Environment=AUDIT_LOG=/var/log/pxe-webapp-audit.log
# Lock TZ so report/backup mtimes and audit-log timestamps render
# in Eastern time regardless of how the host's /etc/localtime ends
# up. Without this, a Python process started before timedatectl
# finishes can cache UTC for its lifetime.
Environment=TZ=America/New_York
# in the site's time zone regardless of how the host's
# /etc/localtime ends up. Without this, a Python process started
# before timedatectl finishes can cache UTC for its lifetime.
Environment=TZ={{ site_timezone }}
Environment=DISPLAY_TZ={{ site_timezone }}
Environment="BLANCCO_SITE={{ site_name if site_name else 'Unnamed site' }}"
ExecStart=/usr/bin/python3 app.py
Restart=always
RestartSec=5
@@ -2470,7 +2503,9 @@
Type=oneshot
WorkingDirectory=/opt/pxe-webapp
Environment=BLANCCO_REPORTS=/srv/samba/blancco-reports
Environment=TZ=America/New_York
Environment=TZ={{ site_timezone }}
Environment=DISPLAY_TZ={{ site_timezone }}
Environment="BLANCCO_SITE={{ site_name if site_name else 'Unnamed site' }}"
ExecStart=/usr/bin/python3 /opt/pxe-webapp/blancco_evidence_monthly.py
- name: pxe-blancco-evidence.timer
content: |