BIOS check fix, parallel downloads, shopfloor hardening
- Fix check-bios.cmd: replace parenthesized if blocks with goto labels (cmd.exe fails silently with if/else on network-mapped drives) - Move BIOS check files to winpeapps/_shared/BIOS for reliable SMB access - Add network wait loop before BIOS check in startnet.cmd - Show firmware status in WinPE menu header (BIOS_STATUS variable) - Add BypassNRO registry key to skip OOBE network requirement - Refactor download-drivers.py with --parallel N flag (ThreadPoolExecutor) - Set SupportUser AutoLogonCount to 3 in shopfloor unattend - Add shutdown -a at start + shutdown /r /t 10 at end of Run-ShopfloorSetup.ps1 - Switch download-drivers.py from wget to curl for reliable stall detection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
814
download-drivers.py
Executable file
814
download-drivers.py
Executable file
@@ -0,0 +1,814 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
download-drivers.py — Download Dell drivers (+ BIOS) and push to PXE server
|
||||||
|
|
||||||
|
Downloads driver packs directly from Dell's public catalog (downloads.dell.com).
|
||||||
|
Matches models from user_selections.json / HardwareDriver.json against Dell's
|
||||||
|
DriverPackCatalog. No GE network or Media Creator Lite required.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
./download-drivers.py # download + push selected drivers
|
||||||
|
./download-drivers.py --list # preview without downloading
|
||||||
|
./download-drivers.py --bios # also download BIOS updates
|
||||||
|
./download-drivers.py --image gea-standard # push directly to an image
|
||||||
|
./download-drivers.py --force # re-download even if on server
|
||||||
|
./download-drivers.py --parallel 4 # process 4 packs concurrently
|
||||||
|
|
||||||
|
Requires: curl, 7z, sshpass, rsync
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import concurrent.futures
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import threading
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO_DIR = Path(__file__).resolve().parent
|
||||||
|
PXE_HOST = "10.9.100.1"
|
||||||
|
PXE_USER = "pxe"
|
||||||
|
PXE_PASS = "pxe"
|
||||||
|
UPLOAD_DEST = "/home/pxe/image-upload"
|
||||||
|
IMAGE_BASE = "/srv/samba/winpeapps"
|
||||||
|
|
||||||
|
DELL_DRIVER_CATALOG = "https://downloads.dell.com/catalog/DriverPackCatalog.cab"
|
||||||
|
DELL_BIOS_CATALOG = "https://downloads.dell.com/catalog/DellSDPCatalogPC.cab"
|
||||||
|
DELL_BASE = "https://downloads.dell.com"
|
||||||
|
NS = {"d": "openmanage/cm/dm"}
|
||||||
|
SDP_CAT_NS = "http://schemas.microsoft.com/sms/2005/04/CorporatePublishing/SystemsManagementCatalog.xsd"
|
||||||
|
SDP_PKG_NS = "http://schemas.microsoft.com/wsus/2005/04/CorporatePublishing/SoftwareDistributionPackage.xsd"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def format_size(n):
|
||||||
|
if n >= 1024**3: return f"{n / 1024**3:.1f} GB"
|
||||||
|
if n >= 1024**2: return f"{n / 1024**2:.0f} MB"
|
||||||
|
return f"{n / 1024:.0f} KB"
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_dest_dir(d):
|
||||||
|
"""Convert *destinationdir*\\Deploy\\... to Deploy/..."""
|
||||||
|
return d.replace("*destinationdir*\\", "").replace("*destinationdir*", "").replace("\\", "/")
|
||||||
|
|
||||||
|
|
||||||
|
def ssh_cmd(host, cmd):
|
||||||
|
return subprocess.run(
|
||||||
|
["sshpass", "-p", PXE_PASS, "ssh", "-o", "StrictHostKeyChecking=no",
|
||||||
|
"-o", "LogLevel=ERROR", f"{PXE_USER}@{host}", cmd],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def verify_sha256(filepath, expected):
|
||||||
|
sha = hashlib.sha256()
|
||||||
|
with open(filepath, "rb") as f:
|
||||||
|
for chunk in iter(lambda: f.read(1024 * 1024), b""):
|
||||||
|
sha.update(chunk)
|
||||||
|
return sha.hexdigest().upper() == expected.upper()
|
||||||
|
|
||||||
|
|
||||||
|
def extract_model_ids(name):
|
||||||
|
"""Extract model identifiers like '5450', 'PC14250', 'QCM1250'."""
|
||||||
|
ids = set(re.findall(r'\b([A-Z]*\d[\w]{2,})\b', name, re.I))
|
||||||
|
# Dell uses Qx* codenames where GE uses QC*/QB* (e.g. QxM1250 = QCM1250)
|
||||||
|
extras = set()
|
||||||
|
for mid in ids:
|
||||||
|
if re.match(r'^Q[A-Z][A-Z]\d', mid, re.I):
|
||||||
|
extras.add("Qx" + mid[2:]) # QCM1250 -> QxM1250
|
||||||
|
elif re.match(r'^Qx[A-Z]\d', mid, re.I):
|
||||||
|
pass # already in Qx form, will match directly
|
||||||
|
return ids | extras
|
||||||
|
|
||||||
|
|
||||||
|
def get_brand(name):
|
||||||
|
lower = name.lower()
|
||||||
|
for b in ["latitude", "precision", "optiplex", "pro max", "pro"]:
|
||||||
|
if b in lower:
|
||||||
|
return b
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Catalog download + parsing
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def download_and_extract_cab(url, tmpdir):
|
||||||
|
"""Download a .cab, extract with 7z, return path to XML inside."""
|
||||||
|
cab = os.path.join(tmpdir, os.path.basename(url))
|
||||||
|
print(f" Fetching {os.path.basename(url)}...", end=" ", flush=True)
|
||||||
|
r = subprocess.run(["wget", "-q", "-O", cab, url])
|
||||||
|
if r.returncode != 0:
|
||||||
|
print("FAILED"); return None
|
||||||
|
subprocess.run(["7z", "x", "-y", f"-o{tmpdir}", cab],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
os.remove(cab)
|
||||||
|
xml_name = os.path.basename(url).replace(".cab", ".xml")
|
||||||
|
xml_path = os.path.join(tmpdir, xml_name)
|
||||||
|
if os.path.exists(xml_path):
|
||||||
|
print("OK"); return xml_path
|
||||||
|
print("FAILED (XML not found)"); return None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_driver_catalog(xml_path, os_filter=None):
|
||||||
|
"""Parse DriverPackCatalog.xml → list of driver pack dicts.
|
||||||
|
os_filter: list of OS prefixes to match, e.g. ["Windows10", "Windows11"].
|
||||||
|
Defaults to ["Windows10", "Windows11"] (both).
|
||||||
|
"""
|
||||||
|
if os_filter is None:
|
||||||
|
os_filter = ["Windows10", "Windows11"]
|
||||||
|
tree = ET.parse(xml_path)
|
||||||
|
packs = []
|
||||||
|
for pkg in tree.getroot().findall(".//d:DriverPackage", NS):
|
||||||
|
if pkg.get("type") != "win":
|
||||||
|
continue
|
||||||
|
os_codes = [o.get("osCode", "") for o in pkg.findall(".//d:OperatingSystem", NS)]
|
||||||
|
if not any(code.startswith(prefix) for code in os_codes for prefix in os_filter):
|
||||||
|
continue
|
||||||
|
models = []
|
||||||
|
for m in pkg.findall(".//d:Model", NS):
|
||||||
|
d = m.find("d:Display", NS)
|
||||||
|
models.append({
|
||||||
|
"name": m.get("name", ""),
|
||||||
|
"display": d.text.strip() if d is not None and d.text else ""
|
||||||
|
})
|
||||||
|
sha256 = ""
|
||||||
|
for h in pkg.findall(".//d:Cryptography/d:Hash", NS):
|
||||||
|
if h.get("algorithm") == "SHA256":
|
||||||
|
sha256 = h.text; break
|
||||||
|
path = pkg.get("path", "")
|
||||||
|
packs.append({
|
||||||
|
"url": f"{DELL_BASE}/{path}",
|
||||||
|
"filename": path.split("/")[-1],
|
||||||
|
"size": int(pkg.get("size", 0)),
|
||||||
|
"sha256": sha256,
|
||||||
|
"models": models,
|
||||||
|
})
|
||||||
|
return packs
|
||||||
|
|
||||||
|
|
||||||
|
def parse_bios_catalog(xml_path, model_names):
|
||||||
|
"""Parse DellSDPCatalogPC.xml → list of latest BIOS update dicts for given models."""
|
||||||
|
tree = ET.parse(xml_path)
|
||||||
|
root = tree.getroot()
|
||||||
|
bios = {} # model_key → best entry
|
||||||
|
|
||||||
|
for pkg in root.iter(f"{{{SDP_CAT_NS}}}SoftwareDistributionPackage"):
|
||||||
|
title_elem = pkg.find(f".//{{{SDP_PKG_NS}}}Title")
|
||||||
|
if title_elem is None or not title_elem.text:
|
||||||
|
continue
|
||||||
|
title = title_elem.text
|
||||||
|
if "BIOS" not in title:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Find which of our models this BIOS applies to
|
||||||
|
matched_model = None
|
||||||
|
for mname in model_names:
|
||||||
|
for mid in extract_model_ids(mname):
|
||||||
|
if mid in title:
|
||||||
|
matched_model = mname
|
||||||
|
break
|
||||||
|
if matched_model:
|
||||||
|
break
|
||||||
|
if not matched_model:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Extract version from title (e.g., "...BIOS,1.20.1,1.20.1")
|
||||||
|
ver_match = re.search(r",(\d+\.\d+\.\d+)", title)
|
||||||
|
version = ver_match.group(1) if ver_match else "0.0.0"
|
||||||
|
|
||||||
|
# Get download URL
|
||||||
|
origin = pkg.find(f".//{{{SDP_PKG_NS}}}OriginFile")
|
||||||
|
if origin is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
"title": title,
|
||||||
|
"version": version,
|
||||||
|
"filename": origin.get("FileName", ""),
|
||||||
|
"url": origin.get("OriginUri", ""),
|
||||||
|
"size": int(origin.get("Size", 0)),
|
||||||
|
"model": matched_model,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keep latest version per model
|
||||||
|
key = matched_model
|
||||||
|
if key not in bios or version > bios[key]["version"]:
|
||||||
|
bios[key] = entry
|
||||||
|
|
||||||
|
return list(bios.values())
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Model matching
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def find_dell_packs(our_model_name, dell_packs):
|
||||||
|
"""Find Dell driver pack(s) matching one of our model names."""
|
||||||
|
our_ids = extract_model_ids(our_model_name)
|
||||||
|
our_brand = get_brand(our_model_name)
|
||||||
|
our_rugged = "rugged" in our_model_name.lower()
|
||||||
|
if not our_ids:
|
||||||
|
return []
|
||||||
|
|
||||||
|
matches = []
|
||||||
|
for pack in dell_packs:
|
||||||
|
for dm in pack["models"]:
|
||||||
|
dell_ids = extract_model_ids(dm["name"]) | extract_model_ids(dm["display"])
|
||||||
|
if not (our_ids & dell_ids):
|
||||||
|
continue
|
||||||
|
# Brand check: if we specify a brand, Dell must match (or have none)
|
||||||
|
if our_brand:
|
||||||
|
dell_brand = get_brand(dm["name"])
|
||||||
|
if dell_brand and dell_brand != our_brand:
|
||||||
|
continue
|
||||||
|
# Rugged check: if Dell explicitly labels pack as Rugged,
|
||||||
|
# only match our Rugged models (prevents non-rugged 5430 matching
|
||||||
|
# Rugged 5430 pack). If Dell doesn't say Rugged, allow any match
|
||||||
|
# (handles 7220/7230 which are Rugged-only but unlabeled in catalog).
|
||||||
|
dell_rugged = "rugged" in dm["name"].lower() or "rugged" in pack["filename"].lower()
|
||||||
|
if dell_rugged and not our_rugged:
|
||||||
|
continue
|
||||||
|
matches.append(pack)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Deduplicate by URL
|
||||||
|
seen = set()
|
||||||
|
return [m for m in matches if m["url"] not in seen and not seen.add(m["url"])]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Download + push
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def make_zip_name(filename, dest_dir):
|
||||||
|
"""Generate a zip filename matching GE convention: win11_<model>_<ver>.zip"""
|
||||||
|
# Strip extension and version suffix to get base name
|
||||||
|
base = re.sub(r'[_-]Win1[01][_.].*', '', filename, flags=re.I)
|
||||||
|
base = re.sub(r'[-_]', '', base).lower()
|
||||||
|
# Extract version from filename (e.g., A04, A13)
|
||||||
|
ver_match = re.search(r'_A(\d+)', filename, re.I)
|
||||||
|
ver = f"a{ver_match.group(1)}" if ver_match else "a00"
|
||||||
|
return f"win11_{base}_{ver}.zip"
|
||||||
|
|
||||||
|
|
||||||
|
def process_download(args, url, filename, sha256, size, target_dir, label, tmpdir):
|
||||||
|
"""Download, verify, extract, re-zip, and push one driver pack. Returns True on success.
|
||||||
|
Each caller should pass a unique tmpdir to avoid collisions in parallel mode."""
|
||||||
|
local_file = os.path.join(tmpdir, filename)
|
||||||
|
|
||||||
|
# Download
|
||||||
|
print(f" [{label}] Downloading {format_size(size)}...")
|
||||||
|
r = subprocess.run(["curl", "-L", "-s", "-S",
|
||||||
|
"--speed-limit", "1000", "--speed-time", "30",
|
||||||
|
"--retry", "3", "--retry-delay", "5",
|
||||||
|
"-o", local_file, url])
|
||||||
|
if r.returncode != 0 or not os.path.exists(local_file):
|
||||||
|
print(f" [{label}] ERROR: Download failed (curl exit {r.returncode})")
|
||||||
|
if os.path.exists(local_file): os.remove(local_file)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Verify hash (if provided)
|
||||||
|
if sha256:
|
||||||
|
print(f" [{label}] Verifying SHA256...", end=" ", flush=True)
|
||||||
|
if not verify_sha256(local_file, sha256):
|
||||||
|
print("MISMATCH!")
|
||||||
|
os.remove(local_file)
|
||||||
|
return False
|
||||||
|
print("OK")
|
||||||
|
|
||||||
|
# Extract locally with 7z (unique subdir per worker)
|
||||||
|
extract_dir = os.path.join(tmpdir, "extract")
|
||||||
|
os.makedirs(extract_dir, exist_ok=True)
|
||||||
|
print(f" [{label}] Extracting...", end=" ", flush=True)
|
||||||
|
r = subprocess.run(["7z", "x", "-y", f"-o{extract_dir}", local_file],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
os.remove(local_file)
|
||||||
|
if r.returncode != 0:
|
||||||
|
print(f"FAILED: {r.stderr[:200]}")
|
||||||
|
subprocess.run(["rm", "-rf", extract_dir])
|
||||||
|
return False
|
||||||
|
print("OK")
|
||||||
|
|
||||||
|
# Re-zip for PESetup.exe (expects zipped driver packs, not loose files)
|
||||||
|
zip_name = make_zip_name(filename, target_dir)
|
||||||
|
zip_path = os.path.join(tmpdir, zip_name)
|
||||||
|
print(f" [{label}] Zipping as {zip_name}...", end=" ", flush=True)
|
||||||
|
r = subprocess.run(["zip", "-r", "-q", zip_path, "."],
|
||||||
|
cwd=extract_dir)
|
||||||
|
subprocess.run(["rm", "-rf", extract_dir])
|
||||||
|
if r.returncode != 0:
|
||||||
|
print("FAILED")
|
||||||
|
return False
|
||||||
|
zip_size = os.path.getsize(zip_path)
|
||||||
|
print(f"OK ({format_size(zip_size)})")
|
||||||
|
|
||||||
|
# Push zip to PXE server
|
||||||
|
print(f" [{label}] Pushing to {target_dir}/{zip_name}...")
|
||||||
|
ssh_cmd(args.server, f"mkdir -p '{target_dir}'")
|
||||||
|
r = subprocess.run([
|
||||||
|
"rsync", "-a",
|
||||||
|
"-e", f"sshpass -p {PXE_PASS} ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR",
|
||||||
|
zip_path, f"{PXE_USER}@{args.server}:{target_dir}/"
|
||||||
|
])
|
||||||
|
os.remove(zip_path)
|
||||||
|
|
||||||
|
if r.returncode != 0:
|
||||||
|
print(f" [{label}] ERROR: rsync failed")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Main
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Download Dell drivers (+ BIOS) and push to PXE server")
|
||||||
|
parser.add_argument("--list", action="store_true",
|
||||||
|
help="Preview without downloading")
|
||||||
|
parser.add_argument("--bios", action="store_true",
|
||||||
|
help="Also download BIOS updates")
|
||||||
|
parser.add_argument("--image",
|
||||||
|
help="Push directly to image type (e.g. gea-standard)")
|
||||||
|
parser.add_argument("--server", default=PXE_HOST,
|
||||||
|
help=f"PXE server IP (default: {PXE_HOST})")
|
||||||
|
parser.add_argument("--force", action="store_true",
|
||||||
|
help="Re-download even if already on server")
|
||||||
|
parser.add_argument("--cache-path",
|
||||||
|
help="Path to local image dir with Deploy/Control/ and Tools/")
|
||||||
|
parser.add_argument("--local",
|
||||||
|
help="Download to local directory (no server needed)")
|
||||||
|
parser.add_argument("--parallel", type=int, default=1, metavar="N",
|
||||||
|
help="Process N packs concurrently (default: 1)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# --- Load our model selections ---
|
||||||
|
control_dir = tools_dir = None
|
||||||
|
if args.cache_path:
|
||||||
|
p = Path(args.cache_path)
|
||||||
|
control_dir = p / "Deploy" / "Control"
|
||||||
|
tools_dir = p / "Tools" if (p / "Tools").is_dir() else p.parent / "Tools"
|
||||||
|
else:
|
||||||
|
for d in sorted(REPO_DIR.iterdir()):
|
||||||
|
if d.is_dir() and (d / "Deploy" / "Control" / "HardwareDriver.json").exists():
|
||||||
|
control_dir = d / "Deploy" / "Control"
|
||||||
|
tools_dir = d / "Tools"
|
||||||
|
break
|
||||||
|
if not control_dir or not (control_dir / "HardwareDriver.json").exists():
|
||||||
|
sys.exit("ERROR: HardwareDriver.json not found. Use --cache-path or ensure a local image dir exists.")
|
||||||
|
if not (tools_dir / "user_selections.json").exists():
|
||||||
|
sys.exit("ERROR: user_selections.json not found")
|
||||||
|
|
||||||
|
with open(control_dir / "HardwareDriver.json") as f:
|
||||||
|
hw_drivers = json.load(f)
|
||||||
|
with open(tools_dir / "user_selections.json") as f:
|
||||||
|
selections = json.load(f)[0]
|
||||||
|
|
||||||
|
os_id = selections["OperatingSystemSelection"]
|
||||||
|
selected_families = set(m["Id"] for m in selections["HardwareModelSelection"])
|
||||||
|
|
||||||
|
# Filter to selected + matching OS
|
||||||
|
our_entries = [d for d in hw_drivers
|
||||||
|
if d["family"] in selected_families and os_id in d.get("aOsIds", [])]
|
||||||
|
|
||||||
|
# Collect unique model names and their DestinationDirs
|
||||||
|
model_dest_map = {} # model_name → dest_dir
|
||||||
|
for entry in our_entries:
|
||||||
|
dest = resolve_dest_dir(entry["DestinationDir"])
|
||||||
|
for m in entry["models"].split(","):
|
||||||
|
m = m.strip()
|
||||||
|
if m not in model_dest_map:
|
||||||
|
model_dest_map[m] = dest
|
||||||
|
|
||||||
|
base_path = f"{IMAGE_BASE}/{args.image}" if args.image else UPLOAD_DEST
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 60)
|
||||||
|
print(" Dell Driver Downloader for PXE Server")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# --- Download Dell catalog ---
|
||||||
|
with tempfile.TemporaryDirectory(prefix="dell-catalog-") as catdir:
|
||||||
|
xml_path = download_and_extract_cab(DELL_DRIVER_CATALOG, catdir)
|
||||||
|
if not xml_path:
|
||||||
|
sys.exit("ERROR: Could not download Dell driver catalog")
|
||||||
|
dell_packs = parse_driver_catalog(xml_path)
|
||||||
|
print(f" Catalog: {len(dell_packs)} Win11 driver packs available")
|
||||||
|
|
||||||
|
bios_updates = []
|
||||||
|
if args.bios:
|
||||||
|
bios_xml = download_and_extract_cab(DELL_BIOS_CATALOG, catdir)
|
||||||
|
if bios_xml:
|
||||||
|
bios_updates = parse_bios_catalog(bios_xml, list(model_dest_map.keys()))
|
||||||
|
print(f" BIOS: {len(bios_updates)} update(s) found")
|
||||||
|
|
||||||
|
# --- Match our models to Dell catalog ---
|
||||||
|
# Group: dest_dir → list of Dell packs to download
|
||||||
|
download_plan = [] # list of {dell_pack, dest_dir, our_models}
|
||||||
|
unmatched = []
|
||||||
|
seen_urls = set()
|
||||||
|
dest_seen = {} # dest_dir → set of URLs already planned
|
||||||
|
|
||||||
|
for model_name, dest_dir in model_dest_map.items():
|
||||||
|
matches = find_dell_packs(model_name, dell_packs)
|
||||||
|
if not matches:
|
||||||
|
unmatched.append(model_name)
|
||||||
|
continue
|
||||||
|
for pack in matches:
|
||||||
|
if pack["url"] in seen_urls:
|
||||||
|
continue
|
||||||
|
seen_urls.add(pack["url"])
|
||||||
|
download_plan.append({
|
||||||
|
"pack": pack,
|
||||||
|
"dest_dir": dest_dir,
|
||||||
|
"model": model_name,
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- Display plan ---
|
||||||
|
print()
|
||||||
|
total_drv_size = sum(d["pack"]["size"] for d in download_plan)
|
||||||
|
print(f" Drivers: {len(download_plan)} pack(s) to download ({format_size(total_drv_size)})")
|
||||||
|
print(f" Target: {args.server}:{base_path}")
|
||||||
|
if unmatched:
|
||||||
|
print(f" No Dell match: {len(unmatched)} model(s)")
|
||||||
|
print()
|
||||||
|
|
||||||
|
for i, d in enumerate(download_plan, 1):
|
||||||
|
p = d["pack"]
|
||||||
|
print(f" {i:3}. {d['model']:<38} {format_size(p['size']):>8} {p['filename']}")
|
||||||
|
print(f" -> {d['dest_dir']}")
|
||||||
|
|
||||||
|
if unmatched:
|
||||||
|
print()
|
||||||
|
print(f" Unmatched models (not in Dell public catalog):")
|
||||||
|
for m in unmatched:
|
||||||
|
print(f" - {m}")
|
||||||
|
|
||||||
|
if bios_updates:
|
||||||
|
total_bios = sum(b["size"] for b in bios_updates)
|
||||||
|
print()
|
||||||
|
print(f" BIOS updates: {len(bios_updates)} ({format_size(total_bios)})")
|
||||||
|
for b in bios_updates:
|
||||||
|
print(f" {b['model']:<35} v{b['version']} {b['filename']}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
if args.list:
|
||||||
|
print(" (--list mode, nothing downloaded)")
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- LOCAL MODE: download to local directory ---
|
||||||
|
if args.local:
|
||||||
|
local_dir = Path(args.local)
|
||||||
|
drv_dir = local_dir / "drivers"
|
||||||
|
bios_local_dir = local_dir / "bios"
|
||||||
|
drv_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Load local manifest
|
||||||
|
manifest_path = local_dir / "manifest.json"
|
||||||
|
manifest = json.loads(manifest_path.read_text()) if manifest_path.exists() else {}
|
||||||
|
|
||||||
|
# Thread-safe counters and manifest access
|
||||||
|
_lock = threading.Lock()
|
||||||
|
counters = {"completed": 0, "skipped": 0, "errors": 0}
|
||||||
|
|
||||||
|
# Build GE filename mapping from our HardwareDriver.json entries
|
||||||
|
ge_filename_map = {} # model_name → GE FileName
|
||||||
|
for entry in our_entries:
|
||||||
|
fn = entry.get("FileName") or entry.get("fileName", "")
|
||||||
|
dest = resolve_dest_dir(entry.get("DestinationDir") or entry.get("destinationDir", ""))
|
||||||
|
for m in (entry.get("models") or entry.get("modelswminame", "")).split(","):
|
||||||
|
m = m.strip()
|
||||||
|
if m and fn:
|
||||||
|
ge_filename_map[m] = {"filename": fn, "dest_dir": dest}
|
||||||
|
|
||||||
|
def _download_one_local(i, d):
|
||||||
|
"""Download a single driver pack (local mode). Thread-safe."""
|
||||||
|
pack = d["pack"]
|
||||||
|
tag = f"[{i}/{len(download_plan)}]"
|
||||||
|
|
||||||
|
with _lock:
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
print(f"{tag} {d['model']} ({format_size(pack['size'])})")
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
|
||||||
|
# Check if already downloaded (manifest or file size match)
|
||||||
|
local_file = drv_dir / pack["filename"]
|
||||||
|
if not args.force:
|
||||||
|
with _lock:
|
||||||
|
existing_hash = manifest.get("drivers", {}).get(pack["url"])
|
||||||
|
if existing_hash == pack["sha256"]:
|
||||||
|
with _lock:
|
||||||
|
print(f"{tag} Already downloaded (hash matches)")
|
||||||
|
counters["skipped"] += 1
|
||||||
|
return
|
||||||
|
if local_file.exists() and local_file.stat().st_size == pack["size"]:
|
||||||
|
with _lock:
|
||||||
|
print(f"{tag} Already downloaded (size matches)")
|
||||||
|
manifest.setdefault("drivers", {})[pack["url"]] = pack["sha256"]
|
||||||
|
counters["skipped"] += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
# Download raw .exe to drivers/
|
||||||
|
with _lock:
|
||||||
|
print(f"{tag} Downloading {format_size(pack['size'])}...")
|
||||||
|
r = subprocess.run(["curl", "-L", "-s", "-S",
|
||||||
|
"--speed-limit", "1000", "--speed-time", "30",
|
||||||
|
"--retry", "3", "--retry-delay", "5",
|
||||||
|
"-o", str(local_file), pack["url"]])
|
||||||
|
if r.returncode != 0 or not local_file.exists():
|
||||||
|
with _lock:
|
||||||
|
print(f"{tag} ERROR: Download failed (curl exit {r.returncode})")
|
||||||
|
counters["errors"] += 1
|
||||||
|
if local_file.exists(): local_file.unlink()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Verify size first
|
||||||
|
actual_size = local_file.stat().st_size
|
||||||
|
if pack["size"] and actual_size != pack["size"]:
|
||||||
|
with _lock:
|
||||||
|
print(f"{tag} ERROR: Size mismatch (got {format_size(actual_size)}, expected {format_size(pack['size'])})")
|
||||||
|
counters["errors"] += 1
|
||||||
|
local_file.unlink()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Verify hash
|
||||||
|
if pack["sha256"]:
|
||||||
|
with _lock:
|
||||||
|
print(f"{tag} Verifying SHA256...", end=" ", flush=True)
|
||||||
|
if not verify_sha256(str(local_file), pack["sha256"]):
|
||||||
|
with _lock:
|
||||||
|
print("MISMATCH!")
|
||||||
|
counters["errors"] += 1
|
||||||
|
local_file.unlink()
|
||||||
|
return
|
||||||
|
with _lock:
|
||||||
|
print("OK")
|
||||||
|
|
||||||
|
ge_info = ge_filename_map.get(d["model"], {})
|
||||||
|
with _lock:
|
||||||
|
counters["completed"] += 1
|
||||||
|
manifest.setdefault("drivers", {})[pack["url"]] = pack["sha256"]
|
||||||
|
manifest.setdefault("mapping", {})[pack["filename"]] = {
|
||||||
|
"model": d["model"],
|
||||||
|
"dell_filename": pack["filename"],
|
||||||
|
"ge_filename": ge_info.get("filename", ""),
|
||||||
|
"dest_dir": d["dest_dir"],
|
||||||
|
"sha256": pack["sha256"],
|
||||||
|
"size": pack["size"],
|
||||||
|
}
|
||||||
|
print(f"{tag} Done.")
|
||||||
|
|
||||||
|
workers = max(1, args.parallel)
|
||||||
|
if workers > 1:
|
||||||
|
print(f" Downloading with {workers} parallel workers")
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool:
|
||||||
|
futures = [pool.submit(_download_one_local, i, d)
|
||||||
|
for i, d in enumerate(download_plan, 1)]
|
||||||
|
concurrent.futures.wait(futures)
|
||||||
|
|
||||||
|
# --- Download BIOS ---
|
||||||
|
bios_ok = bios_err = 0
|
||||||
|
if bios_updates:
|
||||||
|
bios_local_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
print(f" BIOS Updates -> {bios_local_dir}")
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
|
||||||
|
def _download_one_bios(b):
|
||||||
|
nonlocal bios_ok, bios_err
|
||||||
|
with _lock:
|
||||||
|
print(f"\n {b['model']} v{b['version']}")
|
||||||
|
if not args.force:
|
||||||
|
with _lock:
|
||||||
|
existing = manifest.get("bios", {}).get(b["model"])
|
||||||
|
if existing == b["version"]:
|
||||||
|
with _lock:
|
||||||
|
print(f" Already downloaded (v{b['version']})")
|
||||||
|
return
|
||||||
|
|
||||||
|
local_file = bios_local_dir / b["filename"]
|
||||||
|
with _lock:
|
||||||
|
print(f" [{b['model']}] Downloading {format_size(b['size'])}...")
|
||||||
|
r = subprocess.run(["curl", "-L", "-s", "-S",
|
||||||
|
"--speed-limit", "1000", "--speed-time", "30",
|
||||||
|
"--retry", "3", "--retry-delay", "5",
|
||||||
|
"-o", str(local_file), b["url"]])
|
||||||
|
if r.returncode != 0:
|
||||||
|
with _lock:
|
||||||
|
print(f" [{b['model']}] ERROR: Download failed")
|
||||||
|
bios_err += 1
|
||||||
|
if local_file.exists(): local_file.unlink()
|
||||||
|
return
|
||||||
|
|
||||||
|
with _lock:
|
||||||
|
bios_ok += 1
|
||||||
|
manifest.setdefault("bios", {})[b["model"]] = b["version"]
|
||||||
|
manifest.setdefault("bios_mapping", {})[b["filename"]] = {
|
||||||
|
"model": b["model"],
|
||||||
|
"version": b["version"],
|
||||||
|
"filename": b["filename"],
|
||||||
|
}
|
||||||
|
print(f" [{b['model']}] Done.")
|
||||||
|
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool:
|
||||||
|
futures = [pool.submit(_download_one_bios, b) for b in bios_updates]
|
||||||
|
concurrent.futures.wait(futures)
|
||||||
|
|
||||||
|
# Save manifest
|
||||||
|
manifest_path.write_text(json.dumps(manifest, indent=2))
|
||||||
|
|
||||||
|
# --- Summary ---
|
||||||
|
print()
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
print(f" Summary — Local Download")
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
print(f" Drivers downloaded: {counters['completed']}")
|
||||||
|
if counters["skipped"]: print(f" Drivers skipped: {counters['skipped']} (already have)")
|
||||||
|
if counters["errors"]: print(f" Drivers failed: {counters['errors']}")
|
||||||
|
if bios_updates:
|
||||||
|
print(f" BIOS downloaded: {bios_ok}")
|
||||||
|
if bios_err: print(f" BIOS failed: {bios_err}")
|
||||||
|
print(f" Saved to: {local_dir}")
|
||||||
|
print(f" Manifest: {manifest_path}")
|
||||||
|
print()
|
||||||
|
print(f" To push to server later:")
|
||||||
|
print(f" python3 download-drivers.py --push-local {local_dir}")
|
||||||
|
print()
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- REMOTE MODE: download and push to server ---
|
||||||
|
|
||||||
|
# --- Verify SSH ---
|
||||||
|
print(f" Testing SSH to {args.server}...", end=" ", flush=True)
|
||||||
|
r = ssh_cmd(args.server, "echo OK")
|
||||||
|
if r.stdout.strip() != "OK":
|
||||||
|
print("FAILED")
|
||||||
|
sys.exit(f" Cannot SSH to {PXE_USER}@{args.server}: {r.stderr.strip()}")
|
||||||
|
print("OK")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# --- Load manifest (tracks what's been downloaded by hash) ---
|
||||||
|
manifest_path = f"{base_path}/.driver-manifest.json"
|
||||||
|
r = ssh_cmd(args.server, f"cat '{manifest_path}' 2>/dev/null")
|
||||||
|
manifest = json.loads(r.stdout) if r.stdout.strip() else {}
|
||||||
|
|
||||||
|
# Thread-safe counters and manifest access
|
||||||
|
_lock = threading.Lock()
|
||||||
|
counters = {"completed": 0, "skipped": 0, "errors": 0}
|
||||||
|
|
||||||
|
# --- Download drivers ---
|
||||||
|
with tempfile.TemporaryDirectory(prefix="pxe-drivers-") as tmpdir:
|
||||||
|
|
||||||
|
def _process_one_remote(i, d):
|
||||||
|
"""Download, extract, re-zip, and push one driver pack. Thread-safe."""
|
||||||
|
pack = d["pack"]
|
||||||
|
target = f"{base_path}/{d['dest_dir']}"
|
||||||
|
tag = f"[{i}/{len(download_plan)}]"
|
||||||
|
|
||||||
|
with _lock:
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
print(f"{tag} {d['model']} ({format_size(pack['size'])})")
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
|
||||||
|
if not args.force:
|
||||||
|
with _lock:
|
||||||
|
existing_hash = manifest.get(d["dest_dir"], {}).get(pack["filename"])
|
||||||
|
if existing_hash == pack["sha256"]:
|
||||||
|
with _lock:
|
||||||
|
print(f"{tag} Up to date (hash matches manifest)")
|
||||||
|
counters["skipped"] += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
# Each worker gets its own temp subdirectory
|
||||||
|
worker_tmp = os.path.join(tmpdir, f"worker-{i}")
|
||||||
|
os.makedirs(worker_tmp, exist_ok=True)
|
||||||
|
|
||||||
|
ok = process_download(args, pack["url"], pack["filename"],
|
||||||
|
pack["sha256"], pack["size"], target,
|
||||||
|
d["model"], worker_tmp)
|
||||||
|
with _lock:
|
||||||
|
if ok:
|
||||||
|
counters["completed"] += 1
|
||||||
|
manifest.setdefault(d["dest_dir"], {})[pack["filename"]] = pack["sha256"]
|
||||||
|
print(f"{tag} Done.")
|
||||||
|
else:
|
||||||
|
counters["errors"] += 1
|
||||||
|
|
||||||
|
workers = max(1, args.parallel)
|
||||||
|
if workers > 1:
|
||||||
|
print(f" Processing with {workers} parallel workers")
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool:
|
||||||
|
futures = [pool.submit(_process_one_remote, i, d)
|
||||||
|
for i, d in enumerate(download_plan, 1)]
|
||||||
|
concurrent.futures.wait(futures)
|
||||||
|
|
||||||
|
# --- Download BIOS (goes to enrollment share, shared across all images) ---
|
||||||
|
bios_ok = bios_err = 0
|
||||||
|
bios_dir = "/srv/samba/enrollment/BIOS"
|
||||||
|
if bios_updates:
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
print(f" BIOS Updates -> {bios_dir}")
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
ssh_cmd(args.server, f"mkdir -p '{bios_dir}'")
|
||||||
|
models_txt = [] # lines for models.txt manifest
|
||||||
|
|
||||||
|
def _process_one_bios(b):
|
||||||
|
nonlocal bios_ok, bios_err
|
||||||
|
target = f"{bios_dir}/{b['filename']}"
|
||||||
|
|
||||||
|
with _lock:
|
||||||
|
print(f"\n {b['model']} v{b['version']}")
|
||||||
|
if not args.force:
|
||||||
|
with _lock:
|
||||||
|
existing = manifest.get("BIOS", {}).get(b["model"])
|
||||||
|
if existing == b["version"]:
|
||||||
|
with _lock:
|
||||||
|
print(f" Up to date (v{b['version']})")
|
||||||
|
models_txt.append(f"{b['model']}|{b['filename']}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# BIOS .exe goes as-is (not extracted)
|
||||||
|
bios_tmp = os.path.join(tmpdir, f"bios-{b['filename']}")
|
||||||
|
with _lock:
|
||||||
|
print(f" [{b['model']}] Downloading {format_size(b['size'])}...")
|
||||||
|
r = subprocess.run(["curl", "-L", "-s", "-S",
|
||||||
|
"--speed-limit", "1000", "--speed-time", "30",
|
||||||
|
"--retry", "3", "--retry-delay", "5",
|
||||||
|
"-o", bios_tmp, b["url"]])
|
||||||
|
if r.returncode != 0:
|
||||||
|
with _lock:
|
||||||
|
print(f" [{b['model']}] ERROR: Download failed")
|
||||||
|
bios_err += 1
|
||||||
|
if os.path.exists(bios_tmp): os.remove(bios_tmp)
|
||||||
|
return
|
||||||
|
|
||||||
|
r = subprocess.run([
|
||||||
|
"rsync", "-a",
|
||||||
|
"-e", f"sshpass -p {PXE_PASS} ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR",
|
||||||
|
bios_tmp, f"{PXE_USER}@{args.server}:{target}"
|
||||||
|
])
|
||||||
|
os.remove(bios_tmp)
|
||||||
|
if r.returncode != 0:
|
||||||
|
with _lock:
|
||||||
|
print(f" [{b['model']}] ERROR: Push failed")
|
||||||
|
bios_err += 1
|
||||||
|
else:
|
||||||
|
with _lock:
|
||||||
|
print(f" [{b['model']}] Done.")
|
||||||
|
bios_ok += 1
|
||||||
|
manifest.setdefault("BIOS", {})[b["model"]] = b["version"]
|
||||||
|
models_txt.append(f"{b['model']}|{b['filename']}")
|
||||||
|
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool:
|
||||||
|
futures = [pool.submit(_process_one_bios, b) for b in bios_updates]
|
||||||
|
concurrent.futures.wait(futures)
|
||||||
|
|
||||||
|
# Generate models.txt for check-bios.cmd
|
||||||
|
if models_txt:
|
||||||
|
manifest_content = "# ModelSubstring|BIOSFile\\n" + "\\n".join(models_txt) + "\\n"
|
||||||
|
ssh_cmd(args.server,
|
||||||
|
f"printf '{manifest_content}' > '{bios_dir}/models.txt'")
|
||||||
|
print(f"\n models.txt updated ({len(models_txt)} entries)")
|
||||||
|
|
||||||
|
# --- Save manifest ---
|
||||||
|
completed, skipped, errors = counters["completed"], counters["skipped"], counters["errors"]
|
||||||
|
if completed > 0 or bios_ok > 0:
|
||||||
|
manifest_json = json.dumps(manifest, indent=2)
|
||||||
|
ssh_cmd(args.server,
|
||||||
|
f"cat > '{manifest_path}' << 'MANIFEST_EOF'\n{manifest_json}\nMANIFEST_EOF")
|
||||||
|
print(f" Manifest saved to {manifest_path}")
|
||||||
|
|
||||||
|
# --- Summary ---
|
||||||
|
print()
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
print(f" Summary")
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
print(f" Drivers downloaded: {completed}")
|
||||||
|
if skipped: print(f" Drivers skipped: {skipped} (up to date)")
|
||||||
|
if errors: print(f" Drivers failed: {errors}")
|
||||||
|
if bios_updates:
|
||||||
|
print(f" BIOS downloaded: {bios_ok}")
|
||||||
|
if bios_err: print(f" BIOS failed: {bios_err}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if completed > 0 and not args.image:
|
||||||
|
print(f" Drivers staged in {base_path}/Deploy/Out-of-box Drivers/")
|
||||||
|
print(f" Use the webapp (http://{args.server}:9009) to import,")
|
||||||
|
print(f" or re-run with --image <type> to push directly.")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
166
playbook/FlatUnattendW10-shopfloor.xml
Normal file
166
playbook/FlatUnattendW10-shopfloor.xml
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<unattend xmlns="urn:schemas-microsoft-com:unattend">
|
||||||
|
<settings pass="windowsPE">
|
||||||
|
</settings>
|
||||||
|
<settings pass="offlineServicing">
|
||||||
|
<component name="Microsoft-Windows-PnpCustomizationsNonWinPE" processorArchitecture="*arch*" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
|
||||||
|
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<DriverPaths>
|
||||||
|
<PathAndCredentials xmlns="" wcm:keyValue="1" action="add">
|
||||||
|
<Path>W:\Drivers</Path>
|
||||||
|
</PathAndCredentials>
|
||||||
|
</DriverPaths>
|
||||||
|
</component>
|
||||||
|
</settings>
|
||||||
|
<settings pass="specialize">
|
||||||
|
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="*arch*" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||||
|
<ComputerName>H%serialnumber%</ComputerName>
|
||||||
|
<RegisteredOrganization>GE Aerospace</RegisteredOrganization>
|
||||||
|
<RegisteredOwner>GE</RegisteredOwner>
|
||||||
|
<TimeZone>Eastern Standard Time</TimeZone>
|
||||||
|
</component>
|
||||||
|
<component name="Microsoft-Windows-Deployment" processorArchitecture="*arch*" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||||
|
<RunSynchronous>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>1</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Import-Certificate -FilePath 'C:\Deploy\Applications\GE_External_Root_CA_2_1.cer' -CertStoreLocation 'Cert:\LocalMachine\Root'"</Path>
|
||||||
|
<Description>Install External Root Certificate</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>2</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Import-Certificate -FilePath 'C:\Deploy\Applications\GE_External_Intermediate_CA_2_1.cer' -CertStoreLocation 'Cert:\LocalMachine\CA'"</Path>
|
||||||
|
<Description>Install External Intermediate Certificate</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>3</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Import-Certificate -FilePath 'C:\Deploy\Applications\GE_Enterprise_Root_CA_2_1.cer' -CertStoreLocation 'Cert:\LocalMachine\Root'"</Path>
|
||||||
|
<Description>Install Enterprise Root Certificate</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>4</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Import-Certificate -FilePath 'C:\Deploy\Applications\GE_Enterprise_Device_Issuing_CA_2_1.cer' -CertStoreLocation 'Cert:\LocalMachine\CA'"</Path>
|
||||||
|
<Description>Install Enterprise Device Issuing Certificate</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>5</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Import-Certificate -FilePath 'C:\Deploy\Applications\GE_Enterprise_Server_Issuing_CA_2_1.cer' -CertStoreLocation 'Cert:\LocalMachine\CA'"</Path>
|
||||||
|
<Description>Install Enterprise Server Issuing Certificate</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>6</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Import-Certificate -FilePath 'C:\Deploy\Applications\GE_Enterprise_Smart_Card_Issuing_CA_2_1.cer' -CertStoreLocation 'Cert:\LocalMachine\CA'"</Path>
|
||||||
|
<Description>Install Enterprise SmartCard Issuing Certificate</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>7</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Import-Certificate -FilePath 'C:\Deploy\Applications\GE_Enterprise_User_Issuing_CA_2_1.cer' -CertStoreLocation 'Cert:\LocalMachine\CA'"</Path>
|
||||||
|
<Description>Install Enterprise User Issuing Certificate</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>8</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Import-Certificate -FilePath 'C:\Deploy\Applications\GE_Aerospace_Enterprise_Root_CA_1.cer' -CertStoreLocation 'Cert:\LocalMachine\Root'"</Path>
|
||||||
|
<Description>Install Aerospace Enterprise Root Certificate</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>9</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Import-Certificate -FilePath 'C:\Deploy\Applications\ZscalerCommercialCertificate-2048-SHA256.crt' -CertStoreLocation 'Cert:\LocalMachine\Root'"</Path>
|
||||||
|
<Description>Install Zscaler Commercial Certificate</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Path>reg.exe add "HKLM\System\CurrentControlSet\Control\Network\NewNetworkWindowOff" /f </Path>
|
||||||
|
<Description>Disable Network Windows</Description>
|
||||||
|
<Order>10</Order>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>11</Order>
|
||||||
|
<Path>netsh wlan add profile filename="C:\Deploy\Applications\extra\wireless\WiFi-Profile.xml" user=all</Path>
|
||||||
|
<Description>Install INTERNETACCESS WiFi Profile</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>12</Order>
|
||||||
|
<Path>powershell.exe -ExecutionPolicy Bypass -Command "Enable-PSRemoting -Force -SkipNetworkProfileCheck"</Path>
|
||||||
|
<Description>Enable WinRM</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>13</Order>
|
||||||
|
<Path>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE" /v BypassNRO /t REG_DWORD /d 1 /f</Path>
|
||||||
|
<Description>Bypass OOBE network requirement</Description>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
</RunSynchronous>
|
||||||
|
</component>
|
||||||
|
</settings>
|
||||||
|
<settings pass="oobeSystem">
|
||||||
|
<component name="Microsoft-Windows-International-Core" processorArchitecture="*arch*" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||||
|
<InputLocale>en-US</InputLocale>
|
||||||
|
<SystemLocale>en-US</SystemLocale>
|
||||||
|
<UILanguage>en-US</UILanguage>
|
||||||
|
<UserLocale>en-US</UserLocale>
|
||||||
|
</component>
|
||||||
|
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="*arch*" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||||
|
<OOBE>
|
||||||
|
<HideEULAPage>true</HideEULAPage>
|
||||||
|
<HideLocalAccountScreen>true</HideLocalAccountScreen>
|
||||||
|
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
|
||||||
|
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
|
||||||
|
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
|
||||||
|
<ProtectYourPC>3</ProtectYourPC>
|
||||||
|
</OOBE>
|
||||||
|
<UserAccounts>
|
||||||
|
<LocalAccounts>
|
||||||
|
<LocalAccount wcm:action="add">
|
||||||
|
<Password>
|
||||||
|
<Value>Pa55word</Value>
|
||||||
|
<PlainText>true</PlainText>
|
||||||
|
</Password>
|
||||||
|
<Name>SupportUser</Name>
|
||||||
|
<Group>Administrators</Group>
|
||||||
|
<DisplayName>SupportUser</DisplayName>
|
||||||
|
</LocalAccount>
|
||||||
|
</LocalAccounts>
|
||||||
|
</UserAccounts>
|
||||||
|
<AutoLogon>
|
||||||
|
<Password>
|
||||||
|
<Value>Pa55word</Value>
|
||||||
|
<PlainText>true</PlainText>
|
||||||
|
</Password>
|
||||||
|
<Enabled>true</Enabled>
|
||||||
|
<Username>SupportUser</Username>
|
||||||
|
<LogonCount>7</LogonCount>
|
||||||
|
</AutoLogon>
|
||||||
|
<FirstLogonCommands>
|
||||||
|
<SynchronousCommand wcm:action="add">
|
||||||
|
<Order>1</Order>
|
||||||
|
<CommandLine>cmd.exe /c powercfg /change monitor-timeout-ac 0 & powercfg /change monitor-timeout-dc 0 & powercfg /change standby-timeout-ac 0 & powercfg /change standby-timeout-dc 0</CommandLine>
|
||||||
|
<Description>Disable display and sleep timeout during setup</Description>
|
||||||
|
</SynchronousCommand>
|
||||||
|
<SynchronousCommand wcm:action="add">
|
||||||
|
<Order>2</Order>
|
||||||
|
<CommandLine>powershell.exe -ExecutionPolicy Bypass -Command "Get-NetAdapter -Physical | Where-Object { $_.InterfaceDescription -match 'Wi-Fi|Wireless' } | Set-NetIPInterface -InterfaceMetric 10; Get-NetAdapter -Physical | Where-Object { $_.InterfaceDescription -notmatch 'Wi-Fi|Wireless' } | Set-NetIPInterface -InterfaceMetric 100"</CommandLine>
|
||||||
|
<Description>Prioritize WiFi over ethernet</Description>
|
||||||
|
</SynchronousCommand>
|
||||||
|
<SynchronousCommand wcm:action="add">
|
||||||
|
<Order>3</Order>
|
||||||
|
<CommandLine>powershell.exe -ExecutionPolicy Bypass -Command "Get-NetConnectionProfile | Set-NetConnectionProfile -NetworkCategory Private"</CommandLine>
|
||||||
|
<Description>Set network profile to Private</Description>
|
||||||
|
</SynchronousCommand>
|
||||||
|
<SynchronousCommand wcm:action="add">
|
||||||
|
<Order>4</Order>
|
||||||
|
<CommandLine>powershell.exe -ExecutionPolicy Bypass -Command "Write-Host 'Waiting for internet connectivity...'; while (-not (Test-Connection -ComputerName login.microsoftonline.us -Count 1 -Quiet -ErrorAction SilentlyContinue)) { Start-Sleep -Seconds 5 }; Write-Host 'Internet connected.'"</CommandLine>
|
||||||
|
<Description>Wait for internet connectivity</Description>
|
||||||
|
</SynchronousCommand>
|
||||||
|
<SynchronousCommand wcm:action="add">
|
||||||
|
<Order>5</Order>
|
||||||
|
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File "C:\run-enrollment.ps1"</CommandLine>
|
||||||
|
<Description>Run GCCH Enrollment</Description>
|
||||||
|
</SynchronousCommand>
|
||||||
|
<SynchronousCommand wcm:action="add">
|
||||||
|
<Order>6</Order>
|
||||||
|
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File "C:\Enrollment\Run-ShopfloorSetup.ps1"</CommandLine>
|
||||||
|
<Description>Run shopfloor PC type setup</Description>
|
||||||
|
</SynchronousCommand>
|
||||||
|
</FirstLogonCommands>
|
||||||
|
<TimeZone>Eastern Standard Time</TimeZone>
|
||||||
|
</component>
|
||||||
|
</settings>
|
||||||
|
</unattend>
|
||||||
148
playbook/check-bios.cmd
Normal file
148
playbook/check-bios.cmd
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
@echo off
|
||||||
|
REM check-bios.cmd - Check and apply Dell BIOS update from WinPE x64
|
||||||
|
REM Sets BIOS_STATUS for startnet.cmd menu display
|
||||||
|
|
||||||
|
set "BIOSDIR=%~dp0"
|
||||||
|
set "FLASH=%BIOSDIR%Flash64W.exe"
|
||||||
|
set "MANIFEST=%BIOSDIR%models.txt"
|
||||||
|
|
||||||
|
if exist "%FLASH%" goto :flash_ok
|
||||||
|
echo Flash64W.exe not found, skipping BIOS check.
|
||||||
|
set "BIOS_STATUS=Skipped (Flash64W.exe missing)"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:flash_ok
|
||||||
|
if exist "%MANIFEST%" goto :manifest_ok
|
||||||
|
echo models.txt not found, skipping BIOS check.
|
||||||
|
set "BIOS_STATUS=Skipped (models.txt missing)"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:manifest_ok
|
||||||
|
REM --- Get system model from WMI ---
|
||||||
|
set SYSMODEL=
|
||||||
|
for /f "skip=1 tokens=*" %%M in ('wmic csproduct get name 2^>NUL') do (
|
||||||
|
if not defined SYSMODEL set "SYSMODEL=%%M"
|
||||||
|
)
|
||||||
|
for /f "tokens=*" %%a in ("%SYSMODEL%") do set "SYSMODEL=%%a"
|
||||||
|
|
||||||
|
if "%SYSMODEL%"=="" goto :no_model
|
||||||
|
goto :got_model
|
||||||
|
|
||||||
|
:no_model
|
||||||
|
echo Could not detect system model, skipping BIOS check.
|
||||||
|
set "BIOS_STATUS=Skipped (model not detected)"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:got_model
|
||||||
|
echo Model: %SYSMODEL%
|
||||||
|
|
||||||
|
REM --- Get current BIOS version ---
|
||||||
|
set BIOSVER=
|
||||||
|
for /f "skip=1 tokens=*" %%V in ('wmic bios get smbiosbiosversion 2^>NUL') do (
|
||||||
|
if not defined BIOSVER set "BIOSVER=%%V"
|
||||||
|
)
|
||||||
|
for /f "tokens=*" %%a in ("%BIOSVER%") do set "BIOSVER=%%a"
|
||||||
|
echo Current BIOS: %BIOSVER%
|
||||||
|
|
||||||
|
REM --- Read manifest and find matching BIOS file ---
|
||||||
|
set BIOSFILE=
|
||||||
|
set TARGETVER=
|
||||||
|
for /f "usebackq eol=# tokens=1,2,3 delims=|" %%A in ("%MANIFEST%") do (
|
||||||
|
echo "%SYSMODEL%" | find /I "%%A" >NUL
|
||||||
|
if not errorlevel 1 (
|
||||||
|
set "BIOSFILE=%%B"
|
||||||
|
set "TARGETVER=%%C"
|
||||||
|
goto :found_bios
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo No BIOS update available for this model.
|
||||||
|
set "BIOS_STATUS=%SYSMODEL% - no update in catalog"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:found_bios
|
||||||
|
if not exist "%BIOSDIR%%BIOSFILE%" goto :bios_file_missing
|
||||||
|
goto :bios_file_ok
|
||||||
|
|
||||||
|
:bios_file_missing
|
||||||
|
echo WARNING: %BIOSFILE% not found in BIOS folder.
|
||||||
|
set "BIOS_STATUS=%SYSMODEL% - %BIOSFILE% missing"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:bios_file_ok
|
||||||
|
REM --- Skip if already at target version ---
|
||||||
|
echo.%BIOSVER%| find /I "%TARGETVER%" >NUL
|
||||||
|
if not errorlevel 1 goto :already_current
|
||||||
|
|
||||||
|
REM --- Compare versions to prevent downgrade ---
|
||||||
|
call :compare_versions "%BIOSVER%" "%TARGETVER%"
|
||||||
|
if "%VERCMP%"=="newer" goto :already_newer
|
||||||
|
goto :do_flash
|
||||||
|
|
||||||
|
:already_current
|
||||||
|
echo BIOS is already up to date - %TARGETVER%
|
||||||
|
set "BIOS_STATUS=%SYSMODEL% v%BIOSVER% (up to date)"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:already_newer
|
||||||
|
echo Current BIOS %BIOSVER% is newer than target %TARGETVER% - skipping.
|
||||||
|
set "BIOS_STATUS=%SYSMODEL% v%BIOSVER% (up to date)"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:do_flash
|
||||||
|
echo Update: %BIOSVER% -^> %TARGETVER%
|
||||||
|
echo Applying BIOS update (this may take a few minutes, do not power off)...
|
||||||
|
|
||||||
|
pushd "%BIOSDIR%"
|
||||||
|
Flash64W.exe /b="%BIOSFILE%" /s /f /l=X:\bios-update.log
|
||||||
|
set FLASHRC=%ERRORLEVEL%
|
||||||
|
popd
|
||||||
|
echo Flash complete (exit code %FLASHRC%).
|
||||||
|
|
||||||
|
if "%FLASHRC%"=="3" goto :already_current
|
||||||
|
if "%FLASHRC%"=="0" goto :flash_done
|
||||||
|
if "%FLASHRC%"=="2" goto :staged
|
||||||
|
if "%FLASHRC%"=="6" goto :staged
|
||||||
|
|
||||||
|
echo WARNING: Flash64W.exe returned unexpected code %FLASHRC%.
|
||||||
|
set "BIOS_STATUS=%SYSMODEL% flash error (code %FLASHRC%)"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:flash_done
|
||||||
|
echo BIOS update complete.
|
||||||
|
set "BIOS_STATUS=%SYSMODEL% updated %BIOSVER% -^> %TARGETVER%"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:staged
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo BIOS update staged successfully.
|
||||||
|
echo It will flash during POST after the
|
||||||
|
echo post-imaging reboot.
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
set "BIOS_STATUS=%SYSMODEL% STAGED %BIOSVER% -^> %TARGETVER% (flashes on reboot)"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:compare_versions
|
||||||
|
set "VERCMP=equal"
|
||||||
|
set "_CV=%~1"
|
||||||
|
set "_TV=%~2"
|
||||||
|
for /f "tokens=1,2,3 delims=." %%a in ("%_CV%") do (
|
||||||
|
set /a "C1=%%a" 2>NUL
|
||||||
|
set /a "C2=%%b" 2>NUL
|
||||||
|
set /a "C3=%%c" 2>NUL
|
||||||
|
)
|
||||||
|
for /f "tokens=1,2,3 delims=." %%a in ("%_TV%") do (
|
||||||
|
set /a "T1=%%a" 2>NUL
|
||||||
|
set /a "T2=%%b" 2>NUL
|
||||||
|
set /a "T3=%%c" 2>NUL
|
||||||
|
)
|
||||||
|
if %C1% GTR %T1% ( set "VERCMP=newer" & goto :eof )
|
||||||
|
if %C1% LSS %T1% ( set "VERCMP=older" & goto :eof )
|
||||||
|
if %C2% GTR %T2% ( set "VERCMP=newer" & goto :eof )
|
||||||
|
if %C2% LSS %T2% ( set "VERCMP=older" & goto :eof )
|
||||||
|
if %C3% GTR %T3% ( set "VERCMP=newer" & goto :eof )
|
||||||
|
if %C3% LSS %T3% ( set "VERCMP=older" & goto :eof )
|
||||||
|
set "VERCMP=equal"
|
||||||
|
goto :eof
|
||||||
60
playbook/shopfloor-setup/Run-ShopfloorSetup.ps1
Normal file
60
playbook/shopfloor-setup/Run-ShopfloorSetup.ps1
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# Run-ShopfloorSetup.ps1 — Dispatcher for shopfloor PC type setup
|
||||||
|
# Runs Shopfloor baseline scripts first, then type-specific scripts on top.
|
||||||
|
|
||||||
|
# Cancel any pending reboot so it doesn't interrupt setup
|
||||||
|
shutdown -a 2>$null
|
||||||
|
|
||||||
|
$enrollDir = "C:\Enrollment"
|
||||||
|
$typeFile = Join-Path $enrollDir "pc-type.txt"
|
||||||
|
$setupDir = Join-Path $enrollDir "shopfloor-setup"
|
||||||
|
|
||||||
|
if (-not (Test-Path $typeFile)) {
|
||||||
|
Write-Host "No pc-type.txt found - skipping shopfloor setup."
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
$pcType = (Get-Content $typeFile -First 1).Trim()
|
||||||
|
if (-not $pcType) {
|
||||||
|
Write-Host "pc-type.txt is empty - skipping shopfloor setup."
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Shopfloor PC Type: $pcType"
|
||||||
|
|
||||||
|
# --- Run Shopfloor baseline scripts first ---
|
||||||
|
$baselineDir = Join-Path $setupDir "Shopfloor"
|
||||||
|
if (Test-Path $baselineDir) {
|
||||||
|
$scripts = Get-ChildItem -Path $baselineDir -Filter "*.ps1" -File | Sort-Object Name
|
||||||
|
foreach ($script in $scripts) {
|
||||||
|
shutdown /a 2>$null
|
||||||
|
Write-Host "Running baseline: $($script.Name)"
|
||||||
|
try {
|
||||||
|
& $script.FullName
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Baseline script $($script.Name) failed: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Run type-specific scripts (if not just baseline Shopfloor) ---
|
||||||
|
if ($pcType -ne "Shopfloor") {
|
||||||
|
$typeDir = Join-Path $setupDir $pcType
|
||||||
|
if (Test-Path $typeDir) {
|
||||||
|
$scripts = Get-ChildItem -Path $typeDir -Filter "*.ps1" -File | Sort-Object Name
|
||||||
|
foreach ($script in $scripts) {
|
||||||
|
shutdown /a 2>$null
|
||||||
|
Write-Host "Running $pcType setup: $($script.Name)"
|
||||||
|
try {
|
||||||
|
& $script.FullName
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Script $($script.Name) failed: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "No type-specific scripts found for $pcType."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Shopfloor setup complete for $pcType."
|
||||||
|
Write-Host "Rebooting in 10 seconds..."
|
||||||
|
shutdown /r /t 10
|
||||||
@@ -3,37 +3,141 @@ echo Please wait while 'WinPE' is being processed. This may take a few seconds.
|
|||||||
wpeinit
|
wpeinit
|
||||||
powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
||||||
|
|
||||||
|
REM --- Wait for network (DHCP may take a moment after wpeinit) ---
|
||||||
|
echo Waiting for network...
|
||||||
|
:wait_net
|
||||||
|
ping -n 2 10.9.100.1 >NUL 2>&1
|
||||||
|
if errorlevel 1 goto wait_net
|
||||||
|
echo Network ready.
|
||||||
|
|
||||||
|
REM --- BIOS update check (runs before imaging menu) ---
|
||||||
|
set BIOS_STATUS=No BIOS check (share unavailable)
|
||||||
|
net use B: \\10.9.100.1\winpeapps\_shared /user:pxe-upload pxe /persistent:no 2>NUL
|
||||||
|
if exist B:\BIOS\check-bios.cmd (
|
||||||
|
echo.
|
||||||
|
echo Checking for BIOS updates...
|
||||||
|
call B:\BIOS\check-bios.cmd
|
||||||
|
REM If BIOS was flashed, check-bios.cmd reboots and we never reach here.
|
||||||
|
echo.
|
||||||
|
)
|
||||||
|
net use B: /delete 2>NUL
|
||||||
|
|
||||||
:menu
|
:menu
|
||||||
cls
|
cls
|
||||||
echo.
|
echo.
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo WinPE Setup Menu
|
echo WinPE Setup Menu
|
||||||
echo ========================================
|
echo ========================================
|
||||||
|
echo Firmware: %BIOS_STATUS%
|
||||||
echo.
|
echo.
|
||||||
echo Please select an option:
|
echo Please select an option:
|
||||||
echo.
|
echo.
|
||||||
echo 1. GEA Standard
|
echo 1. GEA Standard
|
||||||
echo 2. GEA Engineer
|
echo 2. GEA Engineer
|
||||||
echo 3. GEA Shopfloor
|
echo 3. GEA Shopfloor
|
||||||
echo 4. GEA Shopfloor MCE
|
echo 4. GE Standard
|
||||||
echo 5. GE Standard
|
echo 5. GE Engineer
|
||||||
echo 6. GE Engineer
|
echo 6. GE Shopfloor Lockdown
|
||||||
echo 7. GE Shopfloor Lockdown
|
echo 7. GE Shopfloor MCE
|
||||||
echo 8. GE Shopfloor MCE
|
|
||||||
echo.
|
echo.
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo.
|
echo.
|
||||||
set /p choice=Enter your choice (1-8):
|
set /p choice=Enter your choice (1-7):
|
||||||
|
|
||||||
|
REM --- Only shopfloor images (3,6,7) need GCCH enrollment ---
|
||||||
|
set PPKG=
|
||||||
|
if "%choice%"=="3" goto enroll_menu
|
||||||
|
if "%choice%"=="6" goto enroll_menu
|
||||||
|
if "%choice%"=="7" goto enroll_menu
|
||||||
|
goto enroll_staged
|
||||||
|
|
||||||
|
:enroll_menu
|
||||||
|
cls
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo GCCH Enrollment Profile
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo 1. No Office
|
||||||
|
echo 2. Standard Office (x86)
|
||||||
|
echo 3. Standard Office (x64)
|
||||||
|
echo 4. Pro Plus Office (x86) with Access
|
||||||
|
echo 5. Pro Plus Office (x64) with Access
|
||||||
|
echo 6. Skip enrollment
|
||||||
|
echo.
|
||||||
|
set /p enroll=Enter your choice (1-6):
|
||||||
|
if "%enroll%"=="1" set PPKG=GCCH_Prod_SFLD_NoOffice_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="2" set PPKG=GCCH_Prod_SFLD_StdOffice-x86_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="3" set PPKG=GCCH_Prod_SFLD_StdOffice-x64_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="4" set PPKG=GCCH_Prod_SFLD_ProPlusOffice-x86_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="5" set PPKG=GCCH_Prod_SFLD_ProPlusOffice-x64_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="6" set PPKG=
|
||||||
|
if "%enroll%"=="" goto enroll_menu
|
||||||
|
|
||||||
|
:pctype_menu
|
||||||
|
cls
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo Shopfloor PC Type
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo 1. CMM
|
||||||
|
echo 2. Wax and Trace
|
||||||
|
echo 3. Keyence
|
||||||
|
echo 4. Genspect
|
||||||
|
echo 5. Display
|
||||||
|
echo 6. Shopfloor (General)
|
||||||
|
echo.
|
||||||
|
set PCTYPE=
|
||||||
|
set /p pctype_choice=Enter your choice (1-6):
|
||||||
|
if "%pctype_choice%"=="1" set PCTYPE=CMM
|
||||||
|
if "%pctype_choice%"=="2" set PCTYPE=WaxAndTrace
|
||||||
|
if "%pctype_choice%"=="3" set PCTYPE=Keyence
|
||||||
|
if "%pctype_choice%"=="4" set PCTYPE=Genspect
|
||||||
|
if "%pctype_choice%"=="5" set PCTYPE=Display
|
||||||
|
if "%pctype_choice%"=="6" set PCTYPE=Shopfloor
|
||||||
|
if "%PCTYPE%"=="" goto pctype_menu
|
||||||
|
|
||||||
|
REM --- Display sub-type selection ---
|
||||||
|
set DISPLAYTYPE=
|
||||||
|
if not "%PCTYPE%"=="Display" goto skip_display_menu
|
||||||
|
:display_menu
|
||||||
|
cls
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo Display Type
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo 1. Lobby Display
|
||||||
|
echo 2. Dashboard
|
||||||
|
echo.
|
||||||
|
set /p display_choice=Enter your choice (1-2):
|
||||||
|
if "%display_choice%"=="1" set DISPLAYTYPE=Lobby
|
||||||
|
if "%display_choice%"=="2" set DISPLAYTYPE=Dashboard
|
||||||
|
if "%DISPLAYTYPE%"=="" goto display_menu
|
||||||
|
:skip_display_menu
|
||||||
|
|
||||||
|
REM --- Map enrollment share early (kept open for copy after imaging) ---
|
||||||
|
set NEED_ENROLL=0
|
||||||
|
if not "%PPKG%"=="" set NEED_ENROLL=1
|
||||||
|
if not "%PCTYPE%"=="" set NEED_ENROLL=1
|
||||||
|
if "%NEED_ENROLL%"=="0" goto enroll_staged
|
||||||
|
net use Y: \\10.9.100.1\enrollment /user:pxe-upload pxe /persistent:no
|
||||||
|
if "%PPKG%"=="" goto enroll_staged
|
||||||
|
if not exist "Y:\%PPKG%" (
|
||||||
|
echo WARNING: %PPKG% not found on server. Enrollment will be skipped.
|
||||||
|
set PPKG=
|
||||||
|
)
|
||||||
|
:enroll_staged
|
||||||
|
|
||||||
echo. > X:\Boot.tag
|
echo. > X:\Boot.tag
|
||||||
if "%choice%"=="1" goto gea-standard
|
if "%choice%"=="1" goto gea-standard
|
||||||
if "%choice%"=="2" goto gea-engineer
|
if "%choice%"=="2" goto gea-engineer
|
||||||
if "%choice%"=="3" goto gea-shopfloor
|
if "%choice%"=="3" goto gea-shopfloor
|
||||||
if "%choice%"=="4" goto gea-shopfloor-mce
|
if "%choice%"=="4" goto ge-standard
|
||||||
if "%choice%"=="5" goto ge-standard
|
if "%choice%"=="5" goto ge-engineer
|
||||||
if "%choice%"=="6" goto ge-engineer
|
if "%choice%"=="6" goto ge-shopfloor-lockdown
|
||||||
if "%choice%"=="7" goto ge-shopfloor-lockdown
|
if "%choice%"=="7" goto ge-shopfloor-mce
|
||||||
if "%choice%"=="8" goto ge-shopfloor-mce
|
|
||||||
echo Invalid choice. Please try again.
|
echo Invalid choice. Please try again.
|
||||||
pause
|
pause
|
||||||
goto menu
|
goto menu
|
||||||
@@ -62,14 +166,6 @@ for /l %%i in (1,1,2000000) do rem
|
|||||||
net use Z: \\10.9.100.1\winpeapps\gea-shopfloor /user:pxe-upload pxe /persistent:no
|
net use Z: \\10.9.100.1\winpeapps\gea-shopfloor /user:pxe-upload pxe /persistent:no
|
||||||
goto end
|
goto end
|
||||||
|
|
||||||
:gea-shopfloor-mce
|
|
||||||
echo.
|
|
||||||
echo Starting GEA Shopfloor MCE setup...
|
|
||||||
start "FlatApp" %SYSTEMDRIVE%\GESetup\FlatSetupLoader.exe
|
|
||||||
for /l %%i in (1,1,2000000) do rem
|
|
||||||
net use Z: \\10.9.100.1\winpeapps\gea-shopfloor-mce /user:pxe-upload pxe /persistent:no
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:ge-standard
|
:ge-standard
|
||||||
echo.
|
echo.
|
||||||
echo Starting GE Standard setup...
|
echo Starting GE Standard setup...
|
||||||
@@ -107,12 +203,70 @@ echo.
|
|||||||
echo Waiting for PESetup.exe to start...
|
echo Waiting for PESetup.exe to start...
|
||||||
:wait_start
|
:wait_start
|
||||||
ping -n 3 127.0.0.1 >NUL
|
ping -n 3 127.0.0.1 >NUL
|
||||||
tasklist /FI "IMAGENAME eq PESetup.exe" 2>NUL | find /I "PESetup.exe" >NUL
|
wmic process where "name='PESetup.exe'" get name 2>NUL | find /I "PESetup" >NUL
|
||||||
if errorlevel 1 goto wait_start
|
if errorlevel 1 goto wait_start
|
||||||
echo PESetup.exe is running. Waiting for imaging to complete...
|
echo PESetup.exe is running. Waiting for imaging to complete...
|
||||||
|
|
||||||
|
REM --- Copy enrollment package and shopfloor setup as soon as Windows partition appears ---
|
||||||
|
if "%PPKG%"=="" if "%PCTYPE%"=="" goto wait_finish
|
||||||
|
echo Waiting for Windows partition at W: ...
|
||||||
|
:wait_enroll
|
||||||
|
ping -n 11 127.0.0.1 >NUL
|
||||||
|
if not exist W:\Windows\System32\config\system goto wait_enroll
|
||||||
|
echo Found Windows at W:
|
||||||
|
mkdir W:\Enrollment 2>NUL
|
||||||
|
|
||||||
|
REM --- Copy PPKG if selected ---
|
||||||
|
if "%PPKG%"=="" goto copy_pctype
|
||||||
|
copy /Y "Y:\%PPKG%" "W:\Enrollment\%PPKG%"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo WARNING: Failed to copy enrollment package.
|
||||||
|
goto copy_pctype
|
||||||
|
)
|
||||||
|
copy /Y "Y:\run-enrollment.ps1" "W:\run-enrollment.ps1"
|
||||||
|
|
||||||
|
REM --- Create enroll.cmd at drive root as manual fallback ---
|
||||||
|
> W:\enroll.cmd (
|
||||||
|
echo @echo off
|
||||||
|
echo echo Waiting for network...
|
||||||
|
echo :waitnet
|
||||||
|
echo ping -n 2 8.8.8.8 ^>NUL 2^>^&1
|
||||||
|
echo if errorlevel 1 goto waitnet
|
||||||
|
echo echo Network connected. Running enrollment...
|
||||||
|
echo powershell.exe -ExecutionPolicy Bypass -File "C:\run-enrollment.ps1"
|
||||||
|
)
|
||||||
|
echo Manual fallback created at W:\enroll.cmd
|
||||||
|
|
||||||
|
:copy_pctype
|
||||||
|
REM --- Copy shopfloor PC type setup scripts ---
|
||||||
|
if "%PCTYPE%"=="" goto cleanup_enroll
|
||||||
|
echo %PCTYPE%> W:\Enrollment\pc-type.txt
|
||||||
|
if not "%DISPLAYTYPE%"=="" echo %DISPLAYTYPE%> W:\Enrollment\display-type.txt
|
||||||
|
copy /Y "Y:\shopfloor-setup\Run-ShopfloorSetup.ps1" "W:\Enrollment\Run-ShopfloorSetup.ps1"
|
||||||
|
REM --- Always copy Shopfloor baseline scripts ---
|
||||||
|
mkdir W:\Enrollment\shopfloor-setup 2>NUL
|
||||||
|
if exist "Y:\shopfloor-setup\Shopfloor" (
|
||||||
|
mkdir W:\Enrollment\shopfloor-setup\Shopfloor 2>NUL
|
||||||
|
xcopy /E /Y /I "Y:\shopfloor-setup\Shopfloor" "W:\Enrollment\shopfloor-setup\Shopfloor\"
|
||||||
|
echo Copied Shopfloor baseline setup files.
|
||||||
|
)
|
||||||
|
REM --- Copy type-specific scripts on top of baseline ---
|
||||||
|
if "%PCTYPE%"=="Shopfloor" goto pctype_done
|
||||||
|
if exist "Y:\shopfloor-setup\%PCTYPE%" (
|
||||||
|
mkdir "W:\Enrollment\shopfloor-setup\%PCTYPE%" 2>NUL
|
||||||
|
xcopy /E /Y /I "Y:\shopfloor-setup\%PCTYPE%" "W:\Enrollment\shopfloor-setup\%PCTYPE%\"
|
||||||
|
echo Copied %PCTYPE% setup files.
|
||||||
|
) else (
|
||||||
|
echo WARNING: No setup files found for PC type %PCTYPE%.
|
||||||
|
)
|
||||||
|
:pctype_done
|
||||||
|
|
||||||
|
:cleanup_enroll
|
||||||
|
net use Y: /delete 2>NUL
|
||||||
|
|
||||||
:wait_finish
|
:wait_finish
|
||||||
ping -n 11 127.0.0.1 >NUL
|
ping -n 11 127.0.0.1 >NUL
|
||||||
tasklist /FI "IMAGENAME eq PESetup.exe" 2>NUL | find /I "PESetup.exe" >NUL
|
wmic process where "name='PESetup.exe'" get name 2>NUL | find /I "PESetup" >NUL
|
||||||
if not errorlevel 1 goto wait_finish
|
if not errorlevel 1 goto wait_finish
|
||||||
echo.
|
echo.
|
||||||
echo Imaging complete. Rebooting in 15 seconds...
|
echo Imaging complete. Rebooting in 15 seconds...
|
||||||
|
|||||||
@@ -3,37 +3,141 @@ echo Please wait while 'WinPE' is being processed. This may take a few seconds.
|
|||||||
wpeinit
|
wpeinit
|
||||||
powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
||||||
|
|
||||||
|
REM --- Wait for network (DHCP may take a moment after wpeinit) ---
|
||||||
|
echo Waiting for network...
|
||||||
|
:wait_net
|
||||||
|
ping -n 2 10.9.100.1 >NUL 2>&1
|
||||||
|
if errorlevel 1 goto wait_net
|
||||||
|
echo Network ready.
|
||||||
|
|
||||||
|
REM --- BIOS update check (runs before imaging menu) ---
|
||||||
|
set BIOS_STATUS=No BIOS check (share unavailable)
|
||||||
|
net use B: \\10.9.100.1\winpeapps\_shared /user:pxe-upload pxe /persistent:no 2>NUL
|
||||||
|
if exist B:\BIOS\check-bios.cmd (
|
||||||
|
echo.
|
||||||
|
echo Checking for BIOS updates...
|
||||||
|
call B:\BIOS\check-bios.cmd
|
||||||
|
REM If BIOS was flashed, check-bios.cmd reboots and we never reach here.
|
||||||
|
echo.
|
||||||
|
)
|
||||||
|
net use B: /delete 2>NUL
|
||||||
|
|
||||||
:menu
|
:menu
|
||||||
cls
|
cls
|
||||||
echo.
|
echo.
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo WinPE Setup Menu
|
echo WinPE Setup Menu
|
||||||
echo ========================================
|
echo ========================================
|
||||||
|
echo Firmware: %BIOS_STATUS%
|
||||||
echo.
|
echo.
|
||||||
echo Please select an option:
|
echo Please select an option:
|
||||||
echo.
|
echo.
|
||||||
echo 1. GEA Standard
|
echo 1. GEA Standard
|
||||||
echo 2. GEA Engineer
|
echo 2. GEA Engineer
|
||||||
echo 3. GEA Shopfloor
|
echo 3. GEA Shopfloor
|
||||||
echo 4. GEA Shopfloor MCE
|
echo 4. GE Standard
|
||||||
echo 5. GE Standard
|
echo 5. GE Engineer
|
||||||
echo 6. GE Engineer
|
echo 6. GE Shopfloor Lockdown
|
||||||
echo 7. GE Shopfloor Lockdown
|
echo 7. GE Shopfloor MCE
|
||||||
echo 8. GE Shopfloor MCE
|
|
||||||
echo.
|
echo.
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo.
|
echo.
|
||||||
set /p choice=Enter your choice (1-8):
|
set /p choice=Enter your choice (1-7):
|
||||||
|
|
||||||
|
REM --- Only shopfloor images (3,6,7) need GCCH enrollment ---
|
||||||
|
set PPKG=
|
||||||
|
if "%choice%"=="3" goto enroll_menu
|
||||||
|
if "%choice%"=="6" goto enroll_menu
|
||||||
|
if "%choice%"=="7" goto enroll_menu
|
||||||
|
goto enroll_staged
|
||||||
|
|
||||||
|
:enroll_menu
|
||||||
|
cls
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo GCCH Enrollment Profile
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo 1. No Office
|
||||||
|
echo 2. Standard Office (x86)
|
||||||
|
echo 3. Standard Office (x64)
|
||||||
|
echo 4. Pro Plus Office (x86) with Access
|
||||||
|
echo 5. Pro Plus Office (x64) with Access
|
||||||
|
echo 6. Skip enrollment
|
||||||
|
echo.
|
||||||
|
set /p enroll=Enter your choice (1-6):
|
||||||
|
if "%enroll%"=="1" set PPKG=GCCH_Prod_SFLD_NoOffice_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="2" set PPKG=GCCH_Prod_SFLD_StdOffice-x86_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="3" set PPKG=GCCH_Prod_SFLD_StdOffice-x64_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="4" set PPKG=GCCH_Prod_SFLD_ProPlusOffice-x86_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="5" set PPKG=GCCH_Prod_SFLD_ProPlusOffice-x64_US_Exp_20260430_v4.8.ppkg
|
||||||
|
if "%enroll%"=="6" set PPKG=
|
||||||
|
if "%enroll%"=="" goto enroll_menu
|
||||||
|
|
||||||
|
:pctype_menu
|
||||||
|
cls
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo Shopfloor PC Type
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo 1. CMM
|
||||||
|
echo 2. Wax and Trace
|
||||||
|
echo 3. Keyence
|
||||||
|
echo 4. Genspect
|
||||||
|
echo 5. Display
|
||||||
|
echo 6. Shopfloor (General)
|
||||||
|
echo.
|
||||||
|
set PCTYPE=
|
||||||
|
set /p pctype_choice=Enter your choice (1-6):
|
||||||
|
if "%pctype_choice%"=="1" set PCTYPE=CMM
|
||||||
|
if "%pctype_choice%"=="2" set PCTYPE=WaxAndTrace
|
||||||
|
if "%pctype_choice%"=="3" set PCTYPE=Keyence
|
||||||
|
if "%pctype_choice%"=="4" set PCTYPE=Genspect
|
||||||
|
if "%pctype_choice%"=="5" set PCTYPE=Display
|
||||||
|
if "%pctype_choice%"=="6" set PCTYPE=Shopfloor
|
||||||
|
if "%PCTYPE%"=="" goto pctype_menu
|
||||||
|
|
||||||
|
REM --- Display sub-type selection ---
|
||||||
|
set DISPLAYTYPE=
|
||||||
|
if not "%PCTYPE%"=="Display" goto skip_display_menu
|
||||||
|
:display_menu
|
||||||
|
cls
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo Display Type
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo 1. Lobby Display
|
||||||
|
echo 2. Dashboard
|
||||||
|
echo.
|
||||||
|
set /p display_choice=Enter your choice (1-2):
|
||||||
|
if "%display_choice%"=="1" set DISPLAYTYPE=Lobby
|
||||||
|
if "%display_choice%"=="2" set DISPLAYTYPE=Dashboard
|
||||||
|
if "%DISPLAYTYPE%"=="" goto display_menu
|
||||||
|
:skip_display_menu
|
||||||
|
|
||||||
|
REM --- Map enrollment share early (kept open for copy after imaging) ---
|
||||||
|
set NEED_ENROLL=0
|
||||||
|
if not "%PPKG%"=="" set NEED_ENROLL=1
|
||||||
|
if not "%PCTYPE%"=="" set NEED_ENROLL=1
|
||||||
|
if "%NEED_ENROLL%"=="0" goto enroll_staged
|
||||||
|
net use Y: \\10.9.100.1\enrollment /user:pxe-upload pxe /persistent:no
|
||||||
|
if "%PPKG%"=="" goto enroll_staged
|
||||||
|
if not exist "Y:\%PPKG%" (
|
||||||
|
echo WARNING: %PPKG% not found on server. Enrollment will be skipped.
|
||||||
|
set PPKG=
|
||||||
|
)
|
||||||
|
:enroll_staged
|
||||||
|
|
||||||
echo. > X:\Boot.tag
|
echo. > X:\Boot.tag
|
||||||
if "%choice%"=="1" goto gea-standard
|
if "%choice%"=="1" goto gea-standard
|
||||||
if "%choice%"=="2" goto gea-engineer
|
if "%choice%"=="2" goto gea-engineer
|
||||||
if "%choice%"=="3" goto gea-shopfloor
|
if "%choice%"=="3" goto gea-shopfloor
|
||||||
if "%choice%"=="4" goto gea-shopfloor-mce
|
if "%choice%"=="4" goto ge-standard
|
||||||
if "%choice%"=="5" goto ge-standard
|
if "%choice%"=="5" goto ge-engineer
|
||||||
if "%choice%"=="6" goto ge-engineer
|
if "%choice%"=="6" goto ge-shopfloor-lockdown
|
||||||
if "%choice%"=="7" goto ge-shopfloor-lockdown
|
if "%choice%"=="7" goto ge-shopfloor-mce
|
||||||
if "%choice%"=="8" goto ge-shopfloor-mce
|
|
||||||
echo Invalid choice. Please try again.
|
echo Invalid choice. Please try again.
|
||||||
pause
|
pause
|
||||||
goto menu
|
goto menu
|
||||||
@@ -62,14 +166,6 @@ for /l %%i in (1,1,2000000) do rem
|
|||||||
net use Z: \\10.9.100.1\winpeapps\gea-shopfloor /user:pxe-upload pxe /persistent:no
|
net use Z: \\10.9.100.1\winpeapps\gea-shopfloor /user:pxe-upload pxe /persistent:no
|
||||||
goto end
|
goto end
|
||||||
|
|
||||||
:gea-shopfloor-mce
|
|
||||||
echo.
|
|
||||||
echo Starting GEA Shopfloor MCE setup...
|
|
||||||
start "FlatApp" %SYSTEMDRIVE%\GESetup\FlatSetupLoader.exe
|
|
||||||
for /l %%i in (1,1,2000000) do rem
|
|
||||||
net use Z: \\10.9.100.1\winpeapps\gea-shopfloor-mce /user:pxe-upload pxe /persistent:no
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:ge-standard
|
:ge-standard
|
||||||
echo.
|
echo.
|
||||||
echo Starting GE Standard setup...
|
echo Starting GE Standard setup...
|
||||||
@@ -107,12 +203,70 @@ echo.
|
|||||||
echo Waiting for PESetup.exe to start...
|
echo Waiting for PESetup.exe to start...
|
||||||
:wait_start
|
:wait_start
|
||||||
ping -n 3 127.0.0.1 >NUL
|
ping -n 3 127.0.0.1 >NUL
|
||||||
tasklist /FI "IMAGENAME eq PESetup.exe" 2>NUL | find /I "PESetup.exe" >NUL
|
wmic process where "name='PESetup.exe'" get name 2>NUL | find /I "PESetup" >NUL
|
||||||
if errorlevel 1 goto wait_start
|
if errorlevel 1 goto wait_start
|
||||||
echo PESetup.exe is running. Waiting for imaging to complete...
|
echo PESetup.exe is running. Waiting for imaging to complete...
|
||||||
|
|
||||||
|
REM --- Copy enrollment package and shopfloor setup as soon as Windows partition appears ---
|
||||||
|
if "%PPKG%"=="" if "%PCTYPE%"=="" goto wait_finish
|
||||||
|
echo Waiting for Windows partition at W: ...
|
||||||
|
:wait_enroll
|
||||||
|
ping -n 11 127.0.0.1 >NUL
|
||||||
|
if not exist W:\Windows\System32\config\system goto wait_enroll
|
||||||
|
echo Found Windows at W:
|
||||||
|
mkdir W:\Enrollment 2>NUL
|
||||||
|
|
||||||
|
REM --- Copy PPKG if selected ---
|
||||||
|
if "%PPKG%"=="" goto copy_pctype
|
||||||
|
copy /Y "Y:\%PPKG%" "W:\Enrollment\%PPKG%"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo WARNING: Failed to copy enrollment package.
|
||||||
|
goto copy_pctype
|
||||||
|
)
|
||||||
|
copy /Y "Y:\run-enrollment.ps1" "W:\run-enrollment.ps1"
|
||||||
|
|
||||||
|
REM --- Create enroll.cmd at drive root as manual fallback ---
|
||||||
|
> W:\enroll.cmd (
|
||||||
|
echo @echo off
|
||||||
|
echo echo Waiting for network...
|
||||||
|
echo :waitnet
|
||||||
|
echo ping -n 2 8.8.8.8 ^>NUL 2^>^&1
|
||||||
|
echo if errorlevel 1 goto waitnet
|
||||||
|
echo echo Network connected. Running enrollment...
|
||||||
|
echo powershell.exe -ExecutionPolicy Bypass -File "C:\run-enrollment.ps1"
|
||||||
|
)
|
||||||
|
echo Manual fallback created at W:\enroll.cmd
|
||||||
|
|
||||||
|
:copy_pctype
|
||||||
|
REM --- Copy shopfloor PC type setup scripts ---
|
||||||
|
if "%PCTYPE%"=="" goto cleanup_enroll
|
||||||
|
echo %PCTYPE%> W:\Enrollment\pc-type.txt
|
||||||
|
if not "%DISPLAYTYPE%"=="" echo %DISPLAYTYPE%> W:\Enrollment\display-type.txt
|
||||||
|
copy /Y "Y:\shopfloor-setup\Run-ShopfloorSetup.ps1" "W:\Enrollment\Run-ShopfloorSetup.ps1"
|
||||||
|
REM --- Always copy Shopfloor baseline scripts ---
|
||||||
|
mkdir W:\Enrollment\shopfloor-setup 2>NUL
|
||||||
|
if exist "Y:\shopfloor-setup\Shopfloor" (
|
||||||
|
mkdir W:\Enrollment\shopfloor-setup\Shopfloor 2>NUL
|
||||||
|
xcopy /E /Y /I "Y:\shopfloor-setup\Shopfloor" "W:\Enrollment\shopfloor-setup\Shopfloor\"
|
||||||
|
echo Copied Shopfloor baseline setup files.
|
||||||
|
)
|
||||||
|
REM --- Copy type-specific scripts on top of baseline ---
|
||||||
|
if "%PCTYPE%"=="Shopfloor" goto pctype_done
|
||||||
|
if exist "Y:\shopfloor-setup\%PCTYPE%" (
|
||||||
|
mkdir "W:\Enrollment\shopfloor-setup\%PCTYPE%" 2>NUL
|
||||||
|
xcopy /E /Y /I "Y:\shopfloor-setup\%PCTYPE%" "W:\Enrollment\shopfloor-setup\%PCTYPE%\"
|
||||||
|
echo Copied %PCTYPE% setup files.
|
||||||
|
) else (
|
||||||
|
echo WARNING: No setup files found for PC type %PCTYPE%.
|
||||||
|
)
|
||||||
|
:pctype_done
|
||||||
|
|
||||||
|
:cleanup_enroll
|
||||||
|
net use Y: /delete 2>NUL
|
||||||
|
|
||||||
:wait_finish
|
:wait_finish
|
||||||
ping -n 11 127.0.0.1 >NUL
|
ping -n 11 127.0.0.1 >NUL
|
||||||
tasklist /FI "IMAGENAME eq PESetup.exe" 2>NUL | find /I "PESetup.exe" >NUL
|
wmic process where "name='PESetup.exe'" get name 2>NUL | find /I "PESetup" >NUL
|
||||||
if not errorlevel 1 goto wait_finish
|
if not errorlevel 1 goto wait_finish
|
||||||
echo.
|
echo.
|
||||||
echo Imaging complete. Rebooting in 15 seconds...
|
echo Imaging complete. Rebooting in 15 seconds...
|
||||||
|
|||||||
Reference in New Issue
Block a user