build-pe-script-launcher/CLAUDE.md

14 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository Information

Git Repository: https://git.farmtowntech.com/setonc/build-pe-script-launcher
Branch: master
Status: Active development

# Clone the repository
git clone https://git.farmtowntech.com/setonc/build-pe-script-launcher.git

Project Overview

Build PE Script Launcher - A production-grade Windows PE builder that creates bootable images with PowerShell 7, custom modules, and automatic script launching from external media. The PE image remains static; rebuilds are only needed for PowerShell 7 or module updates.

Quick Start

# Clone and build
.\Build-PE.ps1

# Update PowerShell modules (optional)
.\Update-Modules.ps1

# Test the ISO in a VM
# Boot WinPE.iso with external media containing .scripts folder

Architecture

Core Components

Build-PE.ps1 - Main orchestrator

  • Validates dependencies (auto-installs Windows ADK if missing)
  • Downloads PowerShell 7 if needed
  • Mounts WinPE image via DISM
  • Integrates PowerShell 7 and modules
  • Injects launcher scripts
  • Creates bootable ISO

config.json - Configuration

  • PowerShell modules list
  • Target script name
  • Scripts folder name
  • PowerShell 7 version and download URL

lib/ - Build modules

  • Dependencies.psm1 - ADK validation and auto-installation
  • WinPEBuilder.psm1 - DISM operations (mount, unmount, add packages)
  • PowerShell7.psm1 - PS7 extraction and integration
  • ModuleManager.psm1 - Module copying to WinPE image

resources/ - Runtime scripts

  • Invoke-ScriptLauncher.ps1 - Runs on PE boot, scans for external media
  • Invoke-ScriptMenu.ps1 - Fallback menu for local .scripts folder

modules/ - PowerShell modules

  • Pre-downloaded modules from PSGallery
  • Committed to repository for offline builds
  • Updated via Update-Modules.ps1

Boot Flow

WinPE Boot
    ↓
startnet.cmd (modified)
    ↓
Console window maximizes automatically
    ↓
Invoke-ScriptLauncher.ps1 (PowerShell 7)
    ↓
Scans all drives for .scripts folder
    ↓
    ├─ Found on external media?
    │   ↓
    │   Execute configured target script
    │   (e.g., user's custom Invoke-ScriptMenu.ps1)
    │
    └─ Not found?
        ↓
        Check for local X:\.scripts
        ↓
        ├─ Found? → Launch Invoke-ScriptMenu.ps1
        └─ Not found? → Show instructions, drop to PowerShell 7 prompt

Console Features:

  • PowerShell 7 by default - All scripts and the command prompt use PS7
  • Maximized window - Console automatically maximizes on boot for full-screen experience
  • Elevated privileges - Everything runs as SYSTEM (no UAC in WinPE)
  • Classic UI - WinPE uses legacy console rendering (no modern W10/W11 decorations available)

Build Process Workflow

  1. Dependency Check - Validates admin rights, ADK installation, PS7 availability
  2. Workspace Init - Creates work directory using ADK's copype.cmd
  3. Mount Image - Mounts boot.wim for modification
  4. Add Components - Installs WinPE optional components (WMI, NetFX, PowerShell, etc.)
  5. Install PS7 - Extracts PowerShell 7 to Program Files\PowerShell\7
  6. Copy Modules - Copies modules from repository to image
  7. Inject Scripts - Copies launcher and menu scripts to System32
  8. Configure Startup - Modifies startnet.cmd to run launcher
  9. Unmount - Commits changes and unmounts image
  10. Create ISO - Generates bootable ISO using MakeWinPEMedia

Configuration

config.json Structure

{
  "powershellModules": ["ModuleName1", "ModuleName2"],
  "targetScript": "Invoke-ScriptMenu.ps1",
  "scriptsFolder": ".scripts",
  "startupScript": "Invoke-ScriptLauncher.ps1",
  "fallbackScript": "Invoke-ScriptMenu.ps1",
  "consoleColors": {
    "backgroundColor": "Black",
    "foregroundColor": "Gray"
  },
  "powershell7Version": "7.4.13",
  "powershell7ZipUrl": "https://github.com/PowerShell/PowerShell/releases/..."
}

Configuration Options:

powershellModules - Array of module names to include in WinPE (from PSGallery) targetScript - Script name to execute from external media .scripts folder scriptsFolder - Name of folder to search for on external media (default: ".scripts") startupScript - Script that runs first on boot (default: "Invoke-ScriptLauncher.ps1")

  • Must exist in resources/ folder
  • This is the entry point for your WinPE environment fallbackScript - Script embedded in image for offline/local use (default: "Invoke-ScriptMenu.ps1")
  • Must exist in resources/ folder
  • Runs when no external media is found but X:.scripts exists consoleColors - Customize PowerShell console appearance
  • backgroundColor: Console background color (Black, DarkBlue, DarkGreen, etc.)
  • foregroundColor: Console text color (Gray, White, Cyan, etc.) powershell7Version - Version of PowerShell 7 to integrate powershell7ZipUrl - Direct download URL for PS7 zip archive

Commands

Build WinPE Image

.\Build-PE.ps1

First run downloads Windows ADK (~4GB, 20-30 min), subsequent builds are faster (~10 min).

Options:

  • -CleanBuild - Remove existing work directory
  • -SkipADKCheck - Skip ADK validation (not recommended)
  • -DefenderOptimization <Mode> - Optimize Windows Defender for faster builds (see Performance Optimization below)

Build with Performance Optimization

Windows Defender real-time scanning significantly impacts DISM performance. Use -DefenderOptimization for faster builds:

# Fastest: Temporarily disable real-time protection (50-70% improvement)
.\Build-PE.ps1 -DefenderOptimization DisableRealTime

# Balanced: Add build directories to exclusions (20-30% improvement)
.\Build-PE.ps1 -DefenderOptimization AddExclusions

# Default: No Defender changes (safest)
.\Build-PE.ps1 -DefenderOptimization None

Defender optimization modes:

  • None (default) - No changes to Defender, safest option
  • DisableRealTime - Temporarily disable real-time protection for build duration, restored automatically (fastest)
  • AddExclusions - Add build directories and ADK paths to Defender exclusions, removed after build (balanced)

Note: Defender settings are automatically restored when the build completes, even if it fails.

Update Modules

# Update all modules in config.json
.\Update-Modules.ps1

# Update specific module
.\Update-Modules.ps1 -ModuleName "PSWindowsUpdate"

# Force re-download
.\Update-Modules.ps1 -ModuleName "PSWindowsUpdate" -Force

Directory Structure

Build PE Script Launcher/
├── Build-PE.ps1              # Main build script
├── Update-Modules.ps1        # Module updater utility
├── config.json               # Build configuration
├── lib/                      # Build-time modules
│   ├── Dependencies.psm1
│   ├── WinPEBuilder.psm1
│   ├── PowerShell7.psm1
│   ├── ModuleManager.psm1
│   └── DefenderOptimization.psm1
├── resources/                # Runtime scripts
│   ├── Invoke-ScriptLauncher.ps1
│   └── Invoke-ScriptMenu.ps1
├── modules/                  # PowerShell modules (committed)
│   └── <ModuleName>/
├── work/                     # Build workspace (gitignored)
│   ├── mount/               # Mounted WinPE image
│   └── media/               # WinPE media files
└── output/                   # Build outputs (gitignored)
    ├── WinPE.iso
    └── build.log

Key Technical Details

Windows ADK Integration

  • Auto-installs via silent parameters: /quiet /norestart /features OptionId.DeploymentTools
  • Installs both ADK core and WinPE add-on
  • Uses copype.cmd to create initial WinPE workspace
  • Uses MakeWinPEMedia.cmd to create bootable ISO

PowerShell 7 Integration

  • Based on community method (not officially supported by Microsoft)
  • Extracts PS7 portable zip to X:\Program Files\PowerShell\7
  • Modifies startnet.cmd to add PS7 to PATH
  • Uses PowerShell 7 LTS (v7.4.13) for 3-year support

DISM Operations

All image modifications use DISM commands:

  • Mount: dism /Mount-Image /ImageFile:boot.wim /Index:1 /MountDir:mount
  • Add package: dism /Add-Package /Image:mount /PackagePath:package.cab
  • Unmount: dism /Unmount-Image /MountDir:mount /Commit

Module Management

  • Modules stored in modules/ using Save-Module structure
  • Copied to X:\Program Files\WindowsPowerShell\Modules\ in image
  • Dependencies automatically resolved by Update-Modules.ps1

Troubleshooting

Build fails with "Image is already mounted"

dism /Cleanup-Mountpoints

ADK installation fails

  • Check internet connection
  • Ensure sufficient disk space (~10GB)
  • Run as Administrator

Module not found during build

# Download missing module
.\Update-Modules.ps1 -ModuleName "ModuleName"

# Or verify config.json module names match PSGallery exactly

PowerShell 7 not in PATH when PE boots

  • Check startnet.cmd modification in mounted image
  • Verify PS7 extracted to correct path
  • Review build.log for errors

Modifying the Project

Adding a New Module

  1. Add module name to config.json powershellModules array
  2. Run .\Update-Modules.ps1 -ModuleName "NewModule"
  3. Rebuild: .\Build-PE.ps1

Changing Scripts

External Media Script (runs from USB/external drive):

  1. Modify targetScript in config.json
  2. Ensure your script exists in external media's .scripts folder
  3. Rebuild: .\Build-PE.ps1

Startup Script (first script that runs on boot):

  1. Create your custom script in resources/ folder
  2. Update startupScript in config.json with your script name
  3. Rebuild: .\Build-PE.ps1
  4. Script must handle external media detection or call other scripts

Fallback Script (embedded for offline use):

  1. Create your custom script in resources/ folder
  2. Update fallbackScript in config.json with your script name
  3. Rebuild: .\Build-PE.ps1
  4. Script will be available at X:\Windows\System32\ when no external media found

Customizing Console Colors

  1. Edit consoleColors in config.json
  2. Choose from: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White
  3. Rebuild: .\Build-PE.ps1

Example - Dark blue theme:

"consoleColors": {
  "backgroundColor": "DarkBlue",
  "foregroundColor": "White"
}

Upgrading PowerShell 7

  1. Update powershell7Version and powershell7ZipUrl in config.json
  2. Delete old PowerShell zip file
  3. Rebuild: .\Build-PE.ps1

Customizing Launcher Behavior

Edit resources/Invoke-ScriptLauncher.ps1:

  • Change drive scanning logic
  • Modify error messages
  • Add additional checks
  • Customize banner display

Use Cases

Driver Extraction from Offline Windows

User boots WinPE → Launcher finds .scripts on USB → Runs driver export script

System Recovery Scripts

Multiple recovery scripts on external media → Menu lets user choose → Script runs with PS7 and modules available

Automated Deployment

Pre-configured script on known media → Launcher auto-executes → Unattended deployment

External Media Setup

For End Users

  1. Format USB drive
  2. Create .scripts folder in root
  3. Add target script (e.g., Invoke-ScriptMenu.ps1)
  4. Add additional scripts
  5. Boot WinPE image

Example .scripts Structure

USB:\
└── .scripts\
    ├── Invoke-ScriptMenu.ps1  # Entry point
    ├── Export-Drivers.ps1
    ├── Backup-System.ps1
    └── utilities\
        └── helpers.psm1

Performance Notes

Build Times (without optimization)

  • First build: 30-45 minutes (ADK download + install)
  • Subsequent builds: 5-10 minutes
  • ISO size: ~500MB
  • Module impact: ~10-50MB per module typically

Windows Defender Optimization

Windows Defender real-time scanning significantly impacts DISM and ADK performance. Use -DefenderOptimization for dramatic speed improvements:

Performance Impact by Mode:

Mode First Build Subsequent Improvement Risk Level
None (default) 30-45 min 5-10 min Baseline Safest
AddExclusions 24-32 min 4-7 min 20-30% faster Low
DisableRealTime 15-20 min 2-4 min 50-70% faster Low*

*DisableRealTime temporarily disables protection but automatically restores it when build completes (even on failure).

Which mode to use:

  • Development: Use DisableRealTime for fastest builds
  • CI/CD pipelines: Use AddExclusions for good performance with minimal risk
  • Production/Shared systems: Use None (default) for safety
  • Enterprise: Use AddExclusions with approval from security team

How it works:

  • DisableRealTime - Temporarily disables Windows Defender real-time protection during build, restores original state when done
  • AddExclusions - Adds work/, downloads/, output/, and ADK paths to Defender exclusions during build, removes them when done
  • None - No changes to Defender (default behavior)

All Defender changes are automatically reverted when the build completes, even if the build fails.

Security Considerations

  • Scripts run with SYSTEM privileges in WinPE
  • No execution policy restrictions
  • Only use trusted scripts
  • Validate all external media scripts before use
  • Consider signing scripts for enterprise deployment
  • Defender Optimization: When using -DefenderOptimization, Defender settings are automatically restored after build. Even if build fails, the finally block ensures restoration. Safe for development use.

Known Limitations

  • PowerShell 7 in WinPE is unofficial (works but unsupported)
  • Some cmdlets (Get-PhysicalDisk) may not work under PS7 in WinPE
  • UAC not present in WinPE (all scripts run elevated)
  • Network drivers may need manual addition for some hardware
  • UI Limitations:
    • Console uses legacy Windows console rendering (no modern W10/W11 window decorations)
    • WinPE lacks DWM (Desktop Window Manager) and modern shell components
    • True borderless fullscreen not available; window maximization is the best alternative
    • No Aero, Fluent Design, or modern theming support