enrollment: auto-activate uploaded ppkg via ppkg.conf (no boot.wim edit)
startnet.cmd now sources SOURCE_PPKG/PPKG_VER/PPKG_EXP from \\<pxe>\enrollment\ppkg.conf (baked-in values kept as fallback if the file is missing). The webapp writes ppkg.conf on upload for standard GCCH_..._v<ver>.ppkg files: version parsed from the filename, optional expiry field (blank keeps current), so a new ppkg goes live at next boot with no startnet/boot.wim edit. Enrollment page shows the active ppkg. ppkg.conf seeded on the share (v4.16) and the ppkg.conf-aware startnet.cmd deployed into boot.wim.
This commit is contained in:
@@ -176,10 +176,23 @@ set /p enroll=Enter your choice (1-6):
|
|||||||
REM --- PPKG configuration (constructed at menu time, see docs) ---
|
REM --- PPKG configuration (constructed at menu time, see docs) ---
|
||||||
REM Vendor ships one source PPKG; we construct the BPRT-tagged filename
|
REM Vendor ships one source PPKG; we construct the BPRT-tagged filename
|
||||||
REM by filling in Office, Region, Expiry, Version on the target copy.
|
REM by filling in Office, Region, Expiry, Version on the target copy.
|
||||||
REM Update SOURCE_PPKG + PPKG_VER when a new PPKG is released.
|
REM The ACTIVE ppkg is read from \\172.16.9.1\enrollment\ppkg.conf, which the
|
||||||
set SOURCE_PPKG=GCCH_Prod_SFLD_v4.16.ppkg
|
REM PXE webapp writes on upload - so a new ppkg goes live with no startnet edit.
|
||||||
set PPKG_VER=v4.16
|
REM The baked-in values below are only a fallback if ppkg.conf is missing.
|
||||||
set PPKG_EXP=20260831
|
net use Y: \\172.16.9.1\enrollment /user:pxe-upload pxe /persistent:no >nul 2>&1
|
||||||
|
set SOURCE_PPKG=
|
||||||
|
set PPKG_VER=
|
||||||
|
set PPKG_EXP=
|
||||||
|
if exist "Y:\ppkg.conf" (
|
||||||
|
for /f "usebackq tokens=1,* delims==" %%A in ("Y:\ppkg.conf") do (
|
||||||
|
if /i "%%A"=="SOURCE_PPKG" set "SOURCE_PPKG=%%B"
|
||||||
|
if /i "%%A"=="PPKG_VER" set "PPKG_VER=%%B"
|
||||||
|
if /i "%%A"=="PPKG_EXP" set "PPKG_EXP=%%B"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if "%SOURCE_PPKG%"=="" set SOURCE_PPKG=GCCH_Prod_SFLD_v4.16.ppkg
|
||||||
|
if "%PPKG_VER%"=="" set PPKG_VER=v4.16
|
||||||
|
if "%PPKG_EXP%"=="" set PPKG_EXP=20260831
|
||||||
set REGION=US
|
set REGION=US
|
||||||
|
|
||||||
set OFFICE=
|
set OFFICE=
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ This file is the route surface; most logic lives in ``services/``:
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
@@ -814,6 +815,37 @@ def imaging_set_laps(serial):
|
|||||||
# Routes - Enrollment Packages
|
# Routes - Enrollment Packages
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PPKG_CONF = os.path.join(config.ENROLLMENT_SHARE, "ppkg.conf")
|
||||||
|
|
||||||
|
|
||||||
|
def read_ppkg_conf():
|
||||||
|
"""Read the active-PPKG config (SOURCE_PPKG/PPKG_VER/PPKG_EXP) that
|
||||||
|
startnet.cmd sources at boot. Returns a dict (empty if unset)."""
|
||||||
|
data = {}
|
||||||
|
if os.path.isfile(PPKG_CONF):
|
||||||
|
try:
|
||||||
|
with open(PPKG_CONF, "r", errors="replace") as fh:
|
||||||
|
for line in fh:
|
||||||
|
if "=" in line:
|
||||||
|
k, _, v = line.strip().partition("=")
|
||||||
|
data[k.strip()] = v.strip()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def write_ppkg_conf(source_ppkg, ppkg_ver, ppkg_exp):
|
||||||
|
"""Write ppkg.conf (CRLF) so startnet.cmd picks up the active ppkg at
|
||||||
|
boot with no boot.wim edit."""
|
||||||
|
lines = [
|
||||||
|
f"SOURCE_PPKG={source_ppkg}",
|
||||||
|
f"PPKG_VER={ppkg_ver}",
|
||||||
|
f"PPKG_EXP={ppkg_exp}",
|
||||||
|
]
|
||||||
|
with open(PPKG_CONF, "w", newline="\r\n") as fh:
|
||||||
|
fh.write("\n".join(lines) + "\n")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/enrollment")
|
@app.route("/enrollment")
|
||||||
def enrollment():
|
def enrollment():
|
||||||
packages = []
|
packages = []
|
||||||
@@ -830,6 +862,7 @@ def enrollment():
|
|||||||
return render_template(
|
return render_template(
|
||||||
"enrollment.html",
|
"enrollment.html",
|
||||||
packages=packages,
|
packages=packages,
|
||||||
|
active_ppkg=read_ppkg_conf(),
|
||||||
image_types=config.IMAGE_TYPES,
|
image_types=config.IMAGE_TYPES,
|
||||||
friendly_names=config.FRIENDLY_NAMES,
|
friendly_names=config.FRIENDLY_NAMES,
|
||||||
)
|
)
|
||||||
@@ -856,6 +889,21 @@ def enrollment_upload():
|
|||||||
f.save(dest)
|
f.save(dest)
|
||||||
audit("ENROLLMENT_UPLOAD", filename)
|
audit("ENROLLMENT_UPLOAD", filename)
|
||||||
flash(f"Uploaded {filename} successfully.", "success")
|
flash(f"Uploaded {filename} successfully.", "success")
|
||||||
|
|
||||||
|
# 2b: set this ppkg active for startnet.cmd by writing ppkg.conf on the
|
||||||
|
# share (no boot.wim edit). Only for the standard GCCH_..._v<ver>.ppkg name.
|
||||||
|
m = re.search(r"_(v\d+(?:\.\d+)*)\.ppkg$", filename, re.IGNORECASE)
|
||||||
|
if m:
|
||||||
|
ver = m.group(1)
|
||||||
|
expiry = (request.form.get("expiry") or "").strip()
|
||||||
|
if not expiry:
|
||||||
|
expiry = read_ppkg_conf().get("PPKG_EXP", "")
|
||||||
|
try:
|
||||||
|
write_ppkg_conf(filename, ver, expiry)
|
||||||
|
audit("PPKG_ACTIVATE", f"{filename} ver={ver} exp={expiry}")
|
||||||
|
flash(f"Set {filename} as the active PPKG - startnet.cmd will use it.", "success")
|
||||||
|
except OSError as exc:
|
||||||
|
flash(f"Uploaded, but could not update ppkg.conf: {exc}", "warning")
|
||||||
return redirect(url_for("enrollment"))
|
return redirect(url_for("enrollment"))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if active_ppkg and active_ppkg.SOURCE_PPKG %}
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<strong>Active PPKG:</strong> <span class="mono">{{ active_ppkg.SOURCE_PPKG }}</span>
|
||||||
|
<span class="badge badge-info">{{ active_ppkg.PPKG_VER }}</span>
|
||||||
|
{% if active_ppkg.PPKG_EXP %}<span class="badge badge-secondary">exp {{ active_ppkg.PPKG_EXP }}</span>{% endif %}
|
||||||
|
<span class="form-text">- startnet.cmd boots with this (from <span class="mono">ppkg.conf</span>).</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span><i class="bi bi-box-seam"></i> GCCH Provisioning Packages</span>
|
<span><i class="bi bi-box-seam"></i> GCCH Provisioning Packages</span>
|
||||||
@@ -89,9 +98,16 @@
|
|||||||
<input type="file" class="form-control" id="ppkgFile" name="ppkg_file"
|
<input type="file" class="form-control" id="ppkgFile" name="ppkg_file"
|
||||||
accept=".ppkg" required>
|
accept=".ppkg" required>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
Use <span class="mono">with-office.ppkg</span> or <span class="mono">without-office.ppkg</span> to match the WinPE boot menu.
|
A standard <span class="mono">GCCH_Prod_SFLD_v<ver>.ppkg</span> upload is set as the active PPKG automatically
|
||||||
|
(written to <span class="mono">ppkg.conf</span> on the share; startnet.cmd reads it at boot - no boot.wim edit).
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<label for="ppkgExpiry" class="form-label">Expiry (optional)</label>
|
||||||
|
<input type="text" class="form-control mono" id="ppkgExpiry" name="expiry"
|
||||||
|
placeholder="YYYYMMDD" pattern="\d{8}">
|
||||||
|
<div class="form-text">Blank keeps the current expiry.</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user