diff --git a/autoinstall/user-data b/autoinstall/user-data index feb9aba..ebf1dd2 100644 --- a/autoinstall/user-data +++ b/autoinstall/user-data @@ -55,6 +55,7 @@ autoinstall: cp /mnt/cidata/packages/*.deb /tmp/ dpkg -i /tmp/*.deb 2>/dev/null || true dpkg -i /tmp/*.deb 2>/dev/null || true + dpkg --configure -a 2>/dev/null || true if command -v nmcli >/dev/null; then systemctl enable NetworkManager fi @@ -78,6 +79,7 @@ autoinstall: if compgen -G "/mnt/usb/packages/*.deb" > /dev/null; then dpkg -i /mnt/usb/packages/*.deb 2>/dev/null || true dpkg -i /mnt/usb/packages/*.deb 2>/dev/null || true + dpkg --configure -a 2>/dev/null || true fi if [ -f /mnt/usb/playbook/pxe_server_setup.yml ]; then cd /mnt/usb/playbook diff --git a/build-usb.sh b/build-usb.sh index cfc8ffe..734d80e 100755 --- a/build-usb.sh +++ b/build-usb.sh @@ -270,6 +270,18 @@ else echo " No boot-tools/ found (run prepare-boot-tools.sh first)" fi +# Copy enrollment directory (PPKGs, run-enrollment.ps1) if present +ENROLLMENT_DIR="$SCRIPT_DIR/enrollment" +if [ -d "$ENROLLMENT_DIR" ]; then + mkdir -p "$MOUNT_POINT/enrollment" + cp -r "$ENROLLMENT_DIR"/* "$MOUNT_POINT/enrollment/" 2>/dev/null || true + PPKG_COUNT=$(find "$MOUNT_POINT/enrollment" -name '*.ppkg' 2>/dev/null | wc -l) + ENROLL_SIZE=$(du -sh "$MOUNT_POINT/enrollment" | cut -f1) + echo " Copied enrollment/ ($ENROLL_SIZE, $PPKG_COUNT PPKGs)" +else + echo " No enrollment/ directory found (PPKGs can be uploaded via webapp later)" +fi + # Optionally copy WinPE deployment images if [ -n "$WINPE_SOURCE" ] && [ -d "$WINPE_SOURCE" ]; then echo " Copying WinPE deployment content from $WINPE_SOURCE..." diff --git a/playbook/pxe_server_setup.yml b/playbook/pxe_server_setup.yml index d2ccdd6..066a878 100644 --- a/playbook/pxe_server_setup.yml +++ b/playbook/pxe_server_setup.yml @@ -306,6 +306,19 @@ state: directory mode: '0777' + - name: "Deploy PPKG enrollment packages to enrollment share" + shell: cp -f {{ usb_mount }}/enrollment/*.ppkg /srv/samba/enrollment/ 2>/dev/null || true + args: + warn: false + ignore_errors: yes + + - name: "Deploy run-enrollment.ps1 to enrollment share" + copy: + src: "{{ usb_mount }}/shopfloor-setup/run-enrollment.ps1" + dest: /srv/samba/enrollment/run-enrollment.ps1 + mode: '0644' + ignore_errors: yes + - name: "Deploy shopfloor setup scripts to enrollment share" copy: src: "{{ usb_mount }}/shopfloor-setup/" @@ -674,7 +687,7 @@ dest: /etc/cron.d/dnsmasq-restart mode: '0644' content: | - @reboot root /bin/sleep 15 && /usr/bin/systemctl restart dnsmasq.service + @reboot root /bin/sleep 30 && /usr/bin/systemctl restart dnsmasq.service # --- Web Management App (Flask) --- - name: "Create webapp directory" diff --git a/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 b/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 index 25e090d..9423eb5 100644 --- a/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 +++ b/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 @@ -480,44 +480,51 @@ function Format-Snapshot { $lines += " $(Mk $Snap.Phase1.IntuneEnrolled) Intune enrolled" $lines += " $(Mk $Snap.Phase1.EmTaskExists) EnterpriseMgmt sync tasks" $lines += " $(Mk $Snap.Phase1.PoliciesArriving) Policies arriving" - $lines += "" - $lines += " Phase 2: SFLD configuration" - $lines += " $(Mk $Snap.Phase2.SfldRoot) SFLD reg key" - $lines += " $(Mk $Snap.Phase2.FunctionOk) Function set" - $lines += " $(Mk $Snap.Phase2.SasTokenOk) DSC SAS token configured" - $lines += "" - $lines += " Phase 3: DSC deployment + install" - $lines += " $(Mk $Snap.Phase3.DeployLogExists) DSCDeployment.log present" - $lines += " $(Mk $Snap.Phase3.DeployComplete) Pre-reboot deployment complete" - $lines += " $(Mk $Snap.Phase3.InstallLogExists) DSCInstall.log present" - $lines += " $(Mk $Snap.Phase3.InstallComplete) Post-reboot install complete" - $lines += "" - $lines += " Phase 4: Custom scripts (auto-discovered)" - if (-not $Snap.Phase4 -or $Snap.Phase4.Count -eq 0) { - $lines += " (no Install-*.log files yet in C:\Logs\SFLD)" - } else { - foreach ($s in $Snap.Phase4) { - $mark = switch ($s.Status) { - 'done' { '[v]' } - 'running' { '[.]' } - 'failed' { '[!]' } - 'pending' { '[ ]' } - default { '[?]' } + + if (-not $skipDsc) { + $lines += "" + $lines += " Phase 2: SFLD configuration" + $lines += " $(Mk $Snap.Phase2.SfldRoot) SFLD reg key" + $lines += " $(Mk $Snap.Phase2.FunctionOk) Function set" + $lines += " $(Mk $Snap.Phase2.SasTokenOk) DSC SAS token configured" + $lines += "" + $lines += " Phase 3: DSC deployment + install" + $lines += " $(Mk $Snap.Phase3.DeployLogExists) DSCDeployment.log present" + $lines += " $(Mk $Snap.Phase3.DeployComplete) Pre-reboot deployment complete" + $lines += " $(Mk $Snap.Phase3.InstallLogExists) DSCInstall.log present" + $lines += " $(Mk $Snap.Phase3.InstallComplete) Post-reboot install complete" + $lines += "" + $lines += " Phase 4: Custom scripts (auto-discovered)" + if (-not $Snap.Phase4 -or $Snap.Phase4.Count -eq 0) { + $lines += " (no Install-*.log files yet in C:\Logs\SFLD)" + } else { + foreach ($s in $Snap.Phase4) { + $mark = switch ($s.Status) { + 'done' { '[v]' } + 'running' { '[.]' } + 'failed' { '[!]' } + 'pending' { '[ ]' } + default { '[?]' } + } + $detail = switch ($s.Status) { + 'done' { "done $(Format-Age $s.Age) ago" } + 'running' { "running, last update $(Format-Age $s.Age) ago" } + 'failed' { "FAILED $(Format-Age $s.Age) ago" } + 'pending' { "pending" } + default { "" } + } + $name = $s.Name.PadRight(22) + $lines += " $mark $name $detail" } - $detail = switch ($s.Status) { - 'done' { "done $(Format-Age $s.Age) ago" } - 'running' { "running, last update $(Format-Age $s.Age) ago" } - 'failed' { "FAILED $(Format-Age $s.Age) ago" } - 'pending' { "pending" } - default { "" } - } - $name = $s.Name.PadRight(22) - $lines += " $mark $name $detail" } + $lines += "" + $lines += " Phase 5: Final" + $lines += " $(Mk $Snap.Phase5.ConsumeCredsTask) SFLD - Consume Credentials task" + } else { + $lines += "" + $lines += " (DSC phases not applicable for $pcType)" } - $lines += "" - $lines += " Phase 5: Final" - $lines += " $(Mk $Snap.Phase5.ConsumeCredsTask) SFLD - Consume Credentials task" + $lines += "" $sinceSync = ((Get-Date) - $LastSync).TotalSeconds $untilNext = ($NextRetrigger - (Get-Date)).TotalSeconds @@ -674,6 +681,20 @@ if ($AsTask -and (Test-Path -LiteralPath $syncCompleteMarker)) { exit 0 } +# PC types that don't receive DSC (no SAS token, no DSCInstall.log). +# For these types, enrollment is "done" once Phase 1 (Identity) completes +# and policies start arriving - there is no Phase 2-5 to wait for. +$pcTypeFile = 'C:\Enrollment\pc-type.txt' +$noDscTypes = @('Display') +$skipDsc = $false +if (Test-Path $pcTypeFile) { + $pcType = (Get-Content $pcTypeFile -First 1).Trim() + if ($noDscTypes -contains $pcType) { + $skipDsc = $true + Write-Host "PC type '$pcType' does not use DSC - will complete after Phase 1." + } +} + try { $qrText = Build-QRCodeText $qrRefreshed = [bool]($qrText -notmatch 'not yet Azure AD joined') @@ -705,6 +726,13 @@ try { Invoke-SetupComplete } + # No-DSC types (Display, Lab): complete once Phase 1 identity is solid + if ($skipDsc -and $snap.Phase1.AzureAdJoined -and $snap.Phase1.IntuneEnrolled -and $snap.Phase1.PoliciesArriving) { + Write-Host "" + Write-Host "Phase 1 complete - no DSC needed for $pcType. Finishing up." -ForegroundColor Green + Invoke-SetupComplete + } + # Reboot check (boot-loop-safe). Only prompt once Phase 1 (Identity), # Phase 2 (SFLD config), and Phase 3's first two items (deploy log # present + pre-reboot deployment complete) are all green. Don't rush