mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8ba334732 | |||
| ff46c10d79 | |||
| 4d9e1c1f88 | |||
| d5b81bc482 | |||
| 8f81e69159 | |||
| 4932777f4f | |||
| 12edabf213 | |||
| 1978736133 | |||
| a3faa89ada | |||
| fc8648eb65 | |||
| 49a9fd49c1 | |||
| 3f4836b478 | |||
| 1921809c30 | |||
| 56f3e9d856 | |||
| ae59183a19 |
@@ -15,8 +15,22 @@ del c:\windows\panther\unattend\unattend.xml /F /Q
|
|||||||
del c:\windows\panther\unattend.xml /F /Q
|
del c:\windows\panther\unattend.xml /F /Q
|
||||||
taskkill /IM sysprep.exe
|
taskkill /IM sysprep.exe
|
||||||
timeout /t 10
|
timeout /t 10
|
||||||
REM Run Component Cleanup since dism /online /cleanup-image /analyzecomponentcleanup recommends it
|
REM Run disk cleanup (cleanmgr.exe) with all options enabled: https://learn.microsoft.com/en-us/troubleshoot/windows-server/backup-and-storage/automating-disk-cleanup-tool
|
||||||
REM If adding latest CU, definitely need to do this to keep FFU size smaller
|
set rootkey=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches
|
||||||
dism /online /cleanup-image /startcomponentcleanup /resetbase
|
REM Per above doc, the Offline Pages Files subkey does not have stateflags value
|
||||||
|
for /f "tokens=*" %%K in ('reg query "%rootkey%"') do (
|
||||||
|
echo %%K | findstr /i /c:"Offline Pages Files"
|
||||||
|
if errorlevel 1 (
|
||||||
|
reg add "%%K" /v StateFlags0000 /t REG_DWORD /d 2 /f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cleanmgr.exe /sagerun:0
|
||||||
|
REM Remove the StateFlags0000 registry value
|
||||||
|
for /f "tokens=*" %%K in ('reg query "%rootkey%"') do (
|
||||||
|
echo %%K | findstr /i /c:"Offline Pages Files"
|
||||||
|
if errorlevel 1 (
|
||||||
|
reg delete "%%K" /v StateFlags0000 /f
|
||||||
|
)
|
||||||
|
)
|
||||||
REM Sysprep/Generalize
|
REM Sysprep/Generalize
|
||||||
c:\windows\system32\sysprep\sysprep.exe /quiet /generalize /oobe
|
c:\windows\system32\sysprep\sysprep.exe /quiet /generalize /oobe
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ param(
|
|||||||
[bool]$CleanupDeployISO = $true,
|
[bool]$CleanupDeployISO = $true,
|
||||||
[bool]$CleanupAppsISO = $true
|
[bool]$CleanupAppsISO = $true
|
||||||
)
|
)
|
||||||
$version = '2404.2'
|
$version = '2405.1'
|
||||||
|
|
||||||
#Check if Hyper-V feature is installed (requires only checks the module)
|
#Check if Hyper-V feature is installed (requires only checks the module)
|
||||||
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
|
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
|
||||||
@@ -1438,6 +1438,31 @@ function New-PEMedia {
|
|||||||
Remove-Item -Path "$WinPEFFUPath" -Recurse -Force
|
Remove-Item -Path "$WinPEFFUPath" -Recurse -Force
|
||||||
WriteLog 'Cleanup complete'
|
WriteLog 'Cleanup complete'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Optimize-FFUCaptureDrive {
|
||||||
|
param (
|
||||||
|
[string]$VhdxPath
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
WriteLog 'Mounting VHDX for volume optimization'
|
||||||
|
Mount-VHD -Path $VhdxPath
|
||||||
|
WriteLog 'Defragmenting Windows partition...'
|
||||||
|
Optimize-Volume -DriveLetter W -Defrag -NormalPriority -Verbose
|
||||||
|
WriteLog 'Performing slab consolidation on Windows partition...'
|
||||||
|
Optimize-Volume -DriveLetter W -SlabConsolidate -NormalPriority -Verbose
|
||||||
|
WriteLog 'Dismounting VHDX'
|
||||||
|
Dismount-ScratchVhdx -VhdxPath $VhdxPath
|
||||||
|
WriteLog 'Mounting VHDX as read-only for optimization'
|
||||||
|
Mount-VHD -Path $VhdxPath -NoDriveLetter -ReadOnly
|
||||||
|
WriteLog 'Optimizing VHDX in full mode...'
|
||||||
|
Optimize-VHD -Path $VhdxPath -Mode Full
|
||||||
|
WriteLog 'Dismounting VHDX'
|
||||||
|
Dismount-ScratchVhdx -VhdxPath $VhdxPath
|
||||||
|
} catch {
|
||||||
|
throw $_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function New-FFU {
|
function New-FFU {
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $false)]
|
[Parameter(Mandatory = $false)]
|
||||||
@@ -2039,7 +2064,7 @@ if ($InstallApps) {
|
|||||||
#Update Latest Defender Platform and Definitions - these can't be serviced into the VHDX, will be saved to AppsPath
|
#Update Latest Defender Platform and Definitions - these can't be serviced into the VHDX, will be saved to AppsPath
|
||||||
if ($UpdateLatestDefender) {
|
if ($UpdateLatestDefender) {
|
||||||
WriteLog "`$UpdateLatestDefender is set to true, checking for latest Defender Platform and Definitions"
|
WriteLog "`$UpdateLatestDefender is set to true, checking for latest Defender Platform and Definitions"
|
||||||
$Name = 'Update for Microsoft Defender Antivirus antimalware platform'
|
$Name = "Update for Microsoft Defender Antivirus antimalware platform"
|
||||||
#Check if $DefenderPath exists, if not, create it
|
#Check if $DefenderPath exists, if not, create it
|
||||||
If (-not (Test-Path -Path $DefenderPath)) {
|
If (-not (Test-Path -Path $DefenderPath)) {
|
||||||
WriteLog "Creating $DefenderPath"
|
WriteLog "Creating $DefenderPath"
|
||||||
@@ -2104,7 +2129,7 @@ if ($InstallApps) {
|
|||||||
$OneDriveURL = 'https://go.microsoft.com/fwlink/?linkid=844652'
|
$OneDriveURL = 'https://go.microsoft.com/fwlink/?linkid=844652'
|
||||||
try {
|
try {
|
||||||
Start-BitsTransfer -Source $OneDriveURL -Destination "$OneDrivePath\OneDriveSetup.exe"
|
Start-BitsTransfer -Source $OneDriveURL -Destination "$OneDrivePath\OneDriveSetup.exe"
|
||||||
WriteLog "Defender Definitions downloaded to $OneDrivePath\OneDriveSetup.exe"
|
WriteLog "OneDrive client downloaded to $OneDrivePath\OneDriveSetup.exe"
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Host "Downloading OneDrive client Failed"
|
Write-Host "Downloading OneDrive client Failed"
|
||||||
@@ -2196,7 +2221,7 @@ try {
|
|||||||
#The Windows release info page is updated later than the MU Catalog
|
#The Windows release info page is updated later than the MU Catalog
|
||||||
if ($UpdateLatestCU) {
|
if ($UpdateLatestCU) {
|
||||||
Writelog "`$UpdateLatestCU is set to true, checking for latest CU"
|
Writelog "`$UpdateLatestCU is set to true, checking for latest CU"
|
||||||
$Name = "Cumulative update for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"
|
$Name = """Cumulative update for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
|
||||||
#Check if $KBPath exists, if not, create it
|
#Check if $KBPath exists, if not, create it
|
||||||
If (-not (Test-Path -Path $KBPath)) {
|
If (-not (Test-Path -Path $KBPath)) {
|
||||||
WriteLog "Creating $KBPath"
|
WriteLog "Creating $KBPath"
|
||||||
@@ -2211,7 +2236,7 @@ try {
|
|||||||
#Update Latest .NET Framework
|
#Update Latest .NET Framework
|
||||||
if ($UpdateLatestNet) {
|
if ($UpdateLatestNet) {
|
||||||
Writelog "`$UpdateLatestNet is set to true, checking for latest .NET Framework"
|
Writelog "`$UpdateLatestNet is set to true, checking for latest .NET Framework"
|
||||||
$Name = "Cumulative update for .net framework windows $WindowsRelease $WindowsVersion $WindowsArch"
|
$Name = "Cumulative update for .net framework windows $WindowsRelease $WindowsVersion $WindowsArch -preview"
|
||||||
#Check if $KBPath exists, if not, create it
|
#Check if $KBPath exists, if not, create it
|
||||||
If (-not (Test-Path -Path $KBPath)) {
|
If (-not (Test-Path -Path $KBPath)) {
|
||||||
WriteLog "Creating $KBPath"
|
WriteLog "Creating $KBPath"
|
||||||
@@ -2240,10 +2265,13 @@ try {
|
|||||||
if ($UpdateLatestCU -or $UpdateLatestNet) {
|
if ($UpdateLatestCU -or $UpdateLatestNet) {
|
||||||
try {
|
try {
|
||||||
WriteLog "Adding KBs to $WindowsPartition"
|
WriteLog "Adding KBs to $WindowsPartition"
|
||||||
Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath | Out-Null
|
Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
|
||||||
WriteLog "KBs added to $WindowsPartition"
|
WriteLog "KBs added to $WindowsPartition"
|
||||||
WriteLog "Removing $KBPath"
|
WriteLog "Removing $KBPath"
|
||||||
Remove-Item -Path $KBPath -Recurse -Force | Out-Null
|
Remove-Item -Path $KBPath -Recurse -Force | Out-Null
|
||||||
|
WriteLog "Clean Up the WinSxS Folder"
|
||||||
|
Invoke-Process cmd "/c ""$DandIEnv"" && Dism /Image:$WindowsPartition /Cleanup-Image /StartComponentCleanup /ResetBase" | Out-Null
|
||||||
|
WriteLog "Clean Up the WinSxS Folder completed"
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Host "Adding KB to VHDX failed with error $_"
|
Write-Host "Adding KB to VHDX failed with error $_"
|
||||||
@@ -2364,6 +2392,7 @@ try {
|
|||||||
WriteLog 'Waiting for VM to shutdown'
|
WriteLog 'Waiting for VM to shutdown'
|
||||||
} while ($FFUVM.State -ne 'Off')
|
} while ($FFUVM.State -ne 'Off')
|
||||||
WriteLog 'VM Shutdown'
|
WriteLog 'VM Shutdown'
|
||||||
|
Optimize-FFUCaptureDrive -VhdxPath $VHDXPath
|
||||||
#Capture FFU file
|
#Capture FFU file
|
||||||
New-FFU $FFUVM.Name
|
New-FFU $FFUVM.Name
|
||||||
}
|
}
|
||||||
@@ -2498,4 +2527,4 @@ If ($CleanupAppsISO) {
|
|||||||
#Clean up dirty.txt file
|
#Clean up dirty.txt file
|
||||||
Remove-Item -Path .\dirty.txt -Force | out-null
|
Remove-Item -Path .\dirty.txt -Force | out-null
|
||||||
Write-Host "Script complete"
|
Write-Host "Script complete"
|
||||||
WriteLog "Script complete"
|
WriteLog "Script complete"
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ $LogFileName = 'ScriptLog.txt'
|
|||||||
$USBDrive = Get-USBDrive
|
$USBDrive = Get-USBDrive
|
||||||
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
|
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
|
||||||
$LogFile = $USBDrive + $LogFilename
|
$LogFile = $USBDrive + $LogFilename
|
||||||
$version = '2404.2'
|
$version = '2405.1'
|
||||||
WriteLog 'Begin Logging'
|
WriteLog 'Begin Logging'
|
||||||
WriteLog "Script version: $version"
|
WriteLog "Script version: $version"
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,15 @@ This process will copy Windows in about 2-3 minutes to the target device, option
|
|||||||
While we use this in Education at Microsoft, other industries can use it as well. We esepcially see a need for something like this with partners who do re-imaging on behalf of customers. The difference in Education is that they typically have large deployments that tend to happen at the beginning of the school year and any amount of time saved is helpful. Microsoft Deployment Toolkit, Configuration Manager, and other community solutions are all great solutions, but are typically slower due to WIM deployments being file-based while FFU files are sector-based.
|
While we use this in Education at Microsoft, other industries can use it as well. We esepcially see a need for something like this with partners who do re-imaging on behalf of customers. The difference in Education is that they typically have large deployments that tend to happen at the beginning of the school year and any amount of time saved is helpful. Microsoft Deployment Toolkit, Configuration Manager, and other community solutions are all great solutions, but are typically slower due to WIM deployments being file-based while FFU files are sector-based.
|
||||||
|
|
||||||
# Updates
|
# Updates
|
||||||
|
**2405.1**
|
||||||
|
- Moved the resetbase command from within the VM to after servicing the VHDX. This will make it so the FFU size is smaller after the latest CU or .NET framework are installed. (Thanks to Mike Kelly for the PR [Commit](https://github.com/rbalsleyMSFT/FFU/pull/24))
|
||||||
|
- Some additional FFU size reduction enhancements (Thanks Zehadi Alam [Commit](https://github.com/rbalsleyMSFT/FFU/pull/25)):
|
||||||
|
- Disk cleanup is now run before sysprep to help reduce FFU file size
|
||||||
|
- Before FFU capture, Optimize-FFU is run to defrag and slabconsolidate the VHDX
|
||||||
|
|
||||||
|
|
||||||
|
**2404.3**
|
||||||
|
- Fixed an issue where the latest Windows CU wasn't downloading properly [Commit](https://github.com/rbalsleyMSFT/FFU/commit/ae59183a199f39b310c79b31c9b4980fafdeb79b)
|
||||||
|
|
||||||
**2404.2**
|
**2404.2**
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user