diff --git a/archive/FFUDevelopment/unattend/prefixes.txt b/FFUDevelopment/unattend/prefixes.txt similarity index 100% rename from archive/FFUDevelopment/unattend/prefixes.txt rename to FFUDevelopment/unattend/prefixes.txt diff --git a/archive/FFUDevelopment/unattend/unattend.xml b/FFUDevelopment/unattend/unattend.xml similarity index 100% rename from archive/FFUDevelopment/unattend/unattend.xml rename to FFUDevelopment/unattend/unattend.xml diff --git a/archive/FFUDevelopment/Apps/InstallAppsandSysprep.cmd b/archive/FFUDevelopment/Apps/InstallAppsandSysprep.cmd deleted file mode 100644 index e3533f1..0000000 --- a/archive/FFUDevelopment/Apps/InstallAppsandSysprep.cmd +++ /dev/null @@ -1,13 +0,0 @@ -REM Put each app install on a separate line -REM M365 Apps/Office ProPlus -d:\Office\setup.exe /configure d:\office\DeployFFU.xml -REM Add additional apps below here - - -REM The below lines will remove the unattend.xml that gets the machine into audit mode. If not removed, the OS will get stuck booting to audit mode each time. -REM Also kills the sysprep process in order to automate sysprep generalize -del c:\windows\panther\unattend\unattend.xml /F /Q -del c:\windows\panther\unattend.xml /F /Q -taskkill /IM sysprep.exe -timeout /t 5 -c:\windows\system32\sysprep\sysprep.exe /quiet /generalize /oobe \ No newline at end of file diff --git a/archive/FFUDevelopment/Apps/Office/DeployFFU.xml b/archive/FFUDevelopment/Apps/Office/DeployFFU.xml deleted file mode 100644 index 6977fb3..0000000 --- a/archive/FFUDevelopment/Apps/Office/DeployFFU.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/archive/FFUDevelopment/Apps/Office/DownloadFFU.xml b/archive/FFUDevelopment/Apps/Office/DownloadFFU.xml deleted file mode 100644 index 9054148..0000000 --- a/archive/FFUDevelopment/Apps/Office/DownloadFFU.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/archive/FFUDevelopment/BuildFFUUnattend/unattend.xml b/archive/FFUDevelopment/BuildFFUUnattend/unattend.xml deleted file mode 100644 index 9590b1e..0000000 --- a/archive/FFUDevelopment/BuildFFUUnattend/unattend.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - 1 - d:\InstallAppsandSysprep.cmd - - - - - - - - Audit - - - - - diff --git a/archive/FFUDevelopment/BuildFFUVM.ps1 b/archive/FFUDevelopment/BuildFFUVM.ps1 deleted file mode 100644 index 025528c..0000000 --- a/archive/FFUDevelopment/BuildFFUVM.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -#Modify variables -$VMPath = "c:\VM\$VMName" -$ISOPath = "E:\software\ISOs\Windows\Windows 11\en-us_windows_11_consumer_editions_version_22h2_updated_feb_2023_x64_dvd_4fa87138.iso" -$memory = 8GB -$disksize = 30GB -$processors = 4 -$rand = get-random -$VMName = "_FFU-$rand" -$VHDPath = "$VMPath\$VMName.vhdx" - -# 0. Delete old VMs and remove old certs - -$certPath = 'Cert:\LocalMachine\Shielded VM Local Certificates\' -$vms = get-vm _ffu-* | ? {$_.state -ne 'running'} - -If($null -ne $vms){ - Foreach ($vm in $vms){ - $OldVMName = $vm.VMName - Remove-VM -Name $vm.name -Force -ErrorAction SilentlyContinue - Remove-Item -Path "C:\VM\$OldVMName" -Force -Recurse -ErrorAction SilentlyContinue - Remove-HgsGuardian -Name $OldVMName - $certs = Get-ChildItem -Path $certPath -Recurse | Where-Object { $_.Subject -like "*$OldVMName*" } - foreach ($cert in $Certs){ - Remove-item -Path $cert.PSPath -force - } - } -} - -# 1. Create Dynamic Hard Disk -mkdir -Path $VMPath -Force -New-VHD -Path $VHDPath -Fixed -SizeBytes $disksize -#New-VHD -path $VHDPath -SizeBytes 128000000000 -LogicalSectorSizeBytes 512 -Dynamic - -# 2. Create VM -# - Name: _FFU -# - Location: C:\VM\_FFU -# - Generation: 2 -# - Memory: 4GB static -# - Networking: Not connected -# - Connect to existing VHD: c:\VM\_FFU\ -# - Mount ISO -$VM = New-VM -Name $VMName -Path $VMPath -MemoryStartupBytes $memory -VHDPath $VHDPath -Generation 2 -Set-VMProcessor -VMName $VMName -Count $processors -Add-VMDvdDrive -VMName $VMName -Path $ISOPath -$VMDVDDrive = Get-VMDvdDrive -VMName $VMName -Set-VMFirmware -VMName $VMName -FirstBootDevice $VMDVDDrive - -#Configure TPM -New-HgsGuardian -Name $VMName -GenerateCertificates -$owner = get-hgsguardian -Name $VMName -$kp = New-HgsKeyProtector -Owner $owner -AllowUntrustedRoot -Set-VMKeyProtector -VMName $VMName -KeyProtector $kp.RawData -Enable-VMTPM -VMName $VMName - -#Connect to VM -vmconnect $VM.ComputerName $VMName - -#Use if creating for MDT/SCCM Builds -#Get-VM $VMName | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName "Intel(R) I350 Gigabit Network Connection - Virtual Switch" -#$networkAdapter = Get-VMNetworkAdapter -VMName $VMName \ No newline at end of file diff --git a/archive/FFUDevelopment/BuildFFUVMv2.ps1 b/archive/FFUDevelopment/BuildFFUVMv2.ps1 deleted file mode 100644 index 5337759..0000000 --- a/archive/FFUDevelopment/BuildFFUVMv2.ps1 +++ /dev/null @@ -1,563 +0,0 @@ -#Requires -Modules Hyper-V, Storage -#Requires -PSEdition Desktop - -# To do list (will get around to this stuff sometime next week) - -# Change from using variables to parameters - this way you don't even need to open the script to edit it. -# Clean up the output it sends to powershell so you know what step you're on -# Do some real logging incase folks get in trouble and need my help -# Edit the WinPECaptureFFUFiles\CaptureFFU.ps1 file within this script instead of doing it manually.  -# Make Capture/Deploy media creation optional -# Change DownloadFFU.xml file to match C:\FFUDevelopment path -# If drivers = true, check if drivers folder exists -# C:\VM\VMFolder isn't being deleted - -#Modify Required variables -$ISOPath = "E:\software\ISOs\Windows\Windows 11\en-us_windows_11_consumer_editions_version_22h2_updated_feb_2023_x64_dvd_4fa87138.iso" -$WindowsSKU = 'Pro' -$FFUDevelopmentPath = 'C:\FFUDevelopment' -$AppsISO = "$FFUDevelopmentPath\Apps.iso" -$AppsPath = "$FFUDevelopmentPath\Apps" -$InstallOffice = $true -$InstallApps = $true -$InstallDrivers = $true -$memory = 8GB -$disksize = 30GB -$processors = 4 -$VMSwitchName = '*intel*' - -#Optional variables -$rand = get-random -$VMName = "_FFU-$rand" -$VMLocation = "c:\VM" -$VMPath = $VMLocation + $VMName -$VHDXPath = "$VMPath\$VMName.vhdx" - -#FUNCTIONS -Function Get-ADK { - # Define the registry key and value name to query - $adkRegKey = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots" - $adkRegValueName = "KitsRoot10" - - # Check if the registry key exists - if (Test-Path $adkRegKey) { - # Get the registry value for the Windows ADK installation path - $adkPath = (Get-ItemProperty -Path $adkRegKey -Name $adkRegValueName).$adkRegValueName - - if ($adkPath) { - return $adkPath - } - } - else { - throw "Windows ADK is not installed or the installation path could not be found." - } -} -function Get-ODTURL { - - [String]$MSWebPage = Invoke-RestMethod 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=49117' - - $MSWebPage | ForEach-Object { - if ($_ -match 'url=(https://.*officedeploymenttool.*\.exe)') { - $matches[1] - } - } -} - -function Get-Office { - #Download ODT - $ODTUrl = Get-ODTURL - $ODTInstallFile = "$env:TEMP\odtsetup.exe" - Invoke-WebRequest -Uri $ODTUrl -OutFile $ODTInstallFile - - # Extract ODT - $ODTPath = "$AppsPath\Office" - Start-Process -FilePath $ODTInstallFile -ArgumentList "/extract:$ODTPath /quiet" -Wait - - # Run setup.exe with config.xml - $ConfigXml = "$ODTPath\DownloadFFU.xml" - #Set-Location $ODTPath - Start-Process -FilePath "$ODTPath\setup.exe" -ArgumentList "/download $ConfigXml" -Wait - - #Clean up default configuration files - Remove-Item -Path "$ODTPath\configuration*" -Force -} - -function New-AppsISO { - #Create Apps ISO file - $OSCDIMG = 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe' - Start-Process -FilePath $OSCDIMG -ArgumentList "-n -m -d $Appspath $AppsISO" -wait - - #Remove the Office Download and ODT - if ($InstallOffice) { - $ODTPath = "$AppsPath\Office" - $OfficeDownloadPath = "$ODTPath\Office" - Remove-Item -Path $OfficeDownloadPath -Recurse -Force - Remove-Item -Path "$ODTPath\setup.exe" - } - -} - - - - -function Get-WimFromISO { - # Mount the ISO file using Mount-DiskImage cmdlet - $mountResult = Mount-DiskImage -ImagePath $isoPath -PassThru - - # Get the drive letter of the mounted ISO - $driveLetter = ($mountResult | Get-Volume).DriveLetter - - # Construct the path to the install.wim file - $wimPath = $driveLetter + ":\sources\install.wim" - - # Display the path to the install.wim file - Write-Host "The path to the install.wim file is: $wimPath" - - return $wimpath - -} - -function Get-WimIndex { - [Parameter(Mandatory = $true)] - [string]$WindowsSKU - - $wimindex = switch ($WindowsSKU) { - Home { 1 } - Home_N { 2 } - Home_SL { 3 } - EDU { 4 } - EDU_N { 5 } - Pro { 6 } - Pro_N { 7 } - Pro_EDU { 8 } - Pro_Edu_N { 9 } - Pro_WKS { 10 } - Pro_WKS_N { 11 } - Default { 6 } - } - Return $WimIndex -} - -#Build VHDX -#Create VHDX -function New-ScratchVhdx { - param( - [Parameter(Mandatory = $true)] - [string]$VhdxPath, - [uint64]$SizeBytes = 30GB, - [ValidateSet(512, 4096)] - [uint32]$LogicalSectorSizeBytes = 512, - [switch]$Dynamic, - [Microsoft.PowerShell.Cmdletization.GeneratedTypes.Disk.PartitionStyle]$PartitionStyle = [Microsoft.PowerShell.Cmdletization.GeneratedTypes.Disk.PartitionStyle]::GPT - ) - - Write-Host "Creating new Scratch VHDX..." - - $newVHDX = New-VHD -Path $VhdxPath -SizeBytes $disksize -LogicalSectorSizeBytes $LogicalSectorSizeBytes -Fixed:$true - $toReturn = $newVHDX | Mount-VHD -Passthru | Initialize-Disk -PassThru -PartitionStyle GPT - - #Remove auto-created system partition so we can create the correct partition layout - remove-partition $toreturn.DiskNumber -PartitionNumber 1 -Confirm:$False - - Write-Host "Done." - return $toReturn -} -#Add System Partition -function New-SystemPartition { - param( - [Parameter(Mandatory = $true)] - [ciminstance]$VhdxDisk, - [uint64]$SystemPartitionSize = 256MB - ) - - Write-Host "Creating System partition..." - - $sysPartition = $VhdxDisk | New-Partition -AssignDriveLetter -Size $SystemPartitionSize -GptType "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}" -IsHidden - $sysPartition | Format-Volume -FileSystem FAT32 -Force -NewFileSystemLabel "System" - - Write-Host "Done. System partition at drive $($sysPartition.DriveLetter):" - return $sysPartition.DriveLetter -} -#Add MSRPartition - skip this initially unless needed -function New-MSRPartition { - param( - [Parameter(Mandatory = $true)] - [ciminstance]$VhdxDisk - ) - - Write-Host "Creating MSR partition..." - - $toReturn = $VhdxDisk | New-Partition -AssignDriveLetter -Size 16MB -GptType "{e3c9e316-0b5c-4db8-817d-f92df00215ae}" -IsHidden - - Write-Host "Done." - - return $toReturn -} -#Add OS Partition -function New-OSPartition { - param( - [Parameter(Mandatory = $true)] - [ciminstance]$VhdxDisk, - [Parameter(Mandatory = $true)] - [string]$WimPath, - [uint32]$WimIndex, - [uint64]$OSPartitionSize = 0 - ) - - Write-Host "Creating OS partition..." - - if ($OSPartitionSize -gt 0) { - $osPartition = $vhdxDisk | New-Partition -AssignDriveLetter -Size $OSPartitionSize - } - else { - $osPartition = $vhdxDisk | New-Partition -AssignDriveLetter -UseMaximumSize - } - - $osPartition | Format-Volume -FileSystem NTFS -Confirm:$false -Force -NewFileSystemLabel "Windows" - Write-Host "Done. OS partition at drive $($osPartition.DriveLetter):" - - Write-Host "Writing WIM at $WimPath to OS partition at drive $($osPartition.DriveLetter):..." - - #Server 2019 is missing the Windows Overlay Filter (wof.sys), likely other Server SKUs are missing it as well. Script will error if trying to use the -compact switch on Server OSes - if ((Get-CimInstance Win32_OperatingSystem).Caption -match "Server") { - Write-Host (Expand-WindowsImage -ImagePath $WimPath -Index $WimIndex -ApplyPath "$($osPartition.DriveLetter):\") - } - else { - Write-Host (Expand-WindowsImage -ImagePath $WimPath -Index $WimIndex -ApplyPath "$($osPartition.DriveLetter):\" -Compact) - } - - Write-Host "Done." - - return $osPartition -} - -#Add Recovery partition -function New-RecoveryPartition { - param( - [Parameter(Mandatory = $true)] - [ciminstance]$VhdxDisk, - [Parameter(Mandatory = $true)] - $OsPartition, - [uint64]$RecoveryPartitionSize = 0, - [ciminstance]$DataPartition - ) - - Write-Host "Creating empty Recovery partition (to be filled on first boot automatically)..." - - $calculatedRecoverySize = 0 - $recoveryPartition = $null - - if ($RecoveryPartitionSize -gt 0) { - $calculatedRecoverySize = $RecoveryPartitionSize - } - else { - $winReWim = Get-ChildItem "$($OsPartition.DriveLetter):\Windows\System32\Recovery\Winre.wim" - - if (($null -ne $winReWim) -and ($winReWim.Count -eq 1)) { - # Wim size + 52MB is minimum WinRE partition size. - # NTFS and other partitioning size differences account for about 17MB of space that's unavailable. - # Adding 32MB as a buffer to ensure there's enough space. - $calculatedRecoverySize = $winReWim.Length + 52MB + 32MB - - Write-Host "Calculated space needed for recovery in bytes: $calculatedRecoverySize" - - if ($null -ne $DataPartition) { - $DataPartition | Resize-Partition -Size ($DataPartition.Size - $calculatedRecoverySize) - Write-Host "Data partition shrunk by $calculatedRecoverySize bytes for Recovery partition." - } - else { - $OsPartition | Resize-Partition -Size ($OsPartition.Size - $calculatedRecoverySize) - Write-Host "OS partition shrunk by $calculatedRecoverySize bytes for Recovery partition." - } - - $recoveryPartition = $VhdxDisk | New-Partition -AssignDriveLetter -UseMaximumSize -GptType "{de94bba4-06d1-4d40-a16a-bfd50179d6ac}" ` - | Format-Volume -FileSystem NTFS -Confirm:$false -Force -NewFileSystemLabel "WinRE" - - Write-Host "Done. Recovery partition at drive $($recoveryPartition.DriveLetter):" - } - else { - Write-Host "No WinRE.WIM found in the OS partition under \Windows\System32\Recovery." - Write-Host "Skipping creating the Recovery partition." - Write-Host "If a Recovery partition is desired, please re-run the script setting the -RecoveryPartitionSize flag as appropriate." - } - } - - return $recoveryPartition -} -#Add boot files -function Add-BootFiles { - param( - [Parameter(Mandatory = $true)] - [string]$OsPartitionDriveLetter, - [Parameter(Mandatory = $true)] - [string]$SystemPartitionDriveLetter, - [string]$FirmwareType = 'UEFI' - ) - - Write-Host "Adding boot files for `"$($OsPartitionDriveLetter):\Windows`" to System partition `"$($SystemPartitionDriveLetter):`"..." - - bcdboot "$($OsPartitionDriveLetter):\Windows" /S "$($SystemPartitionDriveLetter):" /F "$FirmwareType" - - Write-Host "Done." -} - -#Dismount VHDX -function Dismount-ScratchVhdx { - param( - [Parameter(Mandatory = $true)] - [string]$VhdxPath - ) - - if (Test-Path $VhdxPath) { - Write-Host "Dismounting scratch VHDX..." - Dismount-VHD -Path $VhdxPath - Write-Host "Done." - } -} - -#Delete old VMs and remove old certs -function Remove-FFUVM { - $certPath = 'Cert:\LocalMachine\Shielded VM Local Certificates\' - $OLDFFUVMs = get-vm _ffu-* | Where-Object { $_.state -ne 'running' } - - If ($null -ne $OLDFFUVMs) { - Foreach ($OLDFFUVM in $OLDFFUVMs) { - $OldVMName = $OLDFFUVM.VMName - Remove-VM -Name $OLDFFUVM.name -Force -ErrorAction SilentlyContinue - #Remove-Item -Path "C:\VM\$OldVMName" -Force -Recurse -ErrorAction SilentlyContinue - Remove-Item -Path "$VMLocation\$OLDVMName" -Force -Recurse -ErrorAction SilentlyContinue - Remove-HgsGuardian -Name $OldVMName - $certs = Get-ChildItem -Path $certPath -Recurse | Where-Object { $_.Subject -like "*$OldVMName*" } - foreach ($cert in $Certs) { - Remove-item -Path $cert.PSPath -force - } - } - } -} - -function New-FFUVM { - #Create new Gen2 VM - $VM = New-VM -Name $VMName -Path $VMPath -MemoryStartupBytes $memory -VHDPath $VHDXPath -Generation 2 - Set-VMProcessor -VMName $VMName -Count $processors - #Mount Office ISO - Add-VMDvdDrive -VMName $VMName -Path $AppsISO - $VMHardDiskDrive = Get-VMHarddiskdrive -VMName $VMName - Set-VMFirmware -VMName $VMName -FirstBootDevice $VMHardDiskDrive - Set-VM -Name $VMName -AutomaticCheckpointsEnabled $false -StaticMemory - - #Configure TPM - New-HgsGuardian -Name $VMName -GenerateCertificates - $owner = get-hgsguardian -Name $VMName - $kp = New-HgsKeyProtector -Owner $owner -AllowUntrustedRoot - Set-VMKeyProtector -VMName $VMName -KeyProtector $kp.RawData - Enable-VMTPM -VMName $VMName - - #Connect to VM - vmconnect $VM.ComputerName $VMName - - #Start VM - Start-VM -Name $VMName - return $VM -} -function New-PEMedia { - param ( - [Parameter()] - [switch] - $Capture, - [Parameter()] - [switch] - $Deploy - ) - #Need to use the Demployment and Imaging tools environment to create winPE media - $DandIEnv = "$adkPath`Assessment and Deployment Kit\Deployment Tools\DandISetEnv.bat" - $WinPEFFUPath = "$FFUDevelopmentPath\WinPE" - - If (Test-path -Path "$WinPEFFUPath") { - #Dismount-WindowsImage -Path "$WinPEFFUPath\mount" -discard - Remove-Item -Path "$WinPEFFUPath" -Recurse -Force - } - - & cmd /c """$DandIEnv"" && copype amd64 $WinPEFFUPath" - Mount-WindowsImage -ImagePath "$WinPEFFUPath\media\sources\boot.wim" -Index 1 -Path "$WinPEFFUPath\mount" - - $Packages = @( - "WinPE-WMI.cab", - "en-us\WinPE-WMI_en-us.cab", - "WinPE-NetFX.cab", - "en-us\WinPE-NetFX_en-us.cab", - "WinPE-Scripting.cab", - "en-us\WinPE-Scripting_en-us.cab", - "WinPE-PowerShell.cab", - "en-us\WinPE-PowerShell_en-us.cab", - "WinPE-StorageWMI.cab", - "en-us\WinPE-StorageWMI_en-us.cab", - "WinPE-DismCmdlets.cab", - "en-us\WinPE-DismCmdlets_en-us.cab" - ) - - $PackagePathBase = "$adkPath`Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\" - - foreach ($Package in $Packages) { - $PackagePath = Join-Path $PackagePathBase $Package - Add-WindowsPackage -Path "$WinPEFFUPath\mount" -PackagePath $PackagePath | Out-Null - } - If($Capture){ - Copy-Item -Path "$FFUDevelopmentPath\WinPECaptureFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force - #Remove Bootfix.bin if capturing from BIOS systems - Remove-Item -Path "$WinPEFFUPath\media\boot\bootfix.bin" -Force - } - If($Deploy){ - Copy-Item -Path "$FFUDevelopmentPath\WinPEDeployFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force - # If you need to add drivers (storage/keyboard most likely), remove the '#' from the below line and change the /Driver:Path to a folder of drivers - # & dism /image:$WinPEFFUPath\mount /Add-Driver /Driver: /Recurse - } - Dismount-WindowsImage -Path "$WinPEFFUPath\mount" -Save - #Make ISO - $OSCDIMGPath = "$adkPath`Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg" - $OSCDIMG = "$OSCDIMGPath\oscdimg.exe" - & "$OSCDIMG" -m -o -u2 -udfver102 -bootdata:2`#p0,e,b$OSCDIMGPath\etfsboot.com`#pEF,e,b$OSCDIMGPath\Efisys_noprompt.bin $WinPEFFUPath\media $FFUDevelopmentPath\WinPE_FFU_Capture.iso - Remove-Item -Path "$WinPEFFUPath" -Recurse -Force -} -function New-FFU { - #Need to use the Demployment and Imaging tools environment to use dism from the Insider ADK to optimize the FFU. This is only needed until Windows 23H2. - $DandIEnv = "$adkPath`Assessment and Deployment Kit\Deployment Tools\DandISetEnv.bat" - #Mount the Capture ISO to the VM - - $CaptureISOPath = "$FFUDevelopmentPath\WinPE_FFU_Capture.iso" - $FFUVMs = get-vm _ffu-* | Where-Object { $_.state -ne 'running' } - - If ($null -ne $FFUVMs) { - Foreach ($FFUVM in $FFUVMs) { - $VMName = $FFUVM.name - $VMDVDDrive = Get-VMDvdDrive -VMName $VMName - Set-VMFirmware -VMName $VMName -FirstBootDevice $VMDVDDrive - Set-VMDvdDrive -VMName $VMName -Path $CaptureISOPath - $VMSwitch = Get-VMSwitch -name $VMSwitchName - get-vm $VMName | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName $VMSwitch.Name - #vmconnect $FFUVM.ComputerName $VMName - } - } - #Start VM - Start-VM -Name $VMName - - # Wait for the VM to turn off - do { - $FFUVM = Get-VM -Name $VMName - Start-Sleep -Seconds 5 - } while ($FFUVM.State -ne 'Off') - - # Check for .ffu files in the FFUDevelopment folder - $FFUFiles = Get-ChildItem -Path $FFUDevelopmentPath -Filter "*.ffu" -File - - # If there's more than one .ffu file, get the most recent and store its path in $FFUFile - if ($FFUFiles.Count -gt 0) { - $FFUFile = ($FFUFiles | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1).FullName - Write-Host "Most recent .ffu file: $FFUFile" - } - else { - Write-Host "No .ffu files found in $FFUFolderPath" - } - #Add drivers - If ($InstallDrivers){ - New-Item -Path "$FFUDevelopmentPath\Mount" -ItemType Directory -Force - Mount-WindowsImage -ImagePath $FFUFile -Index 1 -Path "$FFUDevelopmentPath\Mount" - Add-WindowsDriver -Path "$FFUDevelopmentPath\Mount" -Driver "$FFUDevelopmentPath\Drivers" -Recurse - Dismount-WindowsImage -Path "$FFUDevelopmentPath\Mount" -Save - Remove-Item -Path "$FFUDevelopmentPath\Mount" -Recurse -Force - } - #Optimize FFU - & cmd /c """$DandIEnv"" && dism /optimize-ffu /imagefile:$FFUFile" -} - - - - -try { - #Check if the Windows ADK is installed - $adkPath = Get-ADK -} -catch { - throw $_ -} - -#Build ISO for Office and Apps -try{ - if($InstallOffice){ - Get-Office - } - if($InstallApps){ - New-AppsISO - } -} -catch { - Write-Host "Getting Office and/or building the AppsISO failed" - throw $_ -} - -#Create VHDX -try { - $wimPath = Get-WimFromISO - - $WimIndex = Get-WimIndex - - $vhdxDisk = New-ScratchVhdx -VhdxPath $VHDXPath -SizeBytes $disksize -Dynamic:$true - - $systemPartitionDriveLetter = New-SystemPartition -VhdxDisk $vhdxDisk - - New-MSRPartition -VhdxDisk $vhdxDisk - - $osPartition = New-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $WimIndex[1] - $osPartitionDriveLetter = $osPartition[1].DriveLetter - - $recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition - - Write-Host "All necessary partitions created." - - Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1] - New-Item -Path "$($osPartitionDriveLetter):\Windows\Panther\unattend" -ItemType Directory - Copy-Item -Path "$FFUDevelopmentPath\BuildFFUUnattend\unattend.xml" -Destination "$($osPartitionDriveLetter):\Windows\Panther\Unattend\Unattend.xml" -Force -} -finally { - Dismount-ScratchVhdx -VhdxPath $VHDXPath - Dismount-DiskImage -ImagePath $ISOPath -} - -#Clean up old VMs -try { - Remove-FFUVM -} -catch { - Write-Host "VM cleanup failed" - throw $_ -} - -#Create VM and attach VHDX -try { - $FFUVM = New-FFUVM -} -catch { - Write-Host 'VM creation failed' - throw $_ -} -#Create Capture Media -try{ - #This should happen while the FFUVM is building - New-PEMedia -Capture -} -catch{ - throw $_ -} -#Capture FFU file -try { - #Check if VM is done provisioning - do { - $FFUVM = Get-VM -Name $FFUVM.Name - Start-Sleep -Seconds 10 - } while ($FFUVM.State -ne 'Off') - - #Capture FFU file - New-FFU -} -Catch { - throw $_ -} \ No newline at end of file diff --git a/archive/FFUDevelopment/BuildWinPECaptureMedia.cmd b/archive/FFUDevelopment/BuildWinPECaptureMedia.cmd deleted file mode 100644 index ebb0651..0000000 --- a/archive/FFUDevelopment/BuildWinPECaptureMedia.cmd +++ /dev/null @@ -1,21 +0,0 @@ -rd c:\FFUDevelopment\WinPE /S /Q -cmd /c copype amd64 c:\FFUDevelopment\WinPE -Dism /Mount-Image /ImageFile:"c:\FFUDevelopment\WinPE\media\sources\boot.wim" /Index:1 /MountDir:"c:\FFUDevelopment\WinPE\mount" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-WMI.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-WMI_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-NetFX.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-NetFX_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-Scripting.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-Scripting_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-PowerShell.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-PowerShell_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-StorageWMI.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-StorageWMI_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-DismCmdlets.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-DismCmdlets_en-us.cab" -xcopy "C:\FFUDevelopment\WinPECaptureFFUFiles" c:\FFUDevelopment\WinPE\mount /Y /E -REM If you need to add drivers, remove the REM from the below line and change the /Driver:Path to a folder of drivers -REM dism /image:C:\FFUDevelopment\WinPE\mount /Add-Driver /Driver: /Recurse -Dism /Unmount-Image /MountDir:c:\FFUDevelopment\WinPE\mount /Commit -MakeWinPEMedia /ISO /F c:\FFUDevelopment\WinPE "c:\FFUDevelopment\WinPE_FFU_Capture.iso" - diff --git a/archive/FFUDevelopment/BuildWinPEDeploymentMedia.cmd b/archive/FFUDevelopment/BuildWinPEDeploymentMedia.cmd deleted file mode 100644 index 3054081..0000000 --- a/archive/FFUDevelopment/BuildWinPEDeploymentMedia.cmd +++ /dev/null @@ -1,20 +0,0 @@ -rd c:\FFUDevelopment\WinPE /S /Q -cmd /c copype amd64 c:\FFUDevelopment\WinPE -Dism /Mount-Image /ImageFile:"c:\FFUDevelopment\WinPE\media\sources\boot.wim" /Index:1 /MountDir:"c:\FFUDevelopment\WinPE\mount" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-WMI.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-WMI_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-NetFX.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-NetFX_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-Scripting.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-Scripting_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-PowerShell.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-PowerShell_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-StorageWMI.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-StorageWMI_en-us.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-DismCmdlets.cab" -Dism /Add-Package /Image:"c:\FFUDevelopment\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-DismCmdlets_en-us.cab" -xcopy "C:\FFUDevelopment\WinPEDeployFFUFiles" c:\FFUDevelopment\WinPE\mount /Y /E -REM If you need to add drivers, remove the REM from the below line and change the /Driver:Path to a folder of drivers -REM dism /image:C:\FFUDevelopment\WinPE\mount /Add-Driver /Driver: /Recurse -Dism /Unmount-Image /MountDir:c:\FFUDevelopment\WinPE\mount /Commit -MakeWinPEMedia /ISO /F c:\FFUDevelopment\WinPE "c:\FFUDevelopment\WinPE_FFU_Deploy.iso" \ No newline at end of file diff --git a/archive/FFUDevelopment/CaptureFFU.ps1 b/archive/FFUDevelopment/CaptureFFU.ps1 deleted file mode 100644 index bd539b9..0000000 --- a/archive/FFUDevelopment/CaptureFFU.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -#Modify variables -$ISOPath = 'C:\ffu\WinPE_FFU_Capture.iso' - -$vms = get-vm _ffu* | ? {$_.state -ne 'running'} - - -If($null -ne $vms){ - Foreach ($vm in $vms){ - $VMName = $vm.name - $VMDVDDrive = Get-VMDvdDrive -VMName $VMName - Set-VMFirmware -VMName $VMName -FirstBootDevice $VMDVDDrive - Set-VMDvdDrive -VMName $VMName -Path $ISOPath - $VMSwitch = Get-VMSwitch -name *intel* - get-vm $VMName | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName $VMSwitch.Name - } -} - - - diff --git a/archive/FFUDevelopment/CreateOfficeISO.ps1 b/archive/FFUDevelopment/CreateOfficeISO.ps1 deleted file mode 100644 index e5829a9..0000000 --- a/archive/FFUDevelopment/CreateOfficeISO.ps1 +++ /dev/null @@ -1,51 +0,0 @@ -function Get-ODTURL { - - [String]$MSWebPage = Invoke-RestMethod 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=49117' - - $MSWebPage | ForEach-Object { - if ($_ -match 'url=(https://.*officedeploymenttool.*\.exe)') { - $matches[1] - } - } -} - -$FFUDevelopmentPath = 'C:\FFUDevelopment' -$ODTUrl = Get-ODTURL -$ODTInstallFile = "$env:TEMP\odtsetup.exe" -Invoke-WebRequest -Uri $ODTUrl -OutFile $ODTInstallFile - -# Extract Office Deployment Tool -$ODTPath = "$FFUDevelopmentPath\Apps\Office" -Start-Process -FilePath $ODTInstallFile -ArgumentList "/extract:$ODTPath /quiet" -Wait - -# Run setup.exe with config.xml -$ConfigXml = "$FFUDevelopmentPath\Apps\Office\DownloadFFU.xml" -#Set-Location $ODTPath -Start-Process -FilePath "$FFUDevelopmentPath\Apps\Office\setup.exe" -ArgumentList "/download $ConfigXml" -Wait - -#Make Office ISO -Remove-Item -Path "$ODTPath\configuration*" -Force -$OSCDIMG = 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe' -$AppsISO = "$FFUDevelopmentPath\Apps.iso" -Start-Process -FilePath $OSCDIMG -ArgumentList "-n -m -d $ODTPath $AppsISO" -wait - -#Mount Office ISO to FFU VM -$VMS = get-vm _ffu-* | Where-Object {$_.state -eq 'running'} - -foreach ($VM in $VMs) { - # Check if DVD drive exists - $DVD = Get-VMDvdDrive -VMName $VM.Name - if ($DVD) { - # Attach ISO to existing DVD drive - Set-VMDvdDrive -VMName $VM.Name -Path $AppsISO - } - else { - # Add DVD drive and attach ISO - Add-VMDvdDrive -VMName $VM.Name -Path $AppsISO - } -} - -#Remove the Office Download and ODT -$OfficeDownloadPath = "$FFUDevelopmentPath\Apps\Office\Office" -Remove-Item -Path $OfficeDownloadPath -Recurse -Force -Remove-Item -Path "$ODTPath\setup.exe" \ No newline at end of file diff --git a/archive/FFUDevelopment/Docs/BuildDeployFFU.docx b/archive/FFUDevelopment/Docs/BuildDeployFFU.docx deleted file mode 100644 index f951565..0000000 Binary files a/archive/FFUDevelopment/Docs/BuildDeployFFU.docx and /dev/null differ diff --git a/archive/FFUDevelopment/Docs/BuildDeployFFUv2.docx b/archive/FFUDevelopment/Docs/BuildDeployFFUv2.docx deleted file mode 100644 index 8b6155e..0000000 Binary files a/archive/FFUDevelopment/Docs/BuildDeployFFUv2.docx and /dev/null differ diff --git a/archive/FFUDevelopment/Docs/ConvertWimToFFU.docx b/archive/FFUDevelopment/Docs/ConvertWimToFFU.docx deleted file mode 100644 index d19c9e4..0000000 Binary files a/archive/FFUDevelopment/Docs/ConvertWimToFFU.docx and /dev/null differ diff --git a/archive/FFUDevelopment/Docs/~$ildDeployFFU.docx b/archive/FFUDevelopment/Docs/~$ildDeployFFU.docx deleted file mode 100644 index 97a147c..0000000 Binary files a/archive/FFUDevelopment/Docs/~$ildDeployFFU.docx and /dev/null differ diff --git a/archive/FFUDevelopment/Docs/~$ildDeployFFUv2.docx b/archive/FFUDevelopment/Docs/~$ildDeployFFUv2.docx deleted file mode 100644 index a91bc36..0000000 Binary files a/archive/FFUDevelopment/Docs/~$ildDeployFFUv2.docx and /dev/null differ diff --git a/archive/FFUDevelopment/ModifyVMForCapture.ps1 b/archive/FFUDevelopment/ModifyVMForCapture.ps1 deleted file mode 100644 index 8474168..0000000 --- a/archive/FFUDevelopment/ModifyVMForCapture.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -#Modify variables -$ISOPath = 'C:\FFUDevelopment\WinPE_FFU_Capture.iso' -$VMSwitchName = '*intel*' - -$vms = get-vm _ffu-* | ? {$_.state -ne 'running'} - - -If($null -ne $vms){ - Foreach ($vm in $vms){ - $VMName = $vm.name - $VMDVDDrive = Get-VMDvdDrive -VMName $VMName - Set-VMFirmware -VMName $VMName -FirstBootDevice $VMDVDDrive - Set-VMDvdDrive -VMName $VMName -Path $ISOPath - $VMSwitch = Get-VMSwitch -name $VMSwitchName - get-vm $VMName | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName $VMSwitch.Name - vmconnect $vm.ComputerName $VMName - } -} - - - diff --git a/archive/FFUDevelopment/WinPECaptureFFUFiles/AssignDriveLetter.txt b/archive/FFUDevelopment/WinPECaptureFFUFiles/AssignDriveLetter.txt deleted file mode 100644 index a18948f..0000000 --- a/archive/FFUDevelopment/WinPECaptureFFUFiles/AssignDriveLetter.txt +++ /dev/null @@ -1,4 +0,0 @@ -select disk 0 -select partition 3 -Assign letter="M" -exit diff --git a/archive/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 b/archive/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 deleted file mode 100644 index 28145ec..0000000 --- a/archive/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -#Modify the net use path to map the W: drive to the location you want to copy the FFU file to -net use W: \\192.168.1.2\c$\FFUDevelopment /user:administrator p@ssw0rd - -$AssignDriveLetter = 'x:\AssignDriveLetter.txt' -Start-Process -FilePath diskpart.exe -ArgumentList "/S $AssignDriveLetter" -Wait -ErrorAction Stop | Out-Null -#Load Registry Hive -$Software = 'M:\Windows\System32\config\software' -reg load "HKLM\FFU" $Software - -#Find Windows version values - -$SKU = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'EditionID' -[int]$CurrentBuild = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'CurrentBuild' -$DisplayVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion' -$BuildDate = Get-Date -uformat %b%Y - -$SKU = switch ($SKU){ - Home {'Home'} - Professional {'Pro'} - ProfessionalEducation {'Pro_Edu'} - Enterprise {'Ent'} - Education {'Edu'} -} - -if($CurrentBuild -ge 22000){ - $Name = 'Win11' -} -else{ - $Name = 'Win10' -} - -#If Office is installed, modify the file name of the FFU -#$Office = Get-childitem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue | Out-Null -$Office = Get-childitem -Path 'M:\Program Files\Microsoft Office' -if($Office){ - $ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_Office`_$BuildDate.ffu" - $dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default" - - -} -else{ - $ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_$BuildDate.ffu" - $dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default" - -} - -#Unload Registry -Set-Location X:\ -Remove-Variable SKU -Remove-Variable CurrentBuild -Remove-Variable DisplayVersion -Remove-Variable Office -reg unload "HKLM\FFU" - -Start-Process -FilePath dism.exe -ArgumentList $dismArgs -Wait -PassThru -ErrorAction Stop | Out-Null -#Copy DISM log to Host -xcopy X:\Windows\logs\dism\dism.log W:\ /Y | Out-Null -#Remvove W: drive -net use W: /delete -wpeutil Shutdown diff --git a/archive/FFUDevelopment/WinPECaptureFFUFiles/Windows/System32/startnet.cmd b/archive/FFUDevelopment/WinPECaptureFFUFiles/Windows/System32/startnet.cmd deleted file mode 100644 index ed54781..0000000 --- a/archive/FFUDevelopment/WinPECaptureFFUFiles/Windows/System32/startnet.cmd +++ /dev/null @@ -1,5 +0,0 @@ -wpeinit -powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c -powershell -Noprofile -ExecutionPolicy Bypass -File x:\CaptureFFU.ps1 -exit - diff --git a/archive/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 b/archive/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 deleted file mode 100644 index 6d57e45..0000000 --- a/archive/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 +++ /dev/null @@ -1,582 +0,0 @@ -function Get-USBDrive(){ - $USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Removable' -and $_.FileSystemType -eq 'NTFS'}).DriveLetter - if ($null -eq $USBDriveLetter){ - #Must be using a fixed USB drive - difficult to grab drive letter from win32_diskdrive. Assume user followed instructions and used Deploy as the friendly name for partition - $USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Fixed' -and $_.FileSystemType -eq 'NTFS' -and $_.FileSystemLabel -eq 'Deploy'}).DriveLetter - #If we didn't get the drive letter, stop the script. - if ($null -eq $USBDriveLetter){ - WriteLog 'Cannot find USB drive letter - most likely using a fixed USB drive. Name the 2nd partition with the FFU files as Deploy so the script can grab the drive letter. Exiting' - Exit - } - - } - $USBDriveLetter = $USBDriveLetter + ":\" - return $USBDriveLetter -} - -function Get-HardDrive(){ - $DeviceID = (Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' -and $_.Model -ne 'Microsoft Virtual Disk'}).DeviceID - return $DeviceID -} - -function WriteLog($LogText){ - Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText" -} - -function Set-DiskpartAnswerFiles($DiskpartFile,$DiskID){ - (Get-Content $DiskpartFile).Replace('disk 0', "disk $DiskID") | Set-Content -Path $DiskpartFile -} - -function Set-Computername($computername){ - [xml]$xml = Get-Content $UnattendFile - if($xml.unattend.settings.component.Count -ge 2){ - #Assumes that Computername is the first component element - $xml.unattend.settings.component[0].ComputerName = $computername - }else{ - $xml.unattend.settings.component.ComputerName = $computername - } - $xml.Save($UnattendFile) - return $computername -} - -function Invoke-Process { - [CmdletBinding(SupportsShouldProcess)] - param - ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$FilePath, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$ArgumentList - ) - - $ErrorActionPreference = 'Stop' - - try { - $stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)" - $stdErrTempFile = "$env:TEMP\$((New-Guid).Guid)" - - $startProcessParams = @{ - FilePath = $FilePath - ArgumentList = $ArgumentList - RedirectStandardError = $stdErrTempFile - RedirectStandardOutput = $stdOutTempFile - Wait = $true; - PassThru = $true; - NoNewWindow = $false; - } - if ($PSCmdlet.ShouldProcess("Process [$($FilePath)]", "Run with args: [$($ArgumentList)]")) { - $cmd = Start-Process @startProcessParams - $cmdOutput = Get-Content -Path $stdOutTempFile -Raw - $cmdError = Get-Content -Path $stdErrTempFile -Raw - if ($cmd.ExitCode -ne 0) { - if ($cmdError) { - throw $cmdError.Trim() - } - if ($cmdOutput) { - throw $cmdOutput.Trim() - } - } else { - if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) { - WriteLog $cmdOutput - } - } - } - } catch { - #$PSCmdlet.ThrowTerminatingError($_) - WriteLog $_ - Write-Host 'Script failed - check scriptlog.txt on the USB drive for more info' - throw $_ - - } finally { - Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore - - } - -} - -# This function can be used in instances where battery level might matter (e.g. installing firmware for Surface). The problem is that WinPE doesn't have -# a driver for the battery installed, so you'll need to inject drivers, which can be tricky because just injecting the battery driver might not be enough, -# you might also need other drivers that the battery driver is dependent on. -# function Get-Battery(){ -# while (($BattLev = (Get-CimInstance win32_battery).EstimatedChargeRemaining) -lt "35") -# { -# WriteLog "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..." -# Write-Host "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..." -# Start-Sleep 60 -# } - -# WriteLog "Battery level is $BattLev `%, which is greater than 35'% applying FFU" -# Write-Host "Battery level is $BattLev `%, which is greater than 35'% applying FFU" -# } - -#Get USB Drive and create log file -$LogFileName = 'ScriptLog.txt' -$USBDrive = Get-USBDrive -New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null -$LogFile = $USBDrive + $LogFilename -WriteLog 'Begin Logging' - -#Find PhysicalDrive -$PhysicalDeviceID = Get-HardDrive -WriteLog "Physical DeviceID is $PhysicalDeviceID" - -#Parse DiskID Number -$DiskID = $PhysicalDeviceID.substring($PhysicalDeviceID.length - 1,1) -WriteLog "DiskID is $DiskID" - -#Modify diskpart answer files if DiskID not 0 -# $UEFIFFUPartitions = 'x:\CreateUEFI-FFU-Partitions.txt' -# $ExtendPartition = 'x:\ExtendPartition-UEFI.txt' - -# If ($DiskID -ne '0'){ -# WriteLog 'DiskID is not 0. Need to modify diskpart answer files' -# try { -# Set-DiskpartAnswerFiles $UEFIFFUPartitions $DiskID -# } -# catch { -# WriteLog "Modifying $UEFIFFUPartitions failed with error: $_" -# } - -# try { -# Set-DiskpartAnswerFiles $ExtendPartition $DiskID -# } -# catch { -# WriteLog "Modifying $ExtendPartition failed with error: $_" -# } -# } - -#Find FFU Files -[array]$FFUFiles = @(Get-ChildItem -Path $USBDrive*.ffu) -$FFUCount = $FFUFiles.Count - -#If multiple FFUs found, ask which to install -If ($FFUCount -gt 1) { - WriteLog "Found $FFUCount FFU Files" - $array = @() - - for($i=0;$i -le $FFUCount -1;$i++){ - $Properties = [ordered]@{Number = $i + 1 ; FFUFile = $FFUFiles[$i].FullName} - $array += New-Object PSObject -Property $Properties - } - $array | Format-Table -AutoSize -Property Number, FFUFile - do { - try { - $var = $true - [int]$FFUSelected = Read-Host 'Enter the FFU number to install' - $FFUSelected = $FFUSelected -1 - } - - catch { - Write-Host 'Input was not in correct format. Please enter a valid FFU number' - $var = $false - } - } until (($FFUSelected -le $FFUCount -1) -and $var) - - $FFUFileToInstall = $array[$FFUSelected].FFUFile - WriteLog "$FFUFileToInstall was selected" -} -elseif ($FFUCount -eq 1) { - WriteLog "Found $FFUCount FFU File" - $FFUFileToInstall = $FFUFiles[0].FullName - WriteLog "$FFUFileToInstall will be installed" -} -else { - Writelog 'No FFU files found' - Write-Host 'No FFU files found' - Exit -} - -#FindAP -$APFolder = $USBDrive + "Autopilot\" -If (Test-Path -Path $APFolder){ - [array]$APFiles = @(Get-ChildItem -Path $APFolder*.json) - $APFilesCount = $APFiles.Count - if ($APFilesCount -ge 1){ - $autopilot = $true - } -} - - -#FindPPKG -$PPKGFolder = $USBDrive + "PPKG\" -if (Test-Path -Path $PPKGFolder){ - [array]$PPKGFiles = @(Get-ChildItem -Path $PPKGFolder*.ppkg) - $PPKGFilesCount = $PPKGFiles.Count - if ($PPKGFilesCount -ge 1){ - $PPKG = $true - } -} - -#FindUnattend -$UnattendFolder = $USBDrive + "unattend\" -$UnattendFilePath = $UnattendFolder + "unattend.xml" -$UnattendPrefixPath = $UnattendFolder + "prefixes.txt" -If (Test-Path -Path $UnattendFilePath){ - $UnattendFile = Get-ChildItem -Path $UnattendFilePath - If ($UnattendFile){ - $Unattend = $true - } -} -If (Test-Path -Path $UnattendPrefixPath){ - $UnattendPrefixFile = Get-ChildItem -Path $UnattendPrefixPath - If ($UnattendPrefixFile){ - $UnattendPrefix = $true - } -} - -#Ask for device name if unattend exists -if ($Unattend -and $UnattendPrefix){ - Writelog 'Unattend file found with prefixes.txt. Getting prefixes.' - $UnattendPrefixes = @(Get-content $UnattendPrefixFile) - $UnattendPrefixCount = $UnattendPrefixes.Count - If ($UnattendPrefixCount -gt 1) { - WriteLog "Found $UnattendPrefixCount Prefixes" - $array = @() - for($i=0;$i -le $UnattendPrefixCount -1;$i++){ - $Properties = [ordered]@{Number = $i + 1 ; DeviceNamePrefix = $UnattendPrefixes[$i]} - $array += New-Object PSObject -Property $Properties - } - $array | Format-Table -AutoSize -Property Number, DeviceNamePrefix - do { - try { - $var = $true - [int]$PrefixSelected = Read-Host 'Enter the prefix number to use for the device name' - $PrefixSelected = $PrefixSelected -1 - } - catch { - Write-Host 'Input was not in correct format. Please enter a valid prefix number' - $var = $false - } - } until (($PrefixSelected -le $UnattendPrefixCount -1) -and $var) - $PrefixToUse = $array[$PrefixSelected].DeviceNamePrefix - WriteLog "$PrefixToUse was selected" - } - elseif ($UnattendPrefixCount -eq 1) { - WriteLog "Found $UnattendPrefixCount Prefix" - $PrefixToUse = $UnattendPrefixes[0] - WriteLog "Will use $PrefixToUse as device name prefix" - } - #Get serial number to append. This can make names longer than 15 characters. Trim any leading or trailing whitespace - $serial = (Get-CimInstance -ClassName win32_bios).SerialNumber.Trim() - #Combine prefix with serial - $computername = $PrefixToUse + $serial - #If computername is longer than 15 characters, reduce to 15. Sysprep/unattend doesn't like ComputerName being longer than 15 characters even though Windows accepts it - If ($computername.Length -gt 15){ - $computername = $computername.substring(0,15) - } - $computername = Set-Computername($computername) - Writelog "Computer name set to $computername" -} -elseif($Unattend){ - Writelog 'Unattend file found with no prefixes.txt, asking for name' - [string]$computername = Read-Host 'Enter device name' - Set-Computername($computername) - Writelog "Computer name set to $computername" -} -else { - WriteLog 'No unattend folder found. Device name will be set via PPKG, AP JSON, or default OS name.' -} - -#If both AP and PPKG folder found with files, ask which to use. -If($autopilot -eq $true -and $PPKG -eq $true){ - WriteLog 'Both PPKG and Autopilot json files found' - Write-Host 'Both Autopilot JSON files and Provisioning packages were found.' - do { - try { - $var = $true - [int]$APorPPKG = Read-Host 'Enter 1 for Autopilot or 2 for Provisioning Package' - } - - catch { - Write-Host 'Incorrect value. Please enter 1 for Autopilot or 2 for Provisioning Package' - $var = $false - } - } until (($APorPPKG -gt 0 -and $APorPPKG -lt 3) -and $var) - If ($APorPPKG -eq 1){ - $PPKG = $false - } - else{ - $autopilot = $false - } -} - -#If multiple AP json files found, ask which to install -If ($APFilesCount -gt 1 -and $autopilot -eq $true) { - WriteLog "Found $APFilesCount Autopilot json Files" - $array = @() - - for($i=0;$i -le $APFilesCount -1;$i++){ - $Properties = [ordered]@{Number = $i + 1 ; APFile = $APFiles[$i].FullName; APFileName = $APFiles[$i].Name} - $array += New-Object PSObject -Property $Properties - } - $array | Format-Table -AutoSize -Property Number, APFileName - do { - try { - $var = $true - [int]$APFileSelected = Read-Host 'Enter the AP json file number to install' - $APFileSelected = $APFileSelected - 1 - } - - catch { - Write-Host 'Input was not in correct format. Please enter a valid AP json file number' - $var = $false - } - } until (($APFileSelected -le $APFilesCount -1) -and $var) - - $APFileToInstall = $array[$APFileSelected].APFile - $APFileName = $array[$APFileSelected].APFileName - WriteLog "$APFileToInstall was selected" -} -elseif ($APFilesCount -eq 1 -and $autopilot -eq $true) { - WriteLog "Found $APFilesCount AP File" - $APFileToInstall = $APFiles[0].FullName - $APFileName = $APFiles[0].Name - WriteLog "$APFileToInstall will be copied" -} -else { - Writelog 'No AP files found or AP was not selected' -} - -#If multiple PPKG files found, ask which to install -If ($PPKGFilesCount -gt 1 -and $PPKG -eq $true) { - WriteLog "Found $PPKGFilesCount PPKG Files" - $array = @() - - for($i=0;$i -le $PPKGFilesCount -1;$i++){ - $Properties = [ordered]@{Number = $i + 1 ; PPKGFile = $PPKGFiles[$i].FullName; PPKGFileName = $PPKGFiles[$i].Name} - $array += New-Object PSObject -Property $Properties - } - $array | Format-Table -AutoSize -Property Number, PPKGFileName - do { - try { - $var = $true - [int]$PPKGFileSelected = Read-Host 'Enter the PPKG file number to install' - $PPKGFileSelected = $PPKGFileSelected - 1 - } - - catch { - Write-Host 'Input was not in correct format. Please enter a valid PPKG file number' - $var = $false - } - } until (($PPKGFileSelected -le $PPKGFilesCount -1) -and $var) - - $PPKGFileToInstall = $array[$PPKGFileSelected].PPKGFile - WriteLog "$PPKGFileToInstall was selected" -} -elseif ($PPKGFilesCount -eq 1 -and $PPKG -eq $true) { - WriteLog "Found $PPKGFilesCount PPKG File" - $PPKGFileToInstall = $PPKGFiles[0].FullName - WriteLog "$PPKGFileToInstall will be used" -} -else { - Writelog 'No PPKG files found or PPKG not selected.' -} - -#Find Drivers -$Drivers = $USBDrive + "Drivers" -If (Test-Path -Path $Drivers) -{ - #Check if multiple driver folders found, if so, just select one folder to save time/space - $DriverFolders = Get-ChildItem -Path $Drivers - $DriverFoldersCount = $DriverFolders.count - If ($DriverFoldersCount -gt 1) - { - WriteLog "Found $DriverFoldersCount driver folders" - $array = @() - - for($i=0; $i -le $DriverFoldersCount -1; $i++){ - $Properties = [ordered]@{Number = $i + 1; Drivers = $DriverFolders[$i].FullName} - $array += New-Object PSObject -Property $Properties - } - $array | Format-Table -AutoSize -Property Number, Drivers - do { - try { - $var = $true - [int]$DriversSelected = Read-Host 'Enter the set of drivers to install' - $DriversSelected = $DriversSelected - 1 - } - - catch { - Write-Host 'Input was not in correct format. Please enter a valid driver folder number' - $var = $false - } - } until (($DriversSelected -le $DriverFoldersCount -1) -and $var) - - $Drivers = $array[$DriversSelected].Drivers - WriteLog "$Drivers was selected" - } - elseif ($DriverFoldersCount -eq 1) { - WriteLog "Found $DriverFoldersCount driver folder" - $Drivers = $DriverFolders.FullName - WriteLog "$Drivers will be installed" - } - else { - Writelog 'No driver folders found' - } -} - -#If you want to enable battery level checking, uncomment the line below as well as the Get-Battery function near the top of the script -#Get-Battery - -#Partition drive -Writelog 'Clean Disk' -#Start-Process -FilePath diskpart.exe -ArgumentList "/S $UEFIFFUPartitions" -Wait -ErrorAction Stop | Out-File $Logfile -Append -#Invoke-Process diskpart.exe "/S $UEFIFFUPartitions" -try { - $Disk = Get-Disk -Number $DiskID - $Disk | clear-disk -RemoveData -RemoveOEM -Confirm:$false -} -catch { - WriteLog 'Cleaning disk failed. Exiting' - throw $_ -} - -Writelog 'Cleaning Disk succeeded' - -#Apply FFU -WriteLog "Applying FFU to $PhysicalDeviceID" -WriteLog "Running command dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID" -#In order for Applying Image progress bar to show up, need to call dism directly. Might be a better way to handle, but must have progress bar show up on screen. -dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID -if($LASTEXITCODE -eq 0){ - WriteLog 'Successfully applied FFU' -} -else{ - Writelog "Failed to apply FFU - LastExitCode = $LASTEXITCODE also check dism.log on the USB drive for more info" - #Copy DISM log to USBDrive - invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y" - exit -} - -#Remove recovery partition - this is needed in order to extend the Windows partition so it uses the full disk size. If dism /optimize-ffu worked, this wouldn't be needed -# $disk = get-disk -Number $DiskID -# $RecoveryPartition = $disk | get-partition | Where-Object {$_.type -eq 'Recovery'} -# if ($RecoveryPartition){ -# $RecoveryPartitionNumber = $RecoveryPartition.PartitionNumber -# if ($RecoveryPartitionNumber -eq 4){ -# try { -# WriteLog 'Removing recovery partition' -# Remove-partition -DiskNumber $DiskID -PartitionNumber $RecoveryPartitionNumber -Confirm:$false -# } -# catch { -# WriteLog 'Error removing recovery partition, exiting' -# throw $_ -# } -# } -# else{ -# WriteLog 'Recovery partition not partition 4. Script will exit. Please create the FFU with the recovery partition as the last partition. This is the default and recommended way.' -# exit -# } -# } - -#Extend Windows partition and create recovery partition -# Writelog 'Extending Windows partition' -# Invoke-Process diskpart.exe "/S $ExtendPartition" -# if($LASTEXITCODE -eq 0){ -# WriteLog 'Successfully extended Windows partition and created recovery partition' -# } -# else{ -# Writelog "Failed to extend Windows partition and/or create recovery partition - LastExitCode = $LASTEXITCODE" -# } - -#Set W: drive letter to Windows partition -Get-Disk | Where-Object Number -eq $DiskID | Get-Partition | Where-Object PartitionNumber -eq 3 | Set-Partition -NewDriveLetter W - -#Copy modified WinRE if folder exists, else copy inbox WinRE -$WinRE = $USBDrive + "WinRE\winre.wim" -If (Test-Path -Path $WinRE) -{ - WriteLog 'Copying modified WinRE to Recovery directory' - Invoke-Process xcopy.exe "/h $WinRE R:\Recovery\WindowsRE\ /Y" - WriteLog 'Copying WinRE to Recovery directory succeeded' - WriteLog 'Registering location of recovery tools' - Invoke-Process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows" - WriteLog 'Registering location of recovery tools succeeded' -} -# else -# { -# WriteLog 'Copying default WinRE to Recovery directory' -# Invoke-Process xcopy.exe "/h W:\Windows\System32\Recovery\Winre.wim R:\Recovery\WindowsRE\ /Y" -# WriteLog 'Copying WinRE to Recovery directory succeeded' -# WriteLog 'Registering location of recovery tools' -# Invoke-process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows" -# WriteLog 'Registering location of recovery tools succeeded' -# } - -#Autopilot JSON -If ($APFileToInstall){ - WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot" - Invoke-process xcopy.exe "$APFileToInstall W:\Windows\provisioning\autopilot\" - WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot succeeded" - # Rename file in W:\Windows\Provisioning\Autopilot to AutoPilotConfigurationFile.json - try { - Rename-Item -Path "W:\Windows\Provisioning\Autopilot\$APFileName" -NewName 'W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json' - WriteLog "Renamed W:\Windows\Provisioning\Autopilot\$APFilename to W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json" - } - - catch{ - Writelog "Copying $APFileToInstall to W:\windows\provisioning\autopilot failed with error: $_" - throw $_ - } -} -#Apply PPKG -If ($PPKGFileToInstall){ - try { - #Make sure to delete any existing PPKG on the USB drive - Get-Childitem -Path $USBDrive\*.ppkg | ForEach-Object { - Remove-item -Path $_.FullName - } - WriteLog "Copying $PPKGFileToInstall to $USBDrive" - Invoke-process xcopy.exe "$PPKGFileToInstall $USBDrive" - WriteLog "Copying $PPKGFileToInstall to $USBDrive succeeded" - } - - catch{ - Writelog "Copying $PPKGFileToInstall to $USBDrive failed with error: $_" - throw $_ - } -} -#Set DeviceName -If ($PrefixToUse){ - try{ - $PantherDir = 'w:\windows\panther' - If (Test-Path -Path $PantherDir){ - Writelog "Copying $UnattendFile to $PantherDir" - Invoke-process xcopy "$UnattendFile $PantherDir /Y" - WriteLog "Copying $UnattendFile to $PantherDir succeeded" - } - else{ - Writelog "$PantherDir doesn't exist, creating it" - New-Item -Path $PantherDir -ItemType Directory -Force - Writelog "Copying $UnattendFile to $PantherDir" - Invoke-Process xcopy.exe "$UnattendFile $PantherDir" - WriteLog "Copying $UnattendFile to $PantherDir succeeded" - } - } - catch{ - WriteLog "Copying Unattend.xml to name device failed" - throw $_ - } -} - -#Add Drivers -#Some drivers can sometimes fail to copy and dism ends up with a non-zero error code. Invoke-process will throw and terminate in these instances. -If (Test-Path -Path $Drivers) -{ - WriteLog 'Copying drivers' - Write-Warning 'Copying Drivers - dism will pop a window with no progress. This can take a few minutes to complete. This is done so drivers are logged to the scriptlog.txt file. Please be patient.' - Invoke-process dism.exe "/image:W:\ /Add-Driver /Driver:""$Drivers"" /Recurse" - WriteLog 'Copying drivers succeeded' -} - -#Copy DISM log to USBDrive -WriteLog "Copying dism log to $USBDrive" -invoke-process xcopy "X:\Windows\logs\dism\dism.log $USBDrive /Y" -WriteLog "Copying dism log to $USBDrive succeeded" - - - - diff --git a/archive/FFUDevelopment/WinPEDeployFFUFiles/Windows/System32/startnet.cmd b/archive/FFUDevelopment/WinPEDeployFFUFiles/Windows/System32/startnet.cmd deleted file mode 100644 index 12519ba..0000000 --- a/archive/FFUDevelopment/WinPEDeployFFUFiles/Windows/System32/startnet.cmd +++ /dev/null @@ -1,5 +0,0 @@ -wpeinit -powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c -powershell -Noprofile -ExecutionPolicy Bypass -File x:\ApplyFFU.ps1 -exit - diff --git a/archive/FFUDevelopment/wimToFFU/Convert-WimToFfu.ps1 b/archive/FFUDevelopment/wimToFFU/Convert-WimToFfu.ps1 deleted file mode 100644 index 5739c5c..0000000 --- a/archive/FFUDevelopment/wimToFFU/Convert-WimToFfu.ps1 +++ /dev/null @@ -1,556 +0,0 @@ -#Requires -Modules Hyper-V, Storage -#Requires -PSEdition Desktop - -<# - .NOTES - Copyright (c) Microsoft Corporation. - Licensed under the MIT License. - - .SYNOPSIS - Creates an FFU with proper disk layout given an input WIM - - .DESCRIPTION - Creates an FFU with proper disk layout given an input WIM - - .PARAMETER WimPath - The path to the WIM to be converted into an FFU - - .PARAMETER WimIndex - The index of the image within the WIM to be used - - .PARAMETER FfuPath - Output path of the FFU to be created - - .PARAMETER ScratchVhdxPath - Output path of the scratch VHDX which will be created as an intermediate-step to the process of creating the FFU. - - .PARAMETER SaveScratchVhdx - Keep the scratch VHDX after capturing the FFU. This VHDX can be used to create a VM for testing. - - .PARAMETER SizeBytes - Size in bytes of the disk the FFU is to be applied to. - With an Optimized FFU, the FFU can then be applied to any disk large enough for the data within the FFU. - - .PARAMETER LogicalSectorSizeBytes - Logical sector size to store data within the FFU. - This should match the logical sector size of the disks this FFU is applied to. - - .PARAMETER Dynamic - Whether the scratch VHDX is to be a dynamically sized file or a fixed file size to match the maximum size of the VHDX. - - .PARAMETER PartitionStyle - GPT or MBR partition style for the final disk layout. - - .PARAMETER SkipSystemPartition - Skips creating a System partition. Boot files will be placed on the OS partition. - - .PARAMETER SystemPartitionSize - If creating a System partition, specifies the size in bytes of that partition. - - .PARAMETER SkipMSRPartition - Skips creating an MSR partition. - - .PARAMETER OSPartitionSize - Allows the specification of the OS partition size. If left at default, the OS partition will take all available space, - minus what is needed by the Recovery partition, if any. - If a Data partition is specified, an OS partition size must be specified. - - .PARAMETER AddDataPartition - Allows the addition of an extra Data partition, separate from the OS partition. - - .PARAMETER DataPartitionSize - Allows specifying the size of the Data partition, if the -AddDataPartition flag is used. - - .PARAMETER SkipRecoveryPartition - Skips the creation of a Recovery partition, used to store the Windows Recovery Environment (WinRE.wim). - - .PARAMETER RecoveryPartitionSize - Specifies the size of the Recovery partition to be created. - If left at default, the partition will be the size of the WinRE.wim in the OS partition plus 52 MB plus a buffer of 32 MB - since free space of WinRE.wim + 52 MB is needed. - - .PARAMETER FFUDriveName - Name passed to DISM's /Capture-FFU /Name parameter, used to set a name for the FFU, separate from the file name. - - .PARAMETER FFUCompression - Specifies FFU compression of Default or None. - - .PARAMETER FirmwareType - Specifies to create boot files for the disk for a firmware of BIOS, UEFI, or both (ALL). - - .PARAMETER OptimizeFfu - Creates an Optimized FFU which can be applied to a disk of a different size than the original FFU disk size - as long as the disk it is applied to is large enough to fit the data within the FFU. - Optimized FFUs are only available on Windows version 1903 and higher. - - .PARAMETER Force - Forces the overwriting of existing scratch VHDX or FFUs if the script is run multiple times - or specifying a path with an existing VHDX or FFU of the same name. - - .EXAMPLE - .\Convert-WimToFfu.ps1 -WimPath .\install.wim - - Creates an FFU named install.ffu in the same directory as the passed install.wim - - .EXAMPLE - .\Convert-WimToFfu.ps1 -WimPath .\install.wim -WimIndex 1 -FfuPath .\flash.ffu - - Creates an FFU from the Windows image at index 1 within install.wim and names the FFU "flash.ffu" - - .EXAMPLE - .\Convert-WimToFfu.ps1 -WimPath .\install.wim -WimIndex 1 -FfuPath .\flash.ffu -SizeBytes 64GB - - Creates an FFU that can only be applied on "64GB" disks. - Keep in mind that 64GB is 68,719,476,736 bytes which may be larger than the target disks. - - .EXAMPLE - .\Convert-WimToFfu.ps1 -WimPath .\install.wim -WimIndex 1 -FfuPath .\flash.ffu -OptimizeFfu - Creates an FFU which can be applied to disks of a different size than the original FFU disk size. -#> - -param( - [Parameter(Mandatory = $true, Position = 0)] - [Alias("Path")] - [ValidateScript({ Test-Path $_ })] - [string]$WimPath, - [uint32]$WimIndex = 1, - [string]$FfuPath, - [string]$ScratchVhdxPath, - [switch]$SaveScratchVhdx, - [uint64]$SizeBytes = 31000000000, - [ValidateSet(512, 4096)] - [uint32]$LogicalSectorSizeBytes = 512, - [switch]$Dynamic, - [Microsoft.PowerShell.Cmdletization.GeneratedTypes.Disk.PartitionStyle]$PartitionStyle = [Microsoft.PowerShell.Cmdletization.GeneratedTypes.Disk.PartitionStyle]::GPT, - [switch]$SkipSystemPartition, - [uint64]$SystemPartitionSize = 256MB, - [switch]$SkipMSRPartition, - [uint64]$OSPartitionSize = 0, - [switch]$AddDataPartition, - [uint64]$DataPartitionSize = 0, - [switch]$SkipRecoveryPartition, - [uint64]$RecoveryPartitionSize = 0, - [string]$FFUDriveName = "WimToFfu", - [ValidateSet("Default", "None")] - [string]$FFUCompression = "Default", - [ValidateSet("UEFI", "BIOS", "ALL")] - [string]$FirmwareType = "UEFI", - [switch]$OptimizeFFU, - [switch]$Force -); - -#region FUNCTIONS - -function Add-BootFiles -{ - param( - [Parameter(Mandatory = $true)] - [string]$OsPartitionDriveLetter, - [Parameter(Mandatory = $true)] - [string]$SystemPartitionDriveLetter - ); - - Write-Host "Adding boot files for `"$($OsPartitionDriveLetter):\Windows`" to System partition `"$($SystemPartitionDriveLetter):`"..."; - - bcdboot "$($OsPartitionDriveLetter):\Windows" /S "$($SystemPartitionDriveLetter):" /F "$FirmwareType"; - - Write-Host "Done."; -} - -function Get-RecoveryPartition -{ - param( - [Parameter(Mandatory = $true)] - [ciminstance]$VhdxDisk, - [Parameter(Mandatory = $true)] - [ciminstance]$OsPartition, - [uint64]$RecoveryPartitionSize = 0, - [ciminstance]$DataPartition - ); - - Write-Host "Creating empty Recovery partition (to be filled on first boot automatically)..."; - - $calculatedRecoverySize = 0; - $recoveryPartition = $null; - - if($RecoveryPartitionSize -gt 0) - { - $calculatedRecoverySize = $RecoveryPartitionSize; - } - else - { - $winReWim = Get-ChildItem "$($OsPartition.DriveLetter):\Windows\System32\Recovery\Winre.wim"; - - if(($winReWim -ne $null) -and ($winReWim.Count -eq 1)) - { - # Wim size + 52MB is minimum WinRE partition size. - # NTFS and other partitioning size differences account for about 17MB of space that's unavailable. - # Adding 32MB as a buffer to ensure there's enough space. - $calculatedRecoverySize = $winReWim.Length + 52MB + 32MB; - - Write-Host "Calculated space needed for recovery in bytes: $calculatedRecoverySize"; - - if($DataPartition -ne $null) - { - $DataPartition | Resize-Partition -Size ($DataPartition.Size - $calculatedRecoverySize); - Write-Host "Data partition shrunk by $calculatedRecoverySize bytes for Recovery partition."; - } - else - { - $OsPartition | Resize-Partition -Size ($OsPartition.Size - $calculatedRecoverySize); - Write-Host "OS partition shrunk by $calculatedRecoverySize bytes for Recovery partition."; - } - - $recoveryPartition = $VhdxDisk | New-Partition -AssignDriveLetter -UseMaximumSize -GptType "{de94bba4-06d1-4d40-a16a-bfd50179d6ac}" ` - | Format-Volume -FileSystem NTFS -Confirm:$false -Force -NewFileSystemLabel "WinRE"; - - Write-Host "Done. Recovery partition at drive $($recoveryPartition.DriveLetter):"; - } - else - { - Write-Host "No WinRE.WIM found in the OS partition under \Windows\System32\Recovery."; - Write-Host "Skipping creating the Recovery partition."; - Write-Host "If a Recovery partition is desired, please re-run the script setting the -RecoveryPartitionSize flag as appropriate." - } - } - - return $recoveryPartition; -} - -function Get-DataPartition -{ - param( - [Parameter(Mandatory = $true)] - [ciminstance]$VhdxDisk, - [uint64]$DataPartitionSize = 0 - ); - - Write-Host "Creating Data partition..."; - - $dataPartition = $null; - - if(($OSPartitionSize -ne $null) -and ($OSPartitionSize -gt 0)) - { - if(($DataPartitionSize -ne $null) -and ($DataPartitionSize -gt 0)) - { - $dataPartition = $vhdxDisk | New-Partition -AssignDriveLetter -Size $DataPartitionSize; - } - else - { - $dataPartition = $vhdxDisk | New-Partition -AssignDriveLetter -UseMaximumSize; - } - } - else - { - Write-Host "To add a data partition, OS partition size must be set. Skipping adding data partition..."; - } - - Write-Host "Done. Data partition at drive $($dataPartition.DriveLetter):"; - - return $dataPartition; -} - -function Get-OSPartition -{ - param( - [Parameter(Mandatory = $true)] - [ciminstance]$VhdxDisk, - [Parameter(Mandatory = $true)] - [string]$WimPath, - [uint32]$WimIndex = 1, - [uint64]$OSPartitionSize = 0 - ); - - Write-Host "Creating OS partition..."; - - if($OSPartitionSize -gt 0) - { - $osPartition = $vhdxDisk | New-Partition -AssignDriveLetter -Size $OSPartitionSize; - } - else - { - $osPartition = $vhdxDisk | New-Partition -AssignDriveLetter -UseMaximumSize; - } - - $formattedOsPartition = $osPartition | Format-Volume -FileSystem NTFS -Confirm:$false -Force -NewFileSystemLabel "Windows"; - Write-Host "Done. OS partition at drive $($osPartition.DriveLetter):"; - - Write-Host "Writing WIM at $WimPath to OS partition at drive $($osPartition.DriveLetter):..."; - - #Server 2019 is missing the Windows Overlay Filter (wof.sys), likely other Server SKUs are missing it as well. Script will error if trying to use the -compact switch on Server OSes - if((Get-CimInstance Win32_OperatingSystem).Caption -match "Server"){ - Write-Host (Expand-WindowsImage -ImagePath $WimPath -Index $WimIndex -ApplyPath "$($osPartition.DriveLetter):\"); - } - else { - Write-Host (Expand-WindowsImage -ImagePath $WimPath -Index $WimIndex -ApplyPath "$($osPartition.DriveLetter):\" -Compact); - } - - Write-Host "Done."; - - return $osPartition; -} - -function Get-MSRPartition -{ - param( - [Parameter(Mandatory = $true)] - [ciminstance]$VhdxDisk - ); - - Write-Host "Creating MSR partition..."; - - $toReturn = $VhdxDisk | New-Partition -AssignDriveLetter -Size 16MB -GptType "{e3c9e316-0b5c-4db8-817d-f92df00215ae}" -IsHidden; - - Write-Host "Done."; - - return $toReturn; -} - -function Get-SystemPartition -{ - param( - [Parameter(Mandatory = $true)] - [ciminstance]$VhdxDisk, - [uint64]$SystemPartitionSize = 100MB - ); - - Write-Host "Creating System partition..."; - - $sysPartition = $VhdxDisk | New-Partition -AssignDriveLetter -Size $SystemPartitionSize -GptType "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}" -IsHidden; - $formattedSysPartition = $sysPartition | Format-Volume -FileSystem FAT32 -Force -NewFileSystemLabel "System"; - - Write-Host "Done. System partition at drive $($sysPartition.DriveLetter):"; - return $sysPartition.DriveLetter; -} - -function Dismount-ScratchVhdx -{ - param( - [Parameter(Mandatory = $true)] - [string]$VhdxPath - ); - - if(Test-Path $VhdxPath) - { - Write-Host "Dismounting scratch VHDX..."; - Dismount-VHD -Path $VhdxPath; - Write-Host "Done."; - } -} - -function Get-ScratchVhdx -{ - param( - [Parameter(Mandatory = $true)] - [string]$VhdxPath, - [uint64]$SizeBytes = 64000000000, - [ValidateSet(512, 4096)] - [uint32]$LogicalSectorSizeBytes = 512, - [switch]$Dynamic, - [Microsoft.PowerShell.Cmdletization.GeneratedTypes.Disk.PartitionStyle]$PartitionStyle = [Microsoft.PowerShell.Cmdletization.GeneratedTypes.Disk.PartitionStyle]::GPT, - [switch]$AddRecoveryPartition, - [uint64]$RecoveryPartitionSize = 0 - ); - - Write-Host "Creating new Scratch VHDX..."; - - $newVHDX = New-VHD -Path $VhdxPath -SizeBytes $SizeBytes -LogicalSectorSizeBytes $LogicalSectorSizeBytes -Dynamic:($Dynamic.IsPresent); - $toReturn = $newVHDX | Mount-VHD -Passthru | Initialize-Disk -PassThru -PartitionStyle $PartitionStyle; - - Write-Host "Done."; - return $toReturn; -} - -function Get-OutputFilePath -{ - param( - [Parameter(Mandatory = $true)] - [string]$WimPath, - [string]$OutputFilePath, - [Parameter(Mandatory = $true)] - [string]$ParamName, - [Parameter(Mandatory = $true)] - [string]$Extension, - [Parameter(Mandatory = $true)] - [bool]$Force - ); - - if([string]::IsNullOrEmpty($OutputFilePath)) - { - $OutputFilePath = [System.IO.Path]::ChangeExtension($WimPath, $Extension); - } - - if((Test-Path $OutputFilePath) -and (-not $Force)) - { - throw New-Object System.ArgumentException("Unable to overwrite existing file $OutputFilePath without -Force flag.", $ParamName); - } - - return $OutputFilePath; -} - -function Write-If -{ - param( - [Parameter(Mandatory = $true)] - [bool]$Condition, - [Parameter(Mandatory = $true)] - [string]$MessageIfTrue, - [Parameter(Mandatory = $true)] - [string]$MessageIfFalse - ); - - if($Condition) - { - Write-Host $MessageIfTrue; - } - else - { - Write-Host $MessageIfFalse; - } -} - -#endregion - -#region MAIN SCRIPT BODY - -#region PRINT INPUT PARAMETERS -Write-Host "Using WIM path: $WimPath."; - -$FfuPath = Get-OutputFilePath -WimPath $WimPath -OutputFilePath $FfuPath -ParamName "FfuPath" -Extension "ffu" -Force $Force.IsPresent; -Write-Host "Using FFU path: $FfuPath."; - -$ScratchVhdxPath = Get-OutputFilePath -WimPath $WimPath -OutputFilePath $ScratchVhdxPath -ParamName "ScratchVhdxPath" -Extension "vhdx" -Force $Force.IsPresent; -Write-Host "Using VHDX path: $ScratchVhdxPath."; - -Write-Host "Using WIM Index: $WimIndex."; - -Write-If -Condition $SaveScratchVhdx.IsPresent ` - -MessageIfTrue "Will save intermediate scratch VHDX." ` - -MessageIfFalse "Will delete intermediate scratch VHDX."; - -Write-Host "Using Disk Size (bytes) of $SizeBytes."; -Write-Host "Using Logical Sector Size (bytes) of $LogicalSectorSizeBytes."; - -Write-If -Condition $Dynamic.IsPresent ` - -MessageIfTrue "Intermediate scratch VHDX on disk will be Dynamically sized." ` - -MessageIfFalse "Intermediate scratch VHDX on disk will be Fixed sized."; - -Write-Host "Partition style will be $PartitionStyle."; - -Write-If -Condition $SkipSystemPartition.IsPresent ` - -MessageIfTrue "Will not add System partition." ` - -MessageIfFalse "Will add System partition of size (bytes) $SystemPartitionSize."; - -Write-If -Condition $SkipMSRPartition.IsPresent ` - -MessageIfTrue "Will not add MSR partition." ` - -MessageIfFalse "Will add 16 MB MSR partition."; - -Write-If -Condition ($OSPartitionSize -eq 0) ` - -MessageIfTrue "Will create maximum-sized OS partition." ` - -MessageIfFalse "Will create OS partition of size (bytes) $OSPartitionSize."; - -Write-If -Condition $AddDataPartition.IsPresent ` - -MessageIfTrue "Will add extra Data partition of size (bytes) $DataPartitionSize." ` - -MessageIfFalse "Will not add extra Data partition."; - -Write-If -Condition $SkipRecoveryPartition.IsPresent ` - -MessageIfTrue "Will not add Recovery partition." ` - -MessageIfFalse "Will add Recovery partition."; - -Write-If -Condition $OptimizeFFU.IsPresent ` - -MessageIfTrue "Will run DISM's /Optimize-FFU command." ` - -MessageIfFalse "Will skip DISM's /Optimize-FFU command."; - -if(-not ($SkipSystemPartition.IsPresent)) -{ - if($RecoveryPartitionSize -eq 0) - { - Write-Host "Will use default, calculated Recovery partition size (WinRE.WIM size + 52 MB + plus a buffer of 32 MB due to NTFS)."; - } - else - { - Write-Host "Will add Recovery partition of size (bytes) $RecoveryPartitionSize."; - } -} - -Write-Host "Using FFU Drive name of $FFUDriveName."; -Write-Host "Using FFU compression of $FFUCompression."; -Write-Host "Using Firmware Type (for boot files) of $FirmwareType."; - -Write-If -Condition $Force.IsPresent ` - -MessageIfTrue "Force flag is present. Overwriting files when necessary." ` - -MessageIfFalse "Force flag is not present. Will not overwrite existing files."; - -#endregion PRINT INPUT PARAMETERS - -try -{ - $vhdxDisk = Get-ScratchVhdx -VhdxPath $ScratchVhdxPath -SizeBytes $SizeBytes -LogicalSectorSizeBytes $LogicalSectorSizeBytes -Dynamic:($Dynamic.IsPresent) -PartitionStyle $PartitionStyle; - - if(-not ($SkipSystemPartition.IsPresent)) - { - $systemPartitionDriveLetter = Get-SystemPartition -VhdxDisk $vhdxDisk -SystemPartitionSize $SystemPartitionSize; - } - - if(-not ($SkipMSRPartition.IsPresent)) - { - $msrPartition = Get-MSRPartition -VhdxDisk $vhdxDisk; - } - - $osPartition = Get-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $WimIndex; - - if($AddDataPartition.IsPresent) - { - $dataPartition = Get-DataPartition -VhdxDisk $vhdxDisk -DataPartitionSize $DataPartitionSize; - } - - if(-not($SkipRecoveryPartition.IsPresent)) - { - $recoveryPartition = Get-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition; - } - - Write-Host "All necessary partitions created."; - - if($SkipSystemPartition.IsPresent) - { - Add-BootFiles -OsPartitionDriveLetter $osPartition.DriveLetter -SystemPartitionDriveLetter $osPartition.DriveLetter; - } - else - { - Add-BootFiles -OsPartitionDriveLetter $osPartition.DriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter; - } - - Write-Host "Capturing scratch VHDX into FFU..."; - dism /Capture-FFU /ImageFile:"$FfuPath" /CaptureDrive:"\\.\PhysicalDrive$($vhdxDisk.DiskNumber)" /Name:"$FFUDriveName" /Compress:"$FFUCompression" - Write-Host "Done."; -} -finally -{ - Dismount-ScratchVhdx -VhdxPath $ScratchVhdxPath; -} - -if($SaveScratchVhdx.IsPresent) -{ - Write-Host "Scratch VHDX has been kept at $ScratchVhdxPath"; -} -else -{ - Remove-Item -Path $ScratchVhdxPath -Force -Confirm:$false; - Write-Host "Scratch VHDX has been deleted."; -} - -if($OptimizeFFU.IsPresent) -{ - Write-Host "Running DISM /Optimize-FFU /ImageFile:$FfuPath..."; - dism /Optimize-FFU /ImageFile:"$FfuPath" - Write-Host "Done."; -} -else -{ - Write-Host "Skipping running DISM /Optimize-FFU."; -} - -Write-Host "Convert-WimToFfu.ps1 script complete."; - -#endregion -