Capture site content from a live server so a new one can build every image

A freshly installed server could not build standard or engineer: about
8 GB they install from existed only on the running server.
scripts/capture-site-content.sh pulls it over SSH into the gitignored
payload:
- _shared/Applications/extra (Office, Adobe, OpenText, Java, fonts, Wi-Fi,
  Zscaler), without pxe-status/ (the playbook deploys that);
- each image's own Deploy/Applications files and folders (GE CA
  certificates, the Engineering Layer payload), without the repo-managed
  Install-EngineeringLayer.ps1 and without stale BPRT copies;
- the links images keep there, as links.txt;
- fresh bios-staging (*.exe plus models.txt, which the Dell update
  rewrites), boot.wim, and the known Blancco file set.

It dry-runs by default, compares by checksum (a Blancco config.img had the
same size and time as the stale copy), writes SHA256SUMS, and refuses what
FAT32 cannot hold: files of 4 GiB or more, Windows-illegal names, and
case-only name clashes.

build-usb.sh copies site-content/ unsplit and refuses to build without it
(unless --skip-site-content), or when SHA256SUMS is missing or does not
cover every file. The playbook checks SHA256SUMS, then merges the content
into winpeapps and recreates the links. It only links to folders that
exist, so it never creates an empty _shared BPRT, which would stop the
first MCL import from seeding it. Link lines are validated before anything
is copied.

pull-bios.sh is gone. It read a path the server no longer uses, with a
hard-coded password.
This commit is contained in:
cproudlock
2026-10-16 15:17:42 -04:00
parent d63cc5761b
commit 9af1c6d89a
8 changed files with 381 additions and 27 deletions

View File

@@ -1093,6 +1093,82 @@
dest: "{{ samba_share }}/gea-engineer/Deploy/Applications/extra_engineering/Install-EngineeringLayer.ps1"
mode: '0644'
# Site content (scripts/capture-site-content.sh on the build PC): what
# neither the repo nor the MCL image import brings - _shared/Applications/
# extra (Office, Adobe, OpenText, Java, fonts, Wi-Fi, Zscaler), the GE CA
# certificates and the Engineering Layer payload in each image's
# Deploy/Applications, and the links images keep there. Without it a new
# server cannot build standard or engineer. Checked against SHA256SUMS
# first: a bad copy stops the play instead of imaging PCs from it.
- name: "Site content: check the USB copy against SHA256SUMS"
shell: |
set -e
SC="{{ usb_root }}/site-content"
if [ ! -d "$SC" ]; then echo "SKIP: no site-content on the USB"; exit 0; fi
cd "$SC"
sha256sum --quiet --strict -c SHA256SUMS
LISTED=$(grep -c . SHA256SUMS)
FOUND=$(find . -type f ! -name SHA256SUMS | wc -l)
if [ "$LISTED" -ne "$FOUND" ]; then
echo "site-content holds $FOUND files, SHA256SUMS lists $LISTED" >&2; exit 1
fi
echo "VERIFIED $LISTED files"
args:
executable: /bin/bash
register: site_content_check
changed_when: false
- name: "Site content: restore into winpeapps (merge, nothing deleted)"
when: "'VERIFIED' in site_content_check.stdout"
shell: |
set -euo pipefail
SC="{{ usb_root }}/site-content"
S="{{ samba_share }}"
LINES=""
[ -f "$SC/links.txt" ] && LINES=$(grep -v '^#' "$SC/links.txt" | grep . || true)
# Check every link line before anything is copied.
while IFS=$'\t' read -r img name target; do
[ -n "$img" ] || continue
case "$img/$name/$target" in *..*) echo "REFUSED odd link line: $img $name $target" >&2; exit 1 ;; esac
case "$img$name" in */*) echo "REFUSED odd link line: $img $name" >&2; exit 1 ;; esac
case "$target" in _shared/*) ;; *) echo "REFUSED link outside _shared: $target" >&2; exit 1 ;; esac
done <<< "$LINES"
# Count files written (^>f), not folders whose time was touched.
if [ -d "$SC/shared" ]; then
N=$(rsync -rt --chmod=D0755,F0755 -i "$SC/shared/" "$S/_shared/" | { grep -c '^>f' || true; })
echo "shared: wrote $N files"
fi
if [ -d "$SC/images" ]; then
for d in "$SC/images"/*/; do
img=$(basename "$d")
mkdir -p "$S/$img/Deploy"
N=$(rsync -rt --chmod=D0755,F0755 -i "$d" "$S/$img/Deploy/" | { grep -c '^>f' || true; })
echo "$img: wrote $N files"
done
fi
# Links into _shared, as the captured server had them. Only to a
# folder that exists: an empty _shared/Applications/BPRT made here
# would stop the first MCL import from seeding it (bprt_sync).
while IFS=$'\t' read -r img name target; do
[ -n "$img" ] || continue
link="$S/$img/Deploy/Applications/$name"
if [ ! -d "$S/$target" ]; then echo "link later: $img/$name -> $target (not there yet)"; continue; fi
if [ -L "$link" ] && [ "$(readlink "$link")" = "$S/$target" ]; then continue; fi
if [ -e "$link" ] && [ ! -L "$link" ]; then echo "KEPT real folder $link (not replaced by a link)"; continue; fi
mkdir -p "$S/$img/Deploy/Applications"
ln -sfn "$S/$target" "$link"
echo "link: $img/$name -> $target"
done <<< "$LINES"
args:
executable: /bin/bash
register: site_content_restore
changed_when: site_content_restore.stdout is search('wrote [1-9]|^link:', multiline=True)
- name: "Site content: what was restored"
debug:
var: site_content_restore.stdout_lines
when: site_content_restore.stdout_lines is defined
- name: "Deploy BIOS check script + manifest to winpeapps/_shared/BIOS/"
# Path matches what startnet.cmd reads at WinPE boot:
# net use B: \\172.16.9.1\winpeapps\_shared
@@ -1121,6 +1197,9 @@
shell: >
if [ -d "{{ usb_root }}/bios" ]; then
cp -f {{ usb_root }}/bios/*.exe /srv/samba/winpeapps/_shared/BIOS/ 2>/dev/null || true
# models.txt from the captured server pairs models with these files;
# the repo copy installed above is older (the Dell update rewrites it).
[ -f "{{ usb_root }}/bios/models.txt" ] && cp -f "{{ usb_root }}/bios/models.txt" /srv/samba/winpeapps/_shared/BIOS/models.txt
count=$(find /srv/samba/winpeapps/_shared/BIOS -name '*.exe' | wc -l)
echo "Deployed $count BIOS binaries"
else