Files
pxe-server/scripts/mirror-from-gold.sh
cproudlock eb68793e79 Stage 2a: unified GE-Enforce framework + share-root mirror
Consolidates per-type enforcers (CMM, Keyence, Machine, Common, Acrobat)
into one dispatcher driven by pc-type.txt + site-config and a share-side
manifest layout. Same share is now the single source of truth for routine
software updates without re-imaging.

Runtime:
  common/GE-Enforce.ps1           SYSTEM scheduled task. Reads
                                   common/manifest.json plus optional
                                   <pcType>/manifest.json and
                                   <pcType-subType>/manifest.json.
                                   Dispatches each entry through the lib.
                                   Writes _outputs/logs/<hostname>/status.json
                                   on the share after each cycle for fleet
                                   monitoring.
  common/Register-GEEnforce.ps1   Task registration. Triggers: AtLogOn +
                                   every 5 min (jittered per-PC from
                                   hostname hash) + daily at 05:45,
                                   13:45, 21:45 EST shift windows.
                                   Unregisters legacy per-type tasks on
                                   install so the two coexist at most for
                                   the duration of a single enforce cycle.
  common/Deploy-GEEnforce.ps1     Retrofit helper for already-imaged PCs
                                   (admin-run; copies runtime + registers
                                   task + optional immediate trigger).

Library (common/lib/Install-FromManifest.ps1):
  - New Type values: PS1, BAT, File, Registry, INF
  - New DetectionMethod values: Always, MarkerFile, ValueMatches, pnputil
  - TargetHostnames filter (exact + -like wildcards, ANDed with PCTypes)
  - Schema version check (logs WARN on manifest newer than lib MAJOR)
  - Auto-writes MarkerFile on successful one-shot PS1/BAT/CMD runs
  - MSI log scan on failure surfaces meaningful install errors
  - Lib version bumped 2.0 -> 2.1 for TargetHostnames

Observability:
  common/monitor-fleet-status.py  Scans _outputs/logs/*/status.json for
                                   stale check-ins, failed scopes, and
                                   version drift. Respects scope (dir-name),
                                   PCTypes, and TargetHostnames filters so
                                   entries excluded from a PC do not
                                   false-flag as drift.

Regression harness:
  common/test/                    Parameterized VM harness + README
                                   covering every action type plus
                                   rollback, bad/missing SFLD creds, and
                                   schema versioning.

Imaging integration:
  Run-ShopfloorSetup.ps1 now stages GE-Enforce.ps1 and lib to
  C:\Program Files\GE\Shopfloor\ and invokes Register-GEEnforce.ps1
  at the end of setup. Legacy Register-CommonEnforce invocation is
  kept for the transition; it and the legacy per-type enforcer files
  are dead code once Register-GEEnforce runs and will be removed in a
  dedicated cleanup pass.

Standard-Machine manifest:
  eDNC entry bumped 6.4.3 -> 6.4.5. DetectionValue pinned to the
  4-part FileVersion 6.4.5.0 verified against a fresh install in the
  Win11 analyzer VM. UDC DetectionValue pinned to 1.0.34 (registry
  stores 3-part for UDC; verified live).

scripts/mirror-from-gold.sh:
  Restructured around share-root rsyncs (one pass per Samba share)
  to close gaps in the prior per-subdir layout: winpeapps/_shared/
  Applications (7.5 GB of Adobe + fonts + Java + Office + OpenText
  + printdrivers + wireless + Zscaler), additional winpeapps image
  types, and enrollment flat-layout root files. Adds
  --skip-clonezilla and --skip-reports.

Verified end-to-end in the Win11 analyzer VM:
  - Every action Type and DetectionMethod round-tripped
  - PCTypes filter (Oracle excluded on Shopfloor, Firefox included
    on Shopfloor and DESKTOP-*, excluded elsewhere)
  - TargetHostnames filter (exact, wildcard, no-match)
  - Upgrade path: XML hash bump + fleet re-copy
  - Rollback path: history-archive restore propagates via enforcer,
    fleet converges back without per-PC intervention
  - Status writeback + monitor script drift detection
  - Graceful degradation on bad creds, missing creds, share
    unreachable (all exit 0, log clearly, retry next cycle)

Not in this commit (follow-ups):
  - Retire legacy per-type *-Enforce.ps1 files and simplify
    09-Setup-*.ps1 scripts (coordinated multi-file cleanup)
  - Stage 2b: InUseCheck close-and-reopen, ApplyMode gating,
    UpdateWindow, .apply-now.txt sentinel, BITS pre-staging,
    1618 mutex retry, PostInstallCheck, Uninstall action
  - Management app (manifest CRUD + deploy + rollback + fleet view)
  - ShopFloor autologon persistence bug (deferred for next imaging
    attempt with live registry evidence)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 11:19:23 -04:00

154 lines
5.5 KiB
Bash
Executable File

#!/bin/bash
#
# mirror-from-gold.sh - Byte-identical mirror of /srv/samba/ from an existing
# PXE server (GOLD) onto a freshly-installed PXE server.
#
# Run this ON THE NEW PXE SERVER, pointing at the GOLD server's IP.
# It pulls every Samba share (winpeapps, enrollment, blancco-reports,
# clonezilla) wholesale so the new box matches GOLD regardless of whether
# content lives in the flat layout or the new taxonomy layout.
#
# Usage:
# sudo ./mirror-from-gold.sh <GOLD_IP> [options]
#
# Options:
# --skip-drivers Do not mirror Out-of-box Drivers trees (saves ~178G).
# --skip-dell10 Do not mirror Dell_10 drivers (saves ~179G).
# --skip-latitude Do not mirror Latitude drivers (saves ~48G).
# --skip-os Do not mirror shared Operating Systems (saves ~22G).
# --skip-clonezilla Do not mirror clonezilla backup images (can be huge).
# --skip-reports Do not mirror blancco-reports history.
# --dry-run Show what would transfer without doing it.
#
# Prereqs:
# - GOLD's pxe user accepts the SSH key this script generates.
# - GOLD's filesystem is world-readable for the paths involved
# (it is, by default).
# Note: not using `set -e`. rsync legitimately exits non-zero (e.g. 23
# "some files/attrs were not transferred") when source dirs have files
# the rsync user cannot read (the OpenText W10shortcuts dir on GOLD is
# pxe-upload-group-only). We log and continue instead of aborting.
set -uo pipefail
GOLD="${1:-}"
shift || true
if [ -z "$GOLD" ]; then
echo "Usage: $0 <GOLD_IP> [--skip-drivers|--skip-dell10|--skip-latitude|--skip-os|--dry-run]"
exit 1
fi
if [ "$(id -u)" -ne 0 ]; then
echo "ERROR: must run as root (sudo)."
exit 1
fi
SKIP_DRIVERS=0
SKIP_DELL10=0
SKIP_LATITUDE=0
SKIP_OS=0
SKIP_CLONEZILLA=0
SKIP_REPORTS=0
DRY_RUN=""
while [ $# -gt 0 ]; do
case "$1" in
--skip-drivers) SKIP_DRIVERS=1 ;;
--skip-dell10) SKIP_DELL10=1 ;;
--skip-latitude) SKIP_LATITUDE=1 ;;
--skip-os) SKIP_OS=1 ;;
--skip-clonezilla) SKIP_CLONEZILLA=1 ;;
--skip-reports) SKIP_REPORTS=1 ;;
--dry-run) DRY_RUN="--dry-run" ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
shift
done
KEY=/root/.ssh/pxe-mirror-key
if [ ! -f "$KEY" ]; then
echo "[setup] Generating mirror SSH key at $KEY"
mkdir -p /root/.ssh && chmod 700 /root/.ssh
ssh-keygen -t ed25519 -N '' -f "$KEY" -q
echo
echo "Copy the following public key into pxe@$GOLD's ~/.ssh/authorized_keys"
echo "(easiest: ssh pxe@$GOLD 'mkdir -p ~/.ssh && chmod 700 ~/.ssh' and then"
echo " scp '$KEY.pub' pxe@$GOLD:~/.ssh/authorized_keys, then chmod 600 there)"
echo
cat "$KEY.pub"
echo
read -rp "Press enter once the key is installed on GOLD..."
fi
RSH="ssh -i $KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
# Quick connectivity check
echo "[check] Testing key-based SSH to pxe@$GOLD..."
$RSH "pxe@$GOLD" "whoami" >/dev/null || {
echo "ERROR: SSH to pxe@$GOLD failed. Install $KEY.pub on GOLD first."
exit 1
}
echo " OK"
mirror() {
local label="$1"
local src="$2"
local dest="$3"
shift 3
echo
echo "==== $label ===="
echo " src: pxe@$GOLD:$src"
echo " dest: $dest"
mkdir -p "$dest"
rsync -aHl --delete --info=progress2 --no-inc-recursive $DRY_RUN "$@" \
-e "$RSH" \
"pxe@$GOLD:$src" "$dest" || \
echo " WARNING: rsync exited rc=$? (likely a permissions issue on source); continuing"
}
# ---------- winpeapps share (all image types + _shared) ----------
WINPE_EXCLUDES=()
[ "$SKIP_OS" = "1" ] && WINPE_EXCLUDES+=(--exclude='_shared/gea-Operating Systems')
[ "$SKIP_DRIVERS" = "1" ] && WINPE_EXCLUDES+=(--exclude='_shared/Out-of-box Drivers' --exclude='Out-of-box Drivers')
[ "$SKIP_DELL10" = "1" ] && WINPE_EXCLUDES+=(--exclude='Dell_10')
[ "$SKIP_LATITUDE" = "1" ] && WINPE_EXCLUDES+=(--exclude='Latitude')
mirror "winpeapps (all image types, _shared, tools)" \
"/srv/samba/winpeapps/" \
"/srv/samba/winpeapps/" \
"${WINPE_EXCLUDES[@]}"
# ---------- enrollment share (flat-layout root + taxonomy subdirs) ----------
mirror "enrollment (taxonomy + flat-layout content)" \
"/srv/samba/enrollment/" \
"/srv/samba/enrollment/"
# ---------- blancco-reports share (historical XML reports) ----------
if [ "$SKIP_REPORTS" = "0" ]; then
mirror "blancco-reports (erasure report history)" \
"/srv/samba/blancco-reports/" \
"/srv/samba/blancco-reports/"
fi
# ---------- clonezilla share (disk backup images, can be very large) ----------
if [ "$SKIP_CLONEZILLA" = "0" ]; then
mirror "clonezilla (disk backup images)" \
"/srv/samba/clonezilla/" \
"/srv/samba/clonezilla/"
fi
# Permissions: make sure everything we pulled is readable by the share
chown -R root:root /srv/samba/enrollment /srv/samba/winpeapps /srv/samba/blancco-reports /srv/samba/clonezilla 2>/dev/null || true
find /srv/samba/enrollment /srv/samba/winpeapps -type d -exec chmod 0755 {} \; 2>/dev/null || true
find /srv/samba/enrollment /srv/samba/winpeapps -type f -exec chmod 0644 {} \; 2>/dev/null || true
find /srv/samba/enrollment -name '*.ppkg' -exec chmod 0755 {} \; 2>/dev/null || true
echo
echo "============================================"
echo "Mirror complete. Verify with:"
echo " du -sh /srv/samba/winpeapps/_shared/*"
echo " du -sh /srv/samba/enrollment/*"
echo " df -h /srv"
echo "============================================"