Compare commits

..

221 Commits

Author SHA1 Message Date
rbalsleyMSFT 7b6b5efd8d Merge branch 'main' of https://github.com/rbalsleyMSFT/FFU 2025-01-10 13:50:03 -08:00
rbalsleyMSFT db62e05275 Bug fixes
- Fixed an issue with WinPE Drivers not being added to Deployment media
- Fixed an issue where Windows SKUs that include spaces in their names (e.g. Pro Education) and `$InstallApps $false`, FFU creation would fail due to the name including a space. Added a new function `Get-ShortenedWindowsSKU` to truncate the SKU for FFU name creation purposes. This required various changes throughout the script that relied on the Windows SKU for naming.
- Updated version 2412.3
2025-01-10 13:50:00 -08:00
rbalsleyMSFT 3db66eb55b Merge pull request #119 from HedgeComp/HP-Versions-Fix
Fix HP Driver Windows Version case sensitivity
2025-01-08 12:08:04 -08:00
HedgeComp 4709177bc3 Fix HP Driver Windows Version case sensitivity 2025-01-08 13:16:08 -05:00
rbalsleyMSFT ce7af09f25 Update README.md 2024-12-20 13:03:14 -08:00
rbalsleyMSFT e3da438225 Update README.md 2024-12-20 13:02:20 -08:00
rbalsleyMSFT edbb7ccabe Update ChangeLog.md 2024-12-20 13:01:50 -08:00
rbalsleyMSFT f4360b34d9 Merge pull request #113 from rbalsleyMSFT/2410.1
2412.1
2024-12-20 12:42:44 -08:00
rbalsleyMSFT 0ed0cf4aa2 Changed ApplyFFU version to 2412.1 2024-12-19 17:09:06 -08:00
rbalsleyMSFT 61fc2198c9 Updated docs and update parameter descriptions in BuildFFUVM.ps1 2024-12-19 16:52:52 -08:00
rbalsleyMSFT f45f5a899b Added parameter description for ExportConfigFile 2024-12-19 15:47:10 -08:00
rbalsleyMSFT 37f6dce344 Changes
- Updated parameter definition block to be alphabetized (not to be confused by the param block, which is not alphabetized)
- Added $PEDriversFolder script variable to the param block (for some reason it was missing)
- Added ConfigFile and ExportConfigFile parameters to support json config files
- Changed Version to 2412.1
- Modified vhdxCacheItem class to include $LogicalSectorSizeBytes
- Added new function Get-Parameters to help with new config and export config file functionality
- Fixed Get-MicrosoftDrivers function to not require the HTMLFILE COM object, which isn't available in Windows 11. It seems to be installed with Office, which is what was allowing downloads to work and masked the issue.
- Added long path support to prevent issues with oscdimg creating the Apps.iso.
- Fixed an issue where the $PEDriversFolder variable wasn't being used (instead $FFUDevelopment\PEDrivers was used)
- Created a new function New-FFUFileName - this works in conjunction with the new $CustomFFUNameTemplate. The function was needed to support both scenarios where $InstallApps is either $true or $false.
- Added new function Export-ConfigFile. When passing -ExportConfigFile 'Path\To\ConfigFile.json' the script will generate a parameter dump of all of the configured parameters
- Added driver folder validation to throw an error if spaces are detected in the folder name of the drivers folder (e.g. C:\FFUDevelopment\Drivers\Dell 3190). This is due to an issue with Dell drivers and their inability to handle paths with spaces consistently.
- Added back the Windows Security Platform update which grabs it from the web instead of the Microsoft update catalog
- Fixed an issue where the Drivers folder was being completely deleted instead of its sub-folders
- Removed the Requires -PSEdition Desktop. The script works with both Desktop and Core, so pwsh 7 is fine.
- Created a new config folder to hold config files. A new sample_default.json file is provided to show what the format looks like.
- You can now set the computername in the unattend.xml file whereever you want. Prior it required that the computername was the first component element.
2024-12-18 16:10:35 -08:00
rbalsleyMSFT 10624787fe Fix ODT
- Refactored the Get-ODTURL function to fix recent download issues. Also added some better error handling
- Moved the odtsetup.exe download to the FFUDevelopment folder and will clean it up after office has downloaded
2024-12-10 15:39:49 -08:00
rbalsleyMSFT d7a697d68d Misc Fixes
- Added Get-Childprocesses function to return child processes of parent process
- Added a new -Wait boolean parameter to Invoke-Process function. This is to control whether Invoke-Process should wait in order to track stdout and stderr output. This is needed for processes that may hang, waiting for user input and there isn't a way to bypass (some Intel drivers provided by Dell leave dialog windows even when running silently)
-Invoke-Process now returns process information (returns $cmd). This allows for process tracking when calling the function.
- Since Invoke-Process now returns the process information, also needed to add Out-Null to the majority of the Invoke-Process references to prevent Invoke-Process from writing to the terminal
- Refactored a lot of the Get-DellDrivers function due to inconsistencies with how driver extraction behaves between client and server devices. For client, /s /e seemed to work fine, but for server it would only extract the driver installer content and other dell related files, rather than the driver files themselves. We have since switched to using /s /drivers= which will extract the driver content. Not all drivers support /drivers= and may output some information to the terminal that looks like help documentation. If a driver doesn't support /drivers, the script falls back to using /s /e to do the extraction. If this doesn't work for you, you can always provide your own drivers that you manually download from Dell's website.
- Updated Malicious Software Removal Tool (MSRT) code to handle Windows Server
2024-12-09 14:59:02 -08:00
rbalsleyMSFT 97954f59c3 - Added Windows 2025/24H2 mapping 2024-12-03 16:55:40 -08:00
rbalsleyMSFT 3b23e9f420 Merge pull request #91 from JonasKloseBW:Better-Windows-Server-Patch-Search
2410.1 Find better Windows Server patches
2024-12-03 16:14:17 -08:00
rbalsleyMSFT 94712ecbfc Merge remote-tracking branch 'origin/2410.1' into pr/JonasKloseBW/91 2024-12-03 16:13:26 -08:00
Mike Kelly a2c2b69026 Update USBImagingToolCreator.ps1 2024-12-03 13:43:36 -08:00
rbalsleyMSFT 6abc6f9d1a Fixed an error with removing the office variable when capturing the FFU 2024-12-02 18:34:58 -08:00
rbalsleyMSFT db0fbfaaf4 Refactor CaptureFFU.ps1 to use $WindowsVersion and $WindowsRelease for improved clarity and consistency in FFU naming 2024-12-02 18:11:38 -08:00
JonasKloseBW a59210c559 Update CaptureFFU.ps1
- Add $CustomFFUNameTemplate support in Update CaptureFFU.ps1
2024-12-02 18:11:38 -08:00
JonasKloseBW add11b0037 Update BuildFFUVM.ps1
- Add $CustomFFUNameTemplate and the required code into BuildFFUVM.ps1
2024-12-02 18:11:38 -08:00
HedgeComp c26034a89c Update unattend_arm64.xml 2024-12-02 10:40:18 -08:00
HedgeComp 9287464eb8 Update unattend_x64.xml 2024-12-02 10:40:18 -08:00
HedgeComp 1c103b2db7 Rename SampleUnattend_64.xml to SampleUnattend_x64.xml 2024-12-02 10:40:18 -08:00
HedgeComp ed6a5fc7f1 Create SampleUnattend_64.xml
Sample for Unattend File.
2024-12-02 10:40:18 -08:00
rbalsleyMSFT 768efc8cf7 Merge pull request #99 from rbalsleyMSFT/revert-98-revert-89-2410.1-Allow-VHDX-caching
Revert "Revert "2410.1 Allow vhdx caching""
2024-11-28 17:41:06 -08:00
rbalsleyMSFT 39a9bc9022 Revert "Revert "2410.1 Allow vhdx caching"" 2024-11-28 17:40:22 -08:00
rbalsleyMSFT f90b7b3c9b Merge pull request #98 from rbalsleyMSFT/revert-89-2410.1-Allow-VHDX-caching
Revert "2410.1 Allow vhdx caching"
2024-11-28 17:38:07 -08:00
rbalsleyMSFT 802a225c3e Revert "2410.1 Allow vhdx caching" 2024-11-28 17:36:00 -08:00
rbalsleyMSFT 74370db5de Merge pull request #89 from JonasKloseBW/2410.1-Allow-VHDX-caching 2024-11-27 13:18:20 -08:00
rbalsleyMSFT db788c3c30 - If not passing an ISO, hardcoded WindowsVersion of 22H2 for Windows 10 or 24H2 for Windows 11 since the ESD media only provides those two versions. Not doing this allowed for unnecessary VHDX creation since it checks the WindowsVersion via the json file. This also fixes an issue where CUs could be searched for that didn’t exist, but the media would still download
- Added some additional logging entries
- Removed verbose output of the Optimize-Volume command
- Fixed an issue where not passing an ISO caused the script to fail
- Cleaned up the KBPath folder at the end of the run
- Changed some minor formatting items
2024-11-27 13:13:29 -08:00
JonasKloseBW eae619e7e8 Update BuildFFUVM.ps1
- Fix update search for Windows Server 2025 release
2024-11-04 18:23:57 +01:00
JonasKloseBW 0f3380e91e Merge pull request #19 from rbalsleyMSFT/2410.1
- $WindowsVersion set to 24h2, can override by using -WindowsVersion …
2024-10-12 03:30:16 +02:00
JonasKloseBW 7c80486d88 Merge pull request #18 from rbalsleyMSFT/2410.1
- $WindowsVersion set to 24h2, can override by using -WindowsVersion …
2024-10-12 03:22:31 +02:00
rbalsleyMSFT 1f65198803 - $WindowsVersion set to 24h2, can override by using -WindowsVersion 23H2 if you want the old behavior
- Removed the "Downloading information GUID" messages when downloading content from the Microsoft Update Catalog while -verbose was specified in the command line
- In Get-KBLink, made a change to just grab the first result returned instead of the entire results page. This removes the need to use a break statement in Save-KB when downloading updates. This fixed an issue with the new 24H2 Checkpoint Cumulative Updates in Win11 and Server 2025.
- Changed Windows MSRT search string for x64 and x86. This was mainly to get x64 to the top of the search results. x86 won't actually download since the code isn't in place for content from the MU Catalog to download x86 content (no idea if anyone actually builds x86 FFUs for Win10 - I hope not)
2024-10-09 16:17:11 -07:00
JonasKloseBW 0f18b7bd80 Merge branch '2410.1' into Better-Windows-Server-Patch-Search 2024-10-05 02:36:47 +02:00
JonasKloseBW b89f4a3b6b Merge branch '2410.1' into 2410.1-Allow-VHDX-caching 2024-10-05 02:11:50 +02:00
rbalsleyMSFT 658d2d7af4 - Added server skus to validateset for $WindowsSKU
- Added new variable $installationType which uses $WindowsRelease to determine Server or Client. If $installationType is Server, $WindowsRelease version is used to set $WindowsVersion to the appropriate version (1607, 1809, 21H2)
- Fixed an issue where the recovery partition wouldn't be created on server OSes due to winre.wim being hidden. Never saw this on client OSes even though it also was hidden IIRC.
- Removed verbosity for Optimize-Volume as it was outputting when -verbose was not specified.
- Modified some search strings for .NET CUs when installing on Server OS
- Included SSU for Windows Server 2016 as it's mandatory
- Added some error checking for Server 2019 and 2022 CU installations when CU fails due to lack of SSU. If you run into this error, you're using old media and should use the latest. Always use the latest ISO if you can.
2024-10-04 14:58:21 -07:00
JonasKloseBW f09c404f65 Update BuildFFUVM.ps1
- Find better results for Windows Server 2019 .NET Framework updates
2024-10-03 03:47:33 +02:00
JonasKloseBW fbe8eca263 Update Search Strings: Update BuildFFUVM.ps1
- Update search strings for Windows / .NET Framework updates to get more consistent and reliable results
2024-10-02 15:05:41 +02:00
JonasKloseBW 923e8b070d Merge branch '2410.1' into 2410.1-Allow-VHDX-caching 2024-10-01 03:46:04 +02:00
rbalsleyMSFT e6e53e566f Merge pull request #84 from JonasKloseBW/2410.1
Add support for Windows Server in 2410.1 branch
2024-09-30 10:31:42 -07:00
JonasKloseBW f144f1d71c Update CaptureFFU.ps1
- Add support for Server Core in SKU name
2024-09-30 16:52:15 +02:00
JonasKloseBW 3694e1a6e4 Update BuildFFUVM.ps1
- Add parameter $AllowVHDXCaching
- Adds the ability to cache VHDX files together with their configuration
- This should roughly cut the build time in half as long as there are no new patches released since the last cached VHDX file
2024-09-30 15:44:40 +02:00
JonasKloseBW d45b6dc8dc Update BuildFFUVM.ps1
- improve Optimize-FFUCaptureDrive
- use the drive letter that the Windows partition gets after mounting the vhdx file
2024-09-30 14:14:33 +02:00
JonasKloseBW 5194133a78 Update BuildFFUVM.ps1
- Add help for parameter AppsScriptVariables
2024-09-27 18:34:41 +02:00
JonasKloseBW 6e5d634af6 Update BuildFFUVM.ps1
- Bypass VMSwitchIPAddress to VMHostAddress check on systems with a configured NAT setup
2024-09-27 02:15:25 +02:00
JonasKloseBW 32d5ff3b47 Merge branch 'rbalsleyMSFT:2410.1' into 2410.1 2024-09-26 20:11:45 +02:00
JonasKloseBW 5545554d7e Update BuildFFUVM.ps1
- Fix accidental variable accumulation in InstallAppsandSysprep.cmd
2024-09-26 20:08:47 +02:00
JonasKloseBW 6a0faa958e Update InstallAppsandSysprep.cmd 2/2
- Add parameter $AppsScriptVariablesvariable
- Allow value based behavior changes during the app install phase in InstallAppsandSysprep.cmd
2024-09-26 17:16:22 +02:00
JonasKloseBW 15c0478710 Update BuildFFUVM.ps1 1/2
- Add parameter $AppsScriptVariablesvariable
- Allow value based behavior changes during the app install phase in InstallAppsandSysprep.cmd
2024-09-26 17:15:17 +02:00
rbalsleyMSFT bab9804022 Merge pull request #79 from zehadialam/2410.1
Include Latest Windows Malicious Software Removal Tool
2024-09-24 14:28:23 -07:00
Zehadi Alam c93c417dba Removed Get-MSRTUrl function and use Microsoft Update Catalog to retrieve latest MSRT 2024-09-23 23:42:09 -04:00
JonasKloseBW 2fe91de000 Merge branch 'main' into 2410.1 2024-09-23 11:27:52 +02:00
JonasKloseBW 412e3a078c Update CaptureFFU.ps1
- Add Windows Server support like in BuildFFUVM.ps1
2024-09-23 04:37:09 +02:00
JonasKloseBW b6dda55a82 Update BuildFFUVM.ps1
- Updated the .NET Framework download code
- Download the appropriate .NET Framework for Windows Server
2024-09-23 04:29:04 +02:00
JonasKloseBW b8bda93e8d Update BuildFFUVM.ps1
- Updated MU Catalog search
- Search now includes searching for Windows Server updates

Has been verified to work for Windows Server 2022 and Windows Server 2025 only.
2024-09-23 04:26:30 +02:00
JonasKloseBW ac485f9c87 Update BuildFFUVM.ps1
- Update Get-WindowsVersionInfo
- Server Standard is now called 'Srv_Std'
- Server Datacenter is now called 'Srv_Dtc'
- 17763 is matched as Windows Server 2019
- 20348 is matched as Windows Server 2022
- 26100 is matched as Windows Server 2025
- other versions are matched as $DisplayVersion as a fallback

Please remember to update the Windows Server version of 2025 in case it changes until release.
2024-09-23 04:16:57 +02:00
JonasKloseBW 4b33627d19 Update BuildFFUVM.ps1
- Fix bug in New-OSPartition
- CompactOS is now avoided on Windows Server
2024-09-23 04:11:15 +02:00
JonasKloseBW af28624e2d Update BuildFFUVM.ps1
- Update Get-Index to support processing Windows Server images
2024-09-23 04:08:01 +02:00
JonasKloseBW 3457aedf5d Update BuildFFUVM.ps1
- Add support for searching Windows Server SKUs in images in Get-WimIndex
2024-09-23 04:04:13 +02:00
JonasKloseBW 5acac4ba5b Update BuildFFUVM.ps1
- Update Get-LatestWindowsKB to support searching for Windows Server updates
- Improve the HTML regex to return a more precise match by using the $WindowsRelease variable for lookbehind based searching
2024-09-23 04:02:28 +02:00
JonasKloseBW 0b151f9054 Update BuildFFUVM.ps1
- Throw error when trying to download Windows Server as it's not possible
2024-09-23 03:55:37 +02:00
JonasKloseBW cafc45dbba Update BuildFFUVM.ps1
- Add Dell driver download support for Windows Server 2016, 2022 and fall back to 2022 drivers for other Server versions.
2024-09-23 03:53:45 +02:00
JonasKloseBW e3cbcab6b2 Update BuildFFUVM.ps1
- Add script parameters to allow Windows Server image creation
2024-09-23 03:41:40 +02:00
Zehadi Alam ec7e9a546c Adjust pattern for URL scraping 2024-09-17 21:08:09 -04:00
Zehadi Alam 7a2aab3204 Adjust install command for MSRT 2024-09-17 20:36:22 -04:00
rbalsleyMSFT 5dcdd2c36f Clean up the Get-MicrosoftDrivers old code and version change 2024-09-17 14:03:43 -07:00
rbalsleyMSFT fb0a630bfd Merge pull request #78 from rbalsleyMSFT/main
Merge main to 2410.1
2024-09-17 13:58:55 -07:00
rbalsleyMSFT 47cd0deb03 Merge pull request #77 from rbalsleyMSFT/2409.2
2409.2
2024-09-17 13:54:58 -07:00
rbalsleyMSFT 378941cd5c Two fixes
- Fixed an issue with UWP apps downloaded via Winget weren't being fully installed. Added /StubPackageOption:installfull to the dism command
- Fixed an issue with Surface Drivers not downloading
2024-09-17 13:50:16 -07:00
Zehadi Alam 1da28024cc Add support for updating latest Windows Malicious Software Removal Tool 2024-09-15 00:41:04 -04:00
rbalsleyMSFT 480e6a62b6 Merge pull request #74 from w0/main
Added support for relative paths
2024-09-12 12:32:22 -07:00
w0 807456de86 removed unused variable. more format. 2024-09-12 13:57:58 -05:00
w0 c8ef42ab21 Update USBImagingToolCreator.ps1
added support for relative paths. formatted script and removed functions from inside if statement.
2024-09-12 13:54:58 -05:00
rbalsleyMSFT 60d147c71d Update README.md 2024-09-10 11:45:44 -07:00
rbalsleyMSFT cd36150ddc Merge pull request #71 from rbalsleyMSFT/2409.1
2409.1
2024-09-10 11:44:49 -07:00
rbalsleyMSFT 198a544dbb update change log 2024-09-10 11:44:12 -07:00
rbalsleyMSFT 40616776eb - Added VMHostIPAddress and VMSwitchName validation to validate the IP address matches the VMSwitchName 2024-09-07 09:53:14 -07:00
rbalsleyMSFT 20c9cf8ab3 Merge pull request #65 from HedgeComp/Time-to-Complete-Hours
Time To Complete Shows Hours if Needed
2024-09-06 11:26:54 -07:00
rbalsleyMSFT 17558f86aa Merge pull request #64 from HedgeComp/OneDrive-Silent
Silently Install Onedrive
2024-09-06 11:26:15 -07:00
rbalsleyMSFT 7408dbb435 Merge branch '2409.1' of https://github.com/rbalsleyMSFT/FFU into 2409.1 2024-09-06 11:24:21 -07:00
rbalsleyMSFT 9c1fc59af9 - Added new variables for the PPKG, Unattend, Autopilot, and PEDrivers validation 2024-09-06 11:24:16 -07:00
HedgeComp 31c785b5da Update BuildFFUVM.ps1
Add Hours to Total Time to complete only if greater than 1 hour
2024-09-06 13:00:09 -04:00
HedgeComp 5b93135ebb Update BuildFFUVM.ps1
Add Silent switch install to Onedrivesetup.exe
2024-09-06 12:57:36 -04:00
rbalsleyMSFT 5f4cf0c66e Merge pull request #60 from zehadialam/2409.1
Fix for Missing Windows Boot Manager Entry
2024-09-06 09:34:11 -07:00
Zehadi Alam ddbf2b0339 Use bcdedit to set Windows Boot Manager and default Windows boot loader to be first in the display order of UEFI firmware 2024-09-05 17:57:35 -04:00
rbalsleyMSFT e62d481405 - Remove ValidateScript on InstallDrivers and break it out in a validation block so -Make and -Model can be specified anywhere in the command line
- Check for Prefixes.txt file and copy to the USB drive if it exists
- Perform better validation for PPKG, Unattend, Autopilot json, and drivers
- Comment out the Windows Security Platform update as the file has been removed from the MU Catalog.
2024-09-04 17:05:06 -07:00
rbalsleyMSFT 6c07ac8595 Merge branch '2409.1' of https://github.com/rbalsleyMSFT/FFU into 2409.1 2024-09-04 13:20:38 -07:00
rbalsleyMSFT 7d74feec0c - Fix an issue with removal of Defender/OneDrive/Edge after FFU is complete
- Migrate Winget downloads to use Export-WingetPackage cmdlet as per issue Known Issue: Winget downloads fail on Non-English OS #50
- Add better logging when unable to find HDD when applying FFU
2024-09-04 13:20:35 -07:00
rbalsleyMSFT 6b2a4bcb27 Merge pull request #51 from HedgeComp/2408.2
Allow Preview CU update
2024-09-04 13:08:58 -07:00
rbalsleyMSFT 6da9ece0d8 Merge pull request #54 from w0/main
use ValidateSet for WindowsSKU
2024-08-15 18:59:01 -07:00
w0 dc4438dcf9 use ValidateSet for WindowsSKU 2024-08-15 20:11:41 -05:00
rbalsleyMSFT e250e2a130 Changed some logging when winget apps can't be found 2024-08-13 16:11:36 -07:00
Doctair dad51fdf80 updated Docs to relect new $UpdatePreviewCU Parameter 2024-08-12 13:15:16 -04:00
Doctair d60b0301c5 Remove a temp copy of BUildScript 2024-08-12 10:15:45 -04:00
Doctair db3e09650a Add new Parameter for Installing Preview CU from
Mircosoft Update Catalog. Recent Windows Pro
not Auto Activating to Enterprise License Bug speared
this idea as its resoleve in lastes Prieview CU.

Parameter is default $False but if set to $true will install
Preivew CU and take precendence over $UpdateLastestCU.
2024-08-12 10:07:41 -04:00
rbalsleyMSFT bcb9911cd0 Small update to fix a logging issue with script run time duration 2024-08-07 13:01:26 -07:00
rbalsleyMSFT 67e3035c38 Merge pull request #47 from rbalsleyMSFT/2408.1
2408.1
2024-08-06 17:57:42 -07:00
rbalsleyMSFT 94dd256889 update docs 2024-08-06 17:55:17 -07:00
rbalsleyMSFT cc383c84cb update docs 2024-08-06 17:51:53 -07:00
rbalsleyMSFT b20b614f5e update changelog.md and ApplyFFU.ps1 to 2408.1 2024-08-06 17:39:13 -07:00
rbalsleyMSFT 31984e104e update readme.md 2024-08-06 15:52:35 -07:00
rbalsleyMSFT 94f74a194d update readme 2024-08-06 15:50:32 -07:00
rbalsleyMSFT eaa58e6804 update readme 2024-08-06 15:49:26 -07:00
rbalsleyMSFT 351c87ab96 update readme 2024-08-06 15:47:49 -07:00
rbalsleyMSFT 13e2765c3f update readme 2024-08-06 15:46:42 -07:00
rbalsleyMSFT c049840baa update readme.md 2024-08-06 15:45:44 -07:00
rbalsleyMSFT e1ab74e5a3 Refactored Get-USBDrives and New-DeploymentUSB to use tables when displaying multiple drives or FFUs to the user. Fixed an issue with cleaning up InstallAppsandSysprep.cmd. Re-wrote Readme.md. 2024-08-06 15:43:23 -07:00
rbalsleyMSFT 02d858f27f Merge pull request #45 from HedgeComp/2408.1
2408.1
2024-08-02 11:57:23 -07:00
rbalsleyMSFT 1d8e9f352d Merge branch '2408.1' of https://github.com/rbalsleyMSFT/FFU into 2408.1 2024-08-02 10:54:14 -07:00
rbalsleyMSFT 7b59e3d0ec Fixed an issue with clean up of Defender, OneDrive, and Edge. Fixed an issue with the formatting of InstallAppsandSysprep.cmd file. 2024-08-02 10:53:50 -07:00
Doctair 213da61389 Fix REad-Host Prompt message for FFU selecion 2024-08-02 12:20:17 -04:00
Doctair 9de55eb186 Comment Better 2024-08-02 12:11:54 -04:00
Doctair e3bec5ff45 Clean up some Remmed Lines and Added some Comments
for explanation
2024-08-02 12:02:58 -04:00
Doctair 1bfc4735d3 Sync arm64 changes and Rem $Capture False 2024-08-02 11:52:51 -04:00
Doctair 49b742b47b Added Logic for FFU Selection to Check for Integer
or 'A'

Fix Write Message to Display all FFUs found if not
using -Verbose, currently displays nothing so numbering
of FFUs is unkown for selection prompt

Added Simple total Runtime and Start and Finish Time
2024-08-02 11:35:18 -04:00
rbalsleyMSFT 7f79e50f72 Merge pull request #44 from HedgeComp/2408.1
External Disk Selection
2024-07-31 10:56:55 -07:00
Doctair 39b9d06d21 Correct Logic for Dirve letters and add Dirve Info
to the write-host selection for Disks.
2024-07-30 16:11:07 -04:00
Doctair 047881934a Add Logic to catch the disk selection of USB
External drives so that it will not accept a Letter
2024-07-30 13:57:54 -04:00
rbalsleyMSFT 689808eca7 commented some variables 2024-07-26 16:22:31 -07:00
rbalsleyMSFT 70571a3b49 Fixed an issue where Efisys_noprompt.bin was being used for deployment media. This should only be used for capture media 2024-07-26 16:04:53 -07:00
rbalsleyMSFT 06138ebaff Added $PromptExternalHardDiskMedia as a new variable defaulted to True to prevent accidentental data loss if external hard disks are detected 2024-07-24 17:16:30 -07:00
rbalsleyMSFT 81a3b10a06 added $AllowExternalHardDiskMedia variable and modified USB drive creation code to allow for External hard disks 2024-07-24 10:37:41 -07:00
rbalsleyMSFT 8ba88f4626 Fixed an issue with RAW disks and Clear-Disk 2024-07-23 16:31:16 -07:00
rbalsleyMSFT 9d4b66851a Merge pull request #42 from rbalsleyMSFT/2407.2
Fixed Lenovo driver download issues
2024-07-23 12:41:32 -07:00
rbalsleyMSFT 3ba0da19f8 Fixed Lenovo driver download issues 2024-07-23 12:38:17 -07:00
rbalsleyMSFT 6826f854ae updated drivers info 2024-07-22 14:45:35 -07:00
rbalsleyMSFT afa524091c adding authentication info when downloading store apps via Winget 2024-07-22 14:42:41 -07:00
rbalsleyMSFT f14c7f2b00 Update README.md 2024-07-22 14:19:24 -07:00
rbalsleyMSFT e1aac7ba9d Merge pull request #40 from rbalsleyMSFT/2407.1
2407.1
2024-07-22 13:48:34 -07:00
rbalsleyMSFT 2e7ab9a052 change log updates for 2407.1 2024-07-22 13:46:53 -07:00
rbalsleyMSFT 0a9de96d03 Potential path fix for downloading defender defs for ARM64 2024-07-22 10:55:24 -07:00
rbalsleyMSFT 50c61dd328 Added AppList_InboxAppsSample.json 2024-07-22 10:34:33 -07:00
rbalsleyMSFT 39a919bada Changed AppList.json file name to AppList_Sample.json to not force the sample apps to install automatically. 2024-07-22 10:31:53 -07:00
rbalsleyMSFT 5600b2fbbd fixed issues creating deploy media for VMs, cleaned up some old commented code in ApplyFFU.ps1 2024-07-19 19:17:02 -07:00
rbalsleyMSFT 1d26781dc1 Merge branch '2407.1' of https://github.com/rbalsleyMSFT/FFU into 2407.1 2024-07-18 21:35:18 -07:00
rbalsleyMSFT f29e3c4349 added additional winget app logging, removed static linenumber references 2024-07-18 21:35:15 -07:00
rbalsleyMSFT bd8d0efd66 Merge pull request #39 from zehadialam/minor-fixes
Minor Fixes
2024-07-17 14:27:09 -07:00
Zehadi Alam 5616082275 Add conditional check to not run clear-disk on new drives that are uninitialized 2024-07-17 00:14:00 -04:00
Zehadi Alam 8100df3d24 Add diskpart commands to assign GPT attributes to recovery partition 2024-07-16 23:43:35 -04:00
rbalsleyMSFT a5c38fd09b modified VM deployment iso cmd scripts and fixed appx license file issue 2024-07-16 18:48:43 -07:00
rbalsleyMSFT 88ef8f70a1 Merge branch '2407.1' of https://github.com/rbalsleyMSFT/FFU into 2407.1 2024-07-15 22:55:33 -07:00
rbalsleyMSFT 74fd71161b Added files for VM Deployment for testing 2024-07-15 22:55:24 -07:00
rbalsleyMSFT 8aa79dd134 Merge branch '2407.1' of https://github.com/rbalsleyMSFT/FFU into 2407.1 2024-07-15 09:40:16 -07:00
rbalsleyMSFT e4e499e796 create unattend files for x64 and arm64 2024-07-15 09:36:14 -07:00
rbalsleyMSFT f6c3c0b6c3 create unattend files for x64 and arm64 depending on windows architecture 2024-07-13 08:38:09 -07:00
rbalsleyMSFT 60ac2e4af0 Merge pull request #38 from zehadialam/feature-app-download-automation
Improve WinGet Integration for Automated App Downloads and Installs
2024-07-12 22:49:22 -07:00
Zehadi Alam ddf9c1f986 Fix Get-Apps function parameter name 2024-07-12 23:46:01 -04:00
Zehadi Alam ab58b27a1d Removed checking of appId using result parsing, since appId is already a parameter, added command to remove unprovisioned Notepad++ package, which breaks Sysprep 2024-07-12 23:40:59 -04:00
Zehadi Alam 191c30dd65 Remove New-WinGetSettings, since stable release of WinGet now supports MSStore app downloads 2024-07-12 22:43:10 -04:00
Zehadi Alam 1a444d8e0f Refactor Install-WinGet function and add architecture parameter and stable WinGet download, modify Confirm-WinGetInstallation to check for stable release, refactor Get-WinGetApp for improved maintainability and readability 2024-07-12 22:40:43 -04:00
Zehadi Alam f7f52903a4 Refactor Get-StoreApp for improved readability 2024-07-12 00:38:48 -04:00
Zehadi Alam a9afba9185 Refactor Install-WinGet and New-WinGetSettings for improved readability 2024-07-11 23:32:58 -04:00
rbalsleyMSFT ecf3794f92 Merge pull request #37 from MKellyCBSD/USBImagingToolCreator.ps1
Update to USBImagingToolCreator.ps1
2024-07-10 12:56:28 -07:00
Mike Kelly c0bcfd8bf2 Update USBImagingToolCreator.ps1
Changed the way the DisableAutoplay switch runs. Now it only changes the registry setting if it the current value is set to "0". then it restores the setting back to the previous value
2024-07-10 15:52:09 -04:00
rbalsleyMSFT 98babb3ad2 Merge pull request #36 from MKellyCBSD/USBImagingToolCreator.ps1
Feature Proposal: USBImagingToolCreator.ps1
2024-07-10 09:45:51 -07:00
Mike Kelly ab0b7f67ec Update USBImagingToolCreator.ps1 2024-07-09 15:40:22 -04:00
Mike Kelly 6d85e3ef62 Rename BuildUSBDrives.ps1 to USBImagingToolCreator.ps1
Update Script name
2024-07-09 15:38:33 -04:00
rbalsleyMSFT a91a417a08 Merge pull request #35 from mhaley/patch-1
Assign drive letter to recovery partition before modification
2024-07-09 09:28:30 -07:00
Mike Kelly 67cc8c1225 Update BuildUSBDrives.ps1
Update code
2024-07-09 11:29:58 -04:00
Matthew Haley 3f0377fbf9 Assign drive letter to recovery partition before modification
Assign drive letter so copy succeeds, remove when finished.
2024-07-08 13:45:44 -07:00
Zehadi Alam 325413de13 Moved code into separate functions, refactored existing functions, fixed logical errors in if-statements 2024-07-07 22:27:27 -04:00
Mike Kelly 174c16ecb6 Add files via upload
Upload BuildUSBDrives.ps1
2024-07-05 11:48:39 -04:00
Zehadi Alam 146c1601bd Improved handling of store apps that can't be downloaded 2024-07-05 00:01:06 -04:00
Zehadi Alam 8c897e93fe Added support for AppList to be in JSON format. WinGet searches now use app ID. Modified InstallAppsandSysprep.cmd to handle packages with no dependencies 2024-07-04 22:57:14 -04:00
rbalsleyMSFT 2423814cc2 changed unattend files for naming the PC for arm64 and modified arm64 store app download logic for dependency handling 2024-07-03 16:30:12 -07:00
rbalsleyMSFT c39c30c970 Merge pull request #34 from zehadialam/feature-app-download-automation
Handle Win32 Apps of MSStore Type & Update InstallAppsandSysprep.cmd
2024-07-02 19:04:14 -07:00
Zehadi Alam 95a4664b26 Added handling of win32 apps using the msstore source in winget download command, updated InstallAppsandSysprep.cmd file to use store app license files, and updated AppList.txt with winget prefix, instead of win32 2024-07-02 20:45:14 -04:00
rbalsleyMSFT 0e6d65bf2f Merge pull request #33 from zehadialam/feature-app-download-automation
WinGet Download Architecture Handling
2024-07-01 22:19:10 -07:00
Zehadi Alam 0010c8ad81 Fix typo 2024-07-01 22:39:43 -04:00
Zehadi Alam d16acce0ab Add condition to use winget download command without architecture parameter if specifying it leads to no app result 2024-07-01 22:34:03 -04:00
rbalsleyMSFT dd20eceb55 comment change 2024-07-01 16:23:04 -07:00
rbalsleyMSFT c30aa90e8b added architecture and other minor changes for winget app downloads 2024-07-01 16:22:56 -07:00
rbalsleyMSFT bd4e3a1913 Merge branch '2407.1' of https://github.com/rbalsleyMSFT/FFU into 2407.1 2024-07-01 10:09:34 -07:00
rbalsleyMSFT 7a0dd3435c Fixed a logic issue when downloading ARM KBs 2024-07-01 10:09:11 -07:00
rbalsleyMSFT df33e89e37 Merge pull request #32 from zehadialam/feature-app-download-automation
Automated Win32 and Store App Downloads and Installs
2024-07-01 09:33:34 -07:00
Zehadi Alam 4af808c939 Invoke Get-Apps function with AppList.txt argument 2024-06-30 20:15:13 -04:00
Zehadi Alam 205b58aaa7 Added cleanup of Win32 and MSStore folders 2024-06-30 19:45:43 -04:00
Zehadi Alam 1729eaddd6 Add AppsList, modified InstallAppsandSysprep.cmd file with store apps installation, and updated Clear-InstallAppsandSysprep function 2024-06-30 19:42:06 -04:00
Zehadi Alam cafff0b484 Add Get-Apps function 2024-06-30 19:34:03 -04:00
Zehadi Alam 5c77b171f1 Add Get-StoreApp function 2024-06-30 19:33:26 -04:00
Zehadi Alam 36f5350f12 Add Get-Win32App function 2024-06-30 19:32:54 -04:00
Zehadi Alam 45a2c0c29d Add New-WinGetSettings function 2024-06-30 19:32:18 -04:00
Zehadi Alam 6cfe41f963 Add Install-WinGet function 2024-06-30 19:31:40 -04:00
rbalsleyMSFT 3d13774ee4 initial arm64 support 2024-06-29 21:28:33 -07:00
rbalsleyMSFT 21c5fa931f Merge pull request #28 from rbalsleyMSFT/2406.1
2406.1
2024-06-19 13:41:16 -07:00
rbalsleyMSFT 7214b1b8c2 docs 2024-06-19 13:39:45 -07:00
rbalsleyMSFT ef24b59a0c docs 2024-06-19 13:38:55 -07:00
rbalsleyMSFT 910918b421 docs 2024-06-19 13:36:41 -07:00
rbalsleyMSFT 30685dbced docs 2024-06-19 13:32:57 -07:00
rbalsleyMSFT 7c8e09d4e8 docs 2024-06-19 13:01:40 -07:00
rbalsleyMSFT e04ed8cf1c docs 2024-06-19 12:59:00 -07:00
rbalsleyMSFT c8362d972b docs 2024-06-19 12:58:18 -07:00
rbalsleyMSFT 32ae035d49 Merge branch '2406.1' of https://github.com/rbalsleyMSFT/FFU into 2406.1 2024-06-19 12:57:33 -07:00
rbalsleyMSFT d74f8451d5 docs 2024-06-19 12:57:21 -07:00
rbalsleyMSFT 8ee7cf022c Delete Test 2024-06-19 12:50:15 -07:00
rbalsleyMSFT f931a75636 Create Test 2024-06-19 12:49:06 -07:00
rbalsleyMSFT 63617897f3 docs 2024-06-19 12:44:25 -07:00
rbalsleyMSFT f10433d575 docs 2024-06-19 12:43:21 -07:00
rbalsleyMSFT fd951ea52d docs 2024-06-19 12:38:01 -07:00
rbalsleyMSFT 6eca29a506 Docs 2024-06-19 11:07:27 -07:00
rbalsleyMSFT 181eed8f12 docs 2024-06-17 14:40:55 -07:00
rbalsleyMSFT 08e354ad17 updated version in applyffu.ps1 2024-06-17 12:08:01 -07:00
rbalsleyMSFT 21ebbdf9c9 initial commit 2024-06-17 12:05:56 -07:00
rbalsleyMSFT e8ba334732 Merge pull request #26 from rbalsleyMSFT/2405.1
2405.1
2024-05-24 18:24:07 -07:00
rbalsleyMSFT ff46c10d79 Update README.md 2024-05-24 18:22:21 -07:00
rbalsleyMSFT 4d9e1c1f88 Update README.md 2024-05-24 18:21:08 -07:00
rbalsleyMSFT d5b81bc482 Update README.md 2024-05-24 18:18:39 -07:00
rbalsleyMSFT 8f81e69159 Modify Resetbase command to use DandIEnv 2024-05-24 18:12:43 -07:00
rbalsleyMSFT 4932777f4f syntax cleanup 2024-05-24 18:06:06 -07:00
rbalsleyMSFT 12edabf213 Changed version to 2405.1 2024-05-24 15:29:05 -07:00
rbalsleyMSFT 1978736133 Merge pull request #25 from zehadialam/feature-reduce-ffu-size
Reducing FFU Size
2024-05-24 15:20:14 -07:00
rbalsleyMSFT a3faa89ada Merge branch '2405.1' into feature-reduce-ffu-size 2024-05-24 15:19:31 -07:00
rbalsleyMSFT fc8648eb65 Merge pull request #24 from MKellyCBSD/Move-location-of-Clean-Up-the-WinSxS-Folder-command
Move location of command that cleans up the WinSXS folder
2024-05-24 15:15:31 -07:00
Zehadi Alam 49a9fd49c1 Add Optimize-FFUCaptureDrive function and disk cleanup to InstallAppsandSysprep.cmd file 2024-05-20 20:09:26 -04:00
MKellyCBSD 3f4836b478 Update BuildFFUVM.ps1
Adding "-PreventPending" to the "add-windowspackage" command allows the dism cleanup of the winsxs folder command to be moved from the InstallAppsandSysprep.cmd script to right after the updates are added to the vhdx. The end result: 

FFU image size before: 11.836GB
FFU image size after: 11.190GB
2024-05-15 15:52:38 -04:00
MKellyCBSD 1921809c30 Update InstallAppsandSysprep.cmd
Remove this command because moving it up right after updates are applied to the scratch VHDX saves ~650MB on the final .ffu file.
2024-05-15 15:42:00 -04:00
rbalsleyMSFT 56f3e9d856 Update README.md 2024-05-03 15:35:31 -07:00
rbalsleyMSFT ae59183a19 update issue with downloading latest Windows CU 2024-05-03 15:32:11 -07:00
32 changed files with 4671 additions and 704 deletions
+484
View File
@@ -0,0 +1,484 @@
# Change Log
# 2412.1
This is a major release with a number of quality-of-life improvements that will reduce the time it takes to create FFUs. I highly recommend you update to this release.
## Windows Server Support
Thanks to [JonasKloseBW](https://github.com/JonasKloseBW) we have added support for Windows Server! This includes support for Windows Server 2016 through 2025 and supports both core and desktop experience. It will require you to provide your own Server ISO
using the `-ISOPath` parameter since we can't automatically download it like we can with client. You also will want to set the `-WindowsSKU` parameter to either `'Standard', 'Datacenter', 'Standard (Desktop Experience)', or 'Datacenter (Desktop Experience)'` depending on your needs.
Cumulative Updates for Windows and .NET should work as expected. Defender updates should work too. If you notice anything that doesn't work, open an issue.
## VHDX Caching Support
Thanks again to Jonas for adding VHDX caching support #89. For those of you that might be making many FFUs for different configurations, instead of building the VHDX every time, you can cache the VHDX and re-use it for your next build. In testing, this seems to save about 10 minutes, depending on how you're installing Windows (via MCT download, or your own ISO and how old your media is).
The way this works is a VHDXCache folder is created in the FFUDevelopment folder. If `-AllowVHDXCaching $true`, we store the VHDX file and a config file that keeps track of the following info
```
{
"VhdxFileName": "_FFU-808829869.vhdx",
"LogicalSectorSizeBytes": 512,
"WindowsSKU": "Pro",
"WindowsRelease": "11",
"WindowsVersion": "24H2",
"OptionalFeatures": "",
"IncludedUpdates": [
{
"Name": "windows11.0-kb5043080-x64_953449672073f8fb99badb4cc6d5d7849b9c83e8.msu"
},
{
"Name": "windows11.0-kb5045934-x64-ndp481_fa9c3adfb0532eb8f4e521f4fb92a179380184c5.msu"
},
{
"Name": "windows11.0-kb5048667-x64_d4ad0ca69de9a02bc356757581e0e0d6960c9f93.msu"
}
]
}
```
The VHDX files are cached before boot, so they've never been sysprepped. On subsequent runs, if `-AllowVHDXCaching $true` is set, we search the VHDXCache folder, loop through any config files, and look to see if we find one that matches the build information you've passed to the script. If a match is found, robocopy copies in the VHDX and uses the cached VHDX to build the FFU VM.
## Configuration File Support
A configuration file can now be used to configure the parameters in lieu of, or in conjunction with, parameters specified on the command line. Configuration files are especially helpful for those making FFUs for different models, Windows releases, application sets, and more.
To use, run:
`.\BuildFFUVM.ps1 -ConfigFile 'C:\FFUDevelopment\config\Sample_default.json' -verbose`
### Creating your own Configuration Json file
If you have a command line that youve been using for awhile and would like to convert it to a json file automatically, run your command line like normal, adding
`-exportConfigFile 'C:\FFUDevelopment\config\YourConfigFile.json'`
to the end of the command. Doing this will generate a well-formatted json file with your configuration settings.
You can also temporarily overwrite parameters while using a config file. Using the following sample command:
`.\BuildFFUVM.ps1 -ConfigFile 'C:\FFUDevelopment\config\Sample_default.json' -verbose`
If youd like to not include Office (the Sample_default.json file installs Office), youd add `-InstallOffice $False` to the command line
`.\BuildFFUVM.ps1 -ConfigFile 'C:\FFUDevelopment\config\Sample_default.json' -verbose -InstallOffice $False`
Doing this will temporarily overwrite whatever is in the json for the `InstallOffice` parameter. It will not modify the json file. If you would like to change the json file, you can add `-exportConfigFile 'C:\FFUDevelopment\config\Sample_default.json'` and that will overwrite the json file with the new parameter.
`.\BuildFFUVM.ps1 -ConfigFile 'C:\FFUDevelopment\config\Sample_default.json' -verbose -InstallOffice $False -exportConfigFile 'C:\FFUDevelopment\config\Sample_default.json'`
## Custom FFU Naming Support
Thanks to Jonas, we now have custom FFU naming support. A new parameter -CustomFFUNameTemplate has been added.
This parameter sets a custom FFU output name with placeholders. Allowed placeholders are:
`{WindowsRelease}, {WindowsVersion}, {SKU}, {BuildDate}, {yyyy}, {MM}, {dd}, {H}, {hh}, {mm}, {tt}`
And below is a description of what to expect when you use each placeholder.
```
{WindowsRelease} = 10, 11, 2016, 2019, 2022, 2025
{WindowsVersion} = 1607, 1809, 21h2, 22h2, 23h2, 24h2, etc
{SKU} = Home, Home N, Home Single Language, Education, Education N, Pro, Pro N, Pro Education, Pro Education N, Pro for Workstations, Pro N for Workstations, Enterprise, Enterprise N, Standard, Standard (Desktop Experience), Datacenter, Datacenter (Desktop Experience)
{BuildDate} = e.g. Dec2024
{yyyy} = e.g. 2024
{MM} = 2 digit month format (e.g. 12 for December)
{dd} = Day of the month in 2 digit format (19)
{HH} = Current hour in 24-hour format (e.g., 14 for 2 PM)
{hh} = Current hour in 12-hour format (e.g., 02 for 2 PM)
{mm} = Current minute in 2-digit format (e.g., 09)
{tt} = Current AM/PM designator (e.g., AM or PM)
```
An example for Windows 11 24h2 Pro built today would be:
`-CustomFFUNameTemplate '{WindowsRelease}_{WindowsVersion}_{SKU}_{yyyy}-{MM}-{dd}_{HH}{mm}'`
Would result in a FFU file name of:
`Win11_24h2_Pro_2024-12-20_1225.ffu`
You can also mix in static text in the name
`-CustomFFUNameTemplate '{WindowsRelease}_{WindowsVersion}_{SKU}_Office_{yyyy}-{MM}-{dd}_{HH}{mm}'`
Would result in:
`Win11_24h2_Pro_Office_2024-12-20_1225.ffu`
## Additional PRs added
#79 Includes the latest Microsoft Software Removal Tool from `@zehadialam` - use `-UpdateLatestMSRT $true`
#72 Includes some Unattend Sample files from @HedgeComp in the `$FFUDevelopment\Unattend` folder
#74 Includes some improvements to the USBImagingToolCreator.ps1 file from @w0
#103 Includes some additional improvements to the USBImagingToolCreator.ps1 file from @MKellyCBSD
## Misc Fixes
- Added server skus to validateset for $WindowsSKU
- Added new variable $installationType which uses $WindowsRelease to determine Server or Client. If $installationType is Server, $WindowsRelease version is used to set $WindowsVersion to the appropriate version (1607, 1809, 21H2)
- Fixed an issue where the recovery partition wouldn't be created on server OSes due to winre.wim being hidden. Never saw this on client OSes even though it also was hidden IIRC.
- Removed verbosity for Optimize-Volume as it was outputting when -verbose was not specified.
- Modified some search strings for .NET CUs when installing on Server OS
- Included SSU for Windows Server 2016 as it's mandatory
- Added some error checking for Server 2019 and 2022 CU installations when CU fails due to lack of SSU. If you run into this error, you're using old media and should use the latest. Always use the latest ISO if you can.
- $WindowsVersion set to 24h2, can override by using -WindowsVersion 23H2 if you want the old behavior
- Removed the "Downloading information GUID" messages when downloading content from the Microsoft Update Catalog while -verbose was specified in the command line
- In Get-KBLink, made a change to just grab the first result returned instead of the entire results page. This removes the need to use a break statement in Save-KB when downloading updates. This fixed an issue with the new 24H2 Checkpoint Cumulative Updates in Win11 and Server 2025.
- Changed Windows MSRT search string for x64 and x86. This was mainly to get x64 to the top of the search results. x86 won't actually download since the code isn't in place for content from the MU Catalog to download x86 content (no idea if anyone actually builds x86 FFUs for Win10 - I hope not)
- If not passing an ISO, hardcoded WindowsVersion of 22H2 for Windows 10 or 24H2 for Windows 11 since the ESD media only provides those two versions. Not doing this allowed for unnecessary VHDX creation since it checks the WindowsVersion via the json file. This also fixes an issue where CUs could be searched for that didnt exist, but the media would still download
- Added some additional logging entries
- Removed verbose output of the Optimize-Volume command
- Fixed an issue where not passing an ISO caused the script to fail
- Cleaned up the KBPath folder at the end of the run
- Changed some minor formatting items
- Added Get-Childprocesses function to return child processes of parent process
- Added a new -Wait boolean parameter to Invoke-Process function. This is to control whether Invoke-Process should wait in order to track stdout and stderr output. This is needed for processes that may hang, waiting for user input and there isn't a way to bypass (some Intel drivers provided by Dell leave dialog windows even when running silently)
-Invoke-Process now returns process information (returns $cmd). This allows for process tracking when calling the function.
- Since Invoke-Process now returns the process information, also needed to add Out-Null to the majority of the Invoke-Process references to prevent Invoke-Process from writing to the terminal
- Refactored a lot of the Get-DellDrivers function due to inconsistencies with how driver extraction behaves between client and server devices. For client, /s /e seemed to work fine, but for server it would only extract the driver installer content and other dell related files, rather than the driver files themselves. We have since switched to using /s /drivers= which will extract the driver content. Not all drivers support /drivers= and may output some information to the terminal that looks like help documentation. If a driver doesn't support /drivers, the script falls back to using /s /e to do the extraction. If this doesn't work for you, you can always provide your own drivers that you manually download from Dell's website.
- Updated Malicious Software Removal Tool (MSRT) code to handle Windows Server
- Refactored the Get-ODTURL function to fix recent download issues. Also added some better error handling
- Moved the odtsetup.exe download to the FFUDevelopment folder and will clean it up after office has downloaded
Updated parameter definition block to be alphabetized (not to be confused by the param block, which is not alphabetized)
- Added $PEDriversFolder script variable to the param block (for some reason it was missing)
- Added ConfigFile and ExportConfigFile parameters to support json config files
- Changed Version to 2412.1
- Modified vhdxCacheItem class to include $LogicalSectorSizeBytes
- Added new function Get-Parameters to help with new config and export config file functionality
- Fixed Get-MicrosoftDrivers function to not require the HTMLFILE COM object, which isn't available in Windows 11. It seems to be installed with Office, which is what was allowing downloads to work and masked the issue.
- Added long path support to prevent issues with oscdimg creating the Apps.iso.
- Fixed an issue where the $PEDriversFolder variable wasn't being used (instead $FFUDevelopment\PEDrivers was used)
- Created a new function New-FFUFileName - this works in conjunction with the new $CustomFFUNameTemplate. The function was needed to support both scenarios where $InstallApps is either $true or $false.
- Added new function Export-ConfigFile. When passing -ExportConfigFile 'Path\To\ConfigFile.json' the script will generate a parameter dump of all of the configured parameters
- Added driver folder validation to throw an error if spaces are detected in the folder name of the drivers folder (e.g. C:\FFUDevelopment\Drivers\Dell 3190). This is due to an issue with Dell drivers and their inability to handle paths with spaces consistently.
- Added back the Windows Security Platform update which grabs it from the web instead of the Microsoft update catalog
- Fixed an issue where the Drivers folder was being completely deleted instead of its sub-folders
- Removed the Requires -PSEdition Desktop. The script works with both Desktop and Core, so pwsh 7 is fine.
- Created a new config folder to hold config files. A new sample_default.json file is provided to show what the format looks like.
- You can now set the computername in the unattend.xml file where ever you want. Prior it required that the computername was the first component element.
## **2409.1**
### Fixes
- Fix an issue with removal of Defender/OneDrive/Edge after FFU is complete
- Migrate Winget downloads to use [Export-WingetPackage cmdlet](https://github.com/microsoft/winget-cli/blob/master/doc/specs/%23658%20-%20WinGet%20Download.md#winget-powershell-cmdlet) as per issue #50
- Add support for preview updates https://github.com/rbalsleyMSFT/FFU/pull/51 - thanks to @HedgeComp
- Refactor validation of Unattend/prefixes, PPKG, Autopilot to check for these files early in the process, similar to how we check for drivers
- Add better logging when unable to find HDD when applying FFU. Will inform to add WinPE drivers to Deployment Media if HDD not found.
- Remove ValidateScript on InstallDrivers and break it out in a validation block so -Make and -Model can be specified anywhere in the command line
- Add validation for VMHostIPAddress and VMSwtichName and inform the user if these don't match. Should prevent issues where the FFU isn't getting created.
- Removed installation of the Windows Security Platform Update as it has been removed from the MU Catalog. See issue #58
- Thanks to w0 for PR #54 to change the validation set for WindowsSKU
- Thanks to @zehadialam for PR #60 to fix an issue with Windows boot loader for certain devices where Windows Boot Manager is not the first boot entry after the FFU is applied.
- Thanks to @HedgeComp for PR #64 and PR #65
## **2408.1**
### External Drive Support
Up until now, the USB build process has supported using drives identified by Windows as removable drives. Most USB sticks will identify as removable, however faster drives may show up as external hard disk media. You may also have a smaller, portable SSD drive that you'd like to use for imaging since these are typically much faster than regular USB 3.x thumb drives.
In adding this support, I do realize that there is potential for data loss for those that might have external hard drives attached to their machines.
To handle this, with help from [HedgeComp](https://github.com/HedgeComp), we've refactored the `Get-USBDrives` function. Two new variables have been created:
| Parameter | Type | Description |
| --------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AllowExternalHardDiskMedia | Bool | If `$true`, will allow the use of media identified as External Hard Disk media via WMI class Win32_DiskDrive. Default is not defined. |
| PromptExternalHardDiskMedia | Bool | If `$true` and AllowExternalHardDiskMedia is `$true`, the script will prompt to select which drive to use. When set to `$true`, only a single drive will be created. If `$false`, the script won't prompt for which external hard disk to use and can use multiple external hard disks, similar to how removable USB drives function. |
By default, this functionality won't effect previous USB drive creation behavior. However if you want to take advantage of the new functionality, set `-AllowExternalHardDiskMedia $true`
Fixes/misc
- Fixed a display issue where if multiple FFU files were in the FFU folder, the script wouldn't display which FFUs to choose from when running the script without -verbose. This will now display a table with the last modified date whether you run with the -verbose switch or not.
- Added start/end/duration time (thanks [HedgeComp](https://github.com/HedgeComp))
- Fixed an issue where deployment media wasn't prompting for a key to be pressed as expected
- Fixed an issue when creating the USB drive and the drive had a RAW partition style that clear-disk would generate an error
- Cleaned up some commented code
- Added Create-PEMedia.ps1 as a helper script to quickly generate Deploy or Capture media
- Fixed an issue with clean up of Defender/OneDrive/Edge
- Fixed an issue with the formatting of InstallAppsandSysprep.cmd file
- Updated parameter documentation in the script to include newly added parameters
## **2407.1**
This is another major release that includes:
* Initial ARM64 support
* Winget support
### ARM64 Support
To support the newly released Copilot+ PCs, we now support the creation and deployment of FFUs created with ARM64 media. There are some caveats to this:
* The -WindowsArch parameter must be set to ARM64 (by default this parameter is set to x64)
* If you do not pass -ISOPath with a path to the ARM64 ISO, it will download an ARM64 ESD file from the Media Creation Tool (which is about 7-8 months old now). ARM64 ISOs are available via VLSC, but are not available via Visual Studio Downloads (Yet - unknown if they will ever be made available).
* The host machine you're building the FFU from must be ARM64
* Office/M365 apps don't currently support installing the ARM64 native bits from an offline system. If you pass `-InstallOffice $true` the script will change the value to false. You can install office after the fact when connected to the internet. I'm investigating this behavior and will issue a fix if/when this gets resolved. I still don't recommend building the FFU VM on the internet.
* The [Defender Updates Site](https://www.microsoft.com/en-us/wdsi/defenderupdates) provides download links for Defender definitions. The ARM link doesn't work for ARM64 and mpam-fe.exe fails to install. However there might be an undocumented ARM64 URL that may work. I've included it, but haven't tested it as I'm writing these notes. So we'll see if that works out.
* Drivers - Surface Laptop 7 and Pro 11 don't have ARM64 drivers available yet (there are entries, but they just point to a .txt file). Other OEMs may have drivers available.
In all, testing has gone very well.
### Winget Support
Big thanks to [Zehadi Alam](https://github.com/zehadialam) for his contributions to get this added to the project. You can now add any application in the msstore or winget source via the Winget command line utility. In the 1.8 Winget release the ability to download apps from the msstore source was added, which means being able to download apps like the Company Portal. For those of you that have been asking for Company Portal to be inbox in Windows, this is the next best thing. The script will check if Winget 1.8 is installed and if not, it'll install it.
The way this works is if `-InstallApps $true` and the FFUDevelopment\Apps\AppList.json file exists, whatever apps defined in that json file will be downloaded via Winget and will be installed in the FFU VM prior to capture. We've included two files: AppList_InboxAppsSample.json and AppList_Sample.json. The AppList_InboxAppsSample.json contains all of the apps that are installed in Windows by default and are searchable via `winget search AppID` . Some of these apps do not download and we're investigating why they come up via search, but fail to download. The AppList_Sample.json has Company Portal and New Teams.
![1721678632154](image/ChangeLog/1721678632154.png)
In sticking with the idea of having the most up to date Windows build, inbox store/UWP apps are notoriously out of date and use a lot of bandwidth. By updating all of the UWP apps, bandwidth reductions of ~70% can be achieved.
| | Total Data usage before updating store apps | Total Data usage after updating store apps | Total Data usage after updating Windows Update |
| --------------------------------------------------------------- | ------------------------------------------- | ------------------------------------------ | ---------------------------------------------- |
| July 2024 Windows 11 23H2 Stock ISO Captured as FFU (7.5GB FFU) | 261MB | 1.82GB | 2.09GB |
| July 2024 Windows 11 23H2 Updated FFU (10.5GB) | 13MB | 558MB | 646MB |
Updated means latest .NET, Defender (definition and platform updates), Edge, OneDrive, and all updates available via Winget for Store Apps have been provisioned in the FFU. The numbers in the table are cumulative, meaning the FFU was laid down, store apps were updated via running Get Apps from the Microsoft Store app and data usage was gathered from Settings, then Windows Update was manually kicked off via Settings and data usage was gathered.
In order to get apps to help build your AppList.json file, just run `winget search "AppName"`
![1721679421727](image/ChangeLog/1721679421727.png)
In this example we see that Firefox is published to both the msstore and winget sources. It's up to you which one you'd like to pick (I assume the msstore and the 128 version from the winget source are both the same version, but that may not be the case). You'll want to use the Name, ID, and Source values to help create your AppList.json file.
When downloading msstore apps, it does require an Entra ID. If you're building your FFUs from a machine that is not signed in with an Entra ID, you will be prompted for credentials for each app you download AND for the license file for each app (2 prompts per app). If downloading many store apps is something you plan on doing, I highly recommend signing in with an Entra ID to prevent the authentication prompts.
Other improvements
* [mhaley](https://github.com/mhaley) made their first contribution to [assign the drive letter to the recovery partition when copying in a custom WinRE.wim](https://github.com/rbalsleyMSFT/FFU/pull/35)
* [MKellyCBSD](https://github.com/MKellyCBSD) submitted a PR for a stand-alone USBImagingToolCreator.ps1 script which will create USB drives separate from the main BuildFUVM.ps1 script. This is helpful if you have technicans that need to build USB drives, or would like to make concurrent USB drives at the same time instead of one at a time. [His PR has all the details.](https://github.com/rbalsleyMSFT/FFU/pull/36)
* The WinPE_FFU_Deploy.iso will now work on VMs. This made ARM64 testing a lot easier :) If you're looking to test your FFU on a VM, you'll want to build a new VHDX and add your FFU to it and boot from the WinPE_FFU_Deploy.iso. Make sure to eject the VHDX before adding/booting the new VM. When attaching the new VHDX with your FFU on it, make sure it's not the first SCSI device (it should be 1 or 2, most likely 2 as 0 should be the hard drive you want to install Windows to, and 1 will be the DVD drive). By default the WinPE_FFU_Deploy.iso file is removed after the script completes. Make sure to set `-CreateDeploymentMedia $true` and `-CleanupDeployISO $false` so the ISO remains in the FFUDevelopment folder after the script completes.
The below screenshot should help in understanding what the SCSI config should look like.
![1721681140638](image/ChangeLog/1721681140638.png)
* Cleaned up some old commented code from the ApplyFFU.ps1 file and other files.
## **2406.1**
This is a major release that includes the ability to download drivers from the 4 major OEMs (Microsoft, Dell, HP, Lenovo) by simply passing the -Make and -Model parameters to the command line.
For Dell, HP, and Lenovo, the script leverages a similar process to their corresponding tools that automate driver downloads (Dell SupportAssist, HP Image Assistant, Lenovo System Update/Update Retriever). For Microsoft Surface, it scrapes the Surface Downloads page for the appropriate MSI file to download. Using this method, the drivers that are downloaded will be the latest provided by the OEM, unlike other tools that download out of date enteprise CAB files that are made for ConfigMgr.
The script supports lookups using the -model parameter. For example, if you want to download the drivers for a Surface Laptop Go 3, but don't know the exact model name, you could set -Make 'Microsoft' -Model 'Laptop Go' and it'll give you a list of Surface devices to pick from. If you know the exact name, it'll use that and not prompt.
![FFU Build Command Line that includes -make 'Microsoft' and -model 'Laptop Go' demonstrating how to use the new parameters to download drivers](image/ChangeLog/image-1.png)
The goal here is to make it easy to discover the drivers you want to download without having to know the exact model names.
There are likely going to be bugs with this, but in my testing things seem to work well for the makes and models that I've tried. If you notice something, please fill out an issue in the repro and I'll take a look. If you want to fix whatever issue you're running into, submit a pull request.
### New parameters
| Parameter | Type | Description |
| -------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Make | String | Used for automatically downloading drivers. Valid values are 'Microsoft', 'Dell', 'HP', 'Lenovo'. The script will throw an error if any other string value is used. |
| Model | String | Used for automatically downloading drivers with the Make parameter. |
| DriversFolder | String | Location where Drivers will either be downloaded, and/or the location of the drivers you wish to be added to the FFU, or copied to the deploy partition of the USB drive. The default location is $FFUDevelopmentPath\Drivers (e.g. C:\FFUDevelopmentPath\Drivers |
| CleanupDrivers | Bool | Used to delete the drivers folders underneath the `$DriversFolder` path (e.g. C:\FFUDevelopmentPath\Drivers\HP) after the FFU has been built. Default is `$true`true |
| UserAgent | String | The useragent string is used when invoking Invoke-Webrequest or Invoke-RestMethod. This has been helpful when interacting with the Microsoft Download Center and preventing intermittent errors. Default is Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0 |
| Headers | Hashtable | This hash table is used in conjunction with the Useragent when invoking Invoke-Webrequest or Invoke-RestMethod. This has been helpful when interacting with the Microsoft Download Center and preventing intermittent errors. If interested in the default value, reference the script itself. |
### New Functions
### `Test-URL`
Simple function that accepts \$URL parameter to test if a URL is accessible.
### `Start-BitsTransferWithRetry`
This is simply Start-BITSTransfer with some retry logic and setting `$VerbosePreference` and `$ProgressPreference` to SilentlyContinue. The retry logic was needed due to certain driver files randomly failing to download. The function is hardcoded to retry 3 times before failing and will wait 5 seconds between each retry attempt.
### `Get-MicrosoftDrivers`
For Microsoft Surface, the driver files are hosted on the Microsoft download center. The script will scrape and parse the [Download Surface Drivers and Firmware](https://support.microsoft.com/en-us/surface/download-drivers-and-firmware-for-surface-09bb2e09-2a4b-cb69-0951-078a7739e120) page to get the latest list of Surface devices.
This function accepts -Make, -Model, and -WindowsRelease parameters. Make and Model are both string parameters and WindowsRelease is an integer parameter. If the model parameter doesn't contain an exact match of a known Surface model, it'll give you a list of Surface models to pick from.
The following command line says that we want to download the drivers for a Microsoft Laptop Go for Windows 10.
`.\BuildFFUVM.ps1 -make 'Microsoft' -model 'Laptop Go' -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'external' -VMHostIPAddress '192.168.1.158' -CreateDeploymentMedia $true -BuildUSBDrive $true -UpdateLatestCU $true -UpdateLatestNet $true -UpdateLatestDefender $true -UpdateEdge $true -UpdateOneDrive $true -verbose -RemoveFFU $true -WindowsRelease 10`
![Screenshot of the FFU script displaying a list of Surface models to download drivers for](image/ChangeLog/1718826099739.png)
If you want to build an FFU for Surface Laptop Go 3, enter 18 and it'll download the MSI and extract the drivers to the .\FFUDevelopment\Drivers\Microsoft\Surface Laptop Go 3 folder.
If you would have provided the exact model string instead of just Laptop Go (e.g. -Model 'Surface Laptop Go 3'), the script wouldn't prompt you to enter a valid model.
### `Get-HPDrivers`
For HP, the script uses the same process as the HP Image Assistant tool to automate the downloading of drivers. This function accepts the -Make, -Model, -WindowsArch, -WindowsRelease, and -WindowsVersion parameters. HP is the only vendor that uses -WindowsVersion (e.g. 23h2) for its drivers. This is because their XML files contain the -WindowsVersion value in the file name. By default, the script uses 23h2 for the -WindowsVersion parameter. You can override that for whatever -WindowsVersion you wish to use.
The following command line says that we want to download HP x360 drivers for Windows 10 version 22h2.
`.\BuildFFUVM.ps1 -make 'HP' -model 'x360' -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'external' -VMHostIPAddress '192.168.1.158' -CreateDeploymentMedia $true -CreateCaptureMedia $true -BuildUSBDrive $true -UpdateLatestCU $true -UpdateLatestNet $true -UpdateLatestDefender $true -UpdateEdge $true -UpdateOneDrive $true -RemoveFFU $true -WindowsRelease 10 -WindowsVersion '22h2' -Verbose`
![Screenshot of the output of running the above command to download HP x360 drivers](image/ChangeLog/1718824392698.png)
HP has 40 models that contain the string x360 in the model name. I want to select the HP ProBook x360 11 G7 Education Edition Notebook PC which is number 25. The below screenshot shows the output of selecting the HP ProBook x360 11 G7 Education Edition Notebook PC
![Screenshot of the script downloading drivers for an HP ProBook x360 11 G7 Education Edition Notebook PC](image/ChangeLog/1718824500798.png)
If you were to enter the exact model name (e.g. -model 'HP ProBook x360 11 G7 Education Edition Notebook PC'), the script wouldn't prompt you to select from a list of models.
### `Get-LenovoDrivers`
For Lenovo, the script uses the same process Lenovo System Update/Update Retriever use. It uses the Get-LenovoDrivers function which accepts -Model, -WindowsArch, -WindowsRelease parameters.
Lenovo as a company doesn't use model like other companies do. Lenovo prefers to use a Machine Type value instead of Model number. The Machine Type value can be found on the bottom of your device as the first four characters of the MTM: value. Since most people don't know what the machine type value is, when passing the -model parameter, you can pass either the machine type or the "friendly" model number.
The following command line says that we want to download Lenovo 500w drivers for Windows 10.
`.\BuildFFUVM.ps1 -make 'Lenovo' -model '500w' -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'external' -VMHostIPAddress '192.168.1.158' -CreateDeploymentMedia $true -BuildUSBDrive $true -UpdateLatestCU $true -UpdateLatestNet $true -UpdateLatestDefender $true -UpdateEdge $true -UpdateOneDrive $true -RemoveFFU $true -WindowsRelease 10 -Verbose`
The script will go out to the [Lenovo PSREF](https://psref.lenovo.com/search) page to figure out the Machine Type value and if multiple Machine Types are found (there are usually multiples found for different configuration types).
![A screenshot of the different models and their machine types found from the Lenovo PSREF page and a selection prompt for the end user to pick which machine type they wish to use](image/ChangeLog/1718824806444.png)
The Machine Type is the value in parenthesis. On the bottom of my device, the MTM value is MTM:**82VR**ZAKXXX. I would want to pick number 4 from the list since it includes (82VR). The below screenshot shows the script downloading the appropriate drivers for a Lenovo 500w.
![The output of the script downloading Lenovo drivers](image/ChangeLog/1718824932640.png)
If you use the Machine Type value for the -Model parameter (e.g. -model '82VR') the script will automatically download the drivers without prompting you to select the model.
### `Get-DellDrivers`
For Dell, the script uses the [Dell CatalogPC Cab file](http://downloads.dell.com/catalog/CatalogPC.cab) which is used in Dell Support Assist and possibly other Dell tools to download drivers. The cab consists of an XML file that the script parses to search for drivers applicable for the model you wish to create a FFU for.
The script calls the Get-DellDrivers function which accepts the -Model and -WindowsArch parameters.
Unlike Microsoft Surface drivers, Dell doesn't give a list to pick from when the -model parameter isn't an exact match. This is due to how the CatalogPC XML file lists drivers. It treats the driver as the primary element and lists what models that driver can be installed on.
The following command line says that we want to download Dell 3190 drivers for Windows 10.
`.\BuildFFUVM.ps1 -make 'Dell' -model '3190' -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'external' -VMHostIPAddress '192.168.1.158' -CreateDeploymentMedia $true -CreateCaptureMedia $true -BuildUSBDrive $true -UpdateLatestCU $true -UpdateLatestNet $true -UpdateLatestDefender $true -UpdateEdge $true -UpdateOneDrive $true -RemoveFFU $true -WindowsRelease 10 -Verbose`
The script will find every driver that is tagged with 3190 and download the latest available version. It strips out any firmware or other non-driver file types. You may notice that it will download multiple video or audio drivers. This is due to each model having variants with different video cards or other hardware. This would make the FFU a bit larger, but not excessively so.
Below is a screenshot of what the verbose output of the script looks like when downloading the drivers for a Dell 3190.
![a screenshot of what the verbose output of the script looks like when downloading the drivers for a Dell 3190](image/ChangeLog/1718825319847.png)
* Added -Headers \$Headers -UserAgent \$UserAgent to most Invoke-Webrequest or Invoke-RestMethod commands to solve for intermittent download failures when downloading drivers or Office
* Fixed some minor logging issues
* Updated the BuildDeployFFU.docx with new driver information and cleaned up some sections that were out of date
* Added Changelog.md to keep track of changes and not clutter up the readme.md
## **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**
- If setting `-installdrivers to $true` and `-logicalsectorsizebytes to 4096`, the script will now set `$copyDrivers to $true`. This will create a drivers folder on the deploy partition of the USB drive with the drivers that were supposed to be added to the FFU. There's currently a bug with servicing FFUs with 4096 logical sector byte sizes. Prior to this fix, the script would tell the user to manually set `-copydrivers to $true` as workaround. This fix just does the workaround automatically.
## **2404.1**
There's a big change with this release related to the ADK. The ADK will now be automatically updated to the latest ADK release. This is required in order to fix an issue with optimized FFUs not applying due to an issue with DISM/FFUProvider.dll. The FFUProvider.dll fix was added to the Sept 2023 ADK. Since we now have the ability to auto upgrade the ADK, I'm more confident in having the BuildFFUVM script creating a complete FFU now (prior it was only creating 3 partitions instead of 4 with the recovery partition - at deployment time, the ApplyFFU.ps1 script would create an empty recovery partition and Windows would populate it on first boot). Please open an issue if this creates a problem for you. I do realize that any new ADK release can have it's own challenges and issues and I do suspect we'll see a new ADK released later this year.
- Allow for ISOs with single index WIMs to work [Issue 10](https://github.com/rbalsleyMSFT/FFU/issues/10) - [Commit](https://github.com/rbalsleyMSFT/FFU/commit/9e2da741d53652e6e600ca19cfd38f507bd01fde)
- Added more robust ADK handling. Will now check for the latest ADK and download it if not installed. Thanks to [Zehadi Alam](https://github.com/zehadialam) [PR 18](https://github.com/rbalsleyMSFT/FFU/pull/18)
- Revert code back to allow optimized FFUs to be applied via ApplyFFU.ps1 now that Sept 2023 ADK release has FFUProvider.dll fix. [Commit](https://github.com/rbalsleyMSFT/FFU/commit/79364e334d6d09ff150e70dab7bfb2637d0ad8a8)
- Changed how the script searches for the latest CU. Instead of relying on the Windows release info page to grab the KB number, will just use the MU Catalog, the same as what we do for the .NET Framework. Windows release info page is updated manually and is unknown as to when it will be updated. [Commit](https://github.com/rbalsleyMSFT/FFU/commit/6fd5a4a41fd9ce2f842f43dc3a69bda264c29fa6)
- Added fix to not allow computer names with spaces. Thanks to [JoeMama54 (Rob)](https://github.com/JoeMama54) [PR 20](https://github.com/rbalsleyMSFT/FFU/pull/20)
## **2403.1**
Fixed an issue with the SecurityHealthSetup.exe file giving an error when building the VM if -UpdateLatestDefender was set to $true. A new update for this came out on 3/21 which included a x64 and ARM64 binary. This file doesn't have an architecture designation to it, so it's impossible to know which file is for which architecture. Investigating to see if we can fix this in the Microsoft Update catalog. There is a web site to pull this from, but the support article is out of date.
Included ADK functions from Zehadi Alam [Introduce Automated ADK Retrieval and Installation Functions #14](https://github.com/rbalsleyMSFT/FFU/pull/14) to automate the installation of the ADK if it's not present. Thanks, Zehadi!
## **2402.1**
**New functionality**
* If -BuildUSBDrve $true, script will now check for USB drive before continuing. If not present, script exits
* Added a number of new parameters.
| Parameter | Type | Description |
| -------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| CopyPEDrivers | Bool | When set to\$true, will copy the drivers from the \$FFUDevelopmentPath\PEDrivers folder to the WinPE deployment media. Default is \$false. |
| RemoveFFU | Bool | When set to\$true, will remove the FFU file from the\$FFUDevelopmentPath\FFU folder after it has been copied to the USB drive. Default is \$false. |
| UpdateLatestCU | Bool | When set to\$true, will download and install the latest cumulative update for Windows 10/11. Default is \$false. |
| UpdateLatestNet | Bool | When set to\$true, will download and install the latest .NET Framework for Windows 10/11. Default is \$false. |
| UpdateLatestDefender | Bool | When set to\$true, will download and install the latest Windows Defender definitions and Defender platform update. Default is \$false. |
| UpdateEdge | Bool | When set to\$true, will download and install the latest Microsoft Edge for Windows 10/11. Default is \$false. |
| UpdateOneDrive | Bool | When set to\$true, will download and install the latest OneDrive for Windows 10/11 and install it as a per machine installation instead of per user. Default is \$false. |
| CopyPPKG | Bool | When set to\$true, will copy the provisioning package from the \$FFUDevelopmentPath\PPKG folder to the Deployment partition of the USB drive. Default is \$false. |
| CopyUnattend | Bool | When set to\$true, will copy the \$FFUDevelopmentPath\Unattend folder to the Deployment partition of the USB drive. Default is \$false. |
| CopyAutopilot | Bool | When set to\$true, will copy the \$FFUDevelopmentPath\Autopilot folder to the Deployment partition of the USB drive. Default is \$false. |
| CompactOS | Bool | When set to\$true, will compact the OS when building the FFU. Default is \$true. |
| CleanupCaptureISO | Bool | When set to\$true, will remove the WinPE capture ISO after the FFU has been captured. Default is \$true. |
| CleanupDeployISO | Bool | When set to\$true, will remove the WinPE deployment ISO after the FFU has been captured. Default is \$true. |
| CleanupAppsISO | Bool | When set to\$true, will remove the Apps ISO after the FFU has been captured. Default is \$true. |
* Updated the docs with the new variables and made some minor modifications.
* Changed version variable to 2402.1
## **2401.1**
- Added -CopyDrivers boolean parameter to control the ability to copy drivers to the USB drive in the deploy partition drivers folder.
- Changed version varaible to 2401.1
- When creating the scratch VHDX, switched it to create a dynamic VHDX instead of fixed
- Fixed an issue where adding drivers to the FFU would sometimes fail and would cause the script to exit unexpectedly
- Added -optimize boolean parameter to control whether the FFU is optimized or not. This defaults to $true and in most cases should be left this way.
- Fixed an issue where if the script failed to create the FFU and the old VM was left behind, it wouldn't clean it up if the VM was in the running state. Will now turn off any running VM with a name prefix of _FFU- and then remove any VMs with a name _FFU- if the environment is flagged as dirty.
- Fixed an issue where devices that ship with UFS drives were unable to image due to the script setting a LogicalSectorSizeBytes value of 512. If you're creating a FFU for devices that have UFS drives, you'll need to set -LogicalSectorSizeBytes 4096.
- There's a known issue where adding drivers to a FFU that has a LogicalSectorSizeBytes value of 4096. Added some code to prevent allowing this to happen. Please use -copydrivers $true as a workaround for now. We're investigating whether this is a bug or not.
- Fixed an issue where VHDX only captures (i.e. where -installapps $false) would not install Windows updates.
- Changed Office deployment to use Current channel instead of Monthly enterprise. If you want to change to Monthly Enterprise channel, it's recommended to leverage Intune.
## **2309.2**
New Features
**Multiple USB Drive Support**
You can now plug in multiple USB drives (even using a USB hub) to create multiple USB drives for deployment. This is great for partners or customers who need to provide USB drives to their employees to image a large number of devices. It will copy the content to one USB drive at a time. The most USB drives we've seen created so far is 23 via a USB hub. Open an issue if you see any problems with this.
**Robocopy support**
Replaced Copy-Item with Robocopy when copying content to the USB drive(s). Copy-Item uses buffered IO, which can take a long time to copy large files. Robocopy with the /J switch allows for unbuffered IO support, which reduces the amount of time to copy.
**Better error handling**
Prior to 2309.2, if the script failed or you manually killed the script (ctrl+c, or closing the PowerShell window), the environment would end up in a bad state and you had to do a number of things to manually clean up the environment. Added a new function called Get-FFUEnvironment and a new text file called dirty.txt that gets created in the FFUDevelopment folder. When the script starts, it checks for the dirty.txt file and if it sees it, Get-FFUEnvironment runs and cleans out a number of things to help ensure the next run will complete successfully. Open an issue if you still see problems when the script fails and the next run of the script fails. 
Bug Fixes
- In 2309.1, added a 15 second sleep to allow for the registry to unload to fix a Critical Process Died error on deployment. In this build, increased that to 60 seconds.
- Fixed an issue where the script was incorrectly detecting the USB drive boot and deploy drive letters which caused issues when attempting to copy the WinPE files to the boot partition.
## **2309.1**
- Fixed an issue with a Critical Process Died BSOD that would happen when using -installapps $false. More detailed information in the [commit](https://github.com/rbalsleyMSFT/FFU/pull/2/commits/34efbda7ec56dc7cb43ac42b058725d56c8b8899)
## **2306.1.2**
- Fixed an issue where manually entering a name wouldn't name the computer as expected
## **2306.1.1**
- Included some better error handling if defining optionalfeatures that require source folders (netfx3). ESD files don't have source folders like ISO media, which means installing .net 3.5 as an optional feature would fail. Also cleaned up some formatting.
## **2306.1**
- Added support to automatically download the latest Windows 10 or 11 media via the media creation tool (thanks to [Michael](https://oofhours.com/2022/09/14/want-your-own-windows-11-21h2-arm64-isos/) for the idea). This also allows for different architecture, language, and media type support. If you omit the -ISOPath, the script will download the Windows 11 x64 English (US) consumer media.
An example command to download Windows 11 Pro x64 English (US) consumer media with Office and install drivers (it won't download drivers, you'll put those in your c:\FFUDevelopment\Drivers folder)
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'Name of your VM Switch in Hyper-V' -VMHostIPAddress 'Your IP Address' -CreateCaptureMedia $true -CreateDeploymentMedia $true -BuildUSBDrive $true -verbose
An example command to download Windows 11 Pro x64 French (CA) consumer media with Office and install drivers
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'Name of your VM Switch in Hyper-V' -VMHostIPAddress 'Your IP Address' -CreateCaptureMedia $true -CreateDeploymentMedia $true -BuildUSBDrive $true -WindowsRelease 11 -WindowsArch 'x64' -WindowsLang 'fr-ca' -MediaType 'consumer' -verbose
- Changed default size of System/EFI partition to 260MB from 256MB to accomodate 4Kn drives. 4Kn support needs more testing. I'm not confident yet that this can be done with VMs and FFUs.
- Added versioning with a new version parameter. Using YYMM as the format followed by a point release.
@@ -0,0 +1,209 @@
{
"apps": [
{
"name": "Windows Terminal",
"id": "9N0DX20HK701",
"source": "msstore"
},
{
"name": "Cross Device Experience Host",
"id": "9NTXGKQ8P7N0",
"source": "msstore"
},
{
"name": "Movies & TV",
"id": "9WZDNCRFJ3P2",
"source": "msstore"
},
{
"name": "Microsoft Photos",
"id": "9WZDNCRFJBH4",
"source": "msstore"
},
{
"name": "Mail and Calendar",
"id": "9WZDNCRFHVQM",
"source": "msstore"
},
{
"name": "Microsoft Sticky Notes",
"id": "9NBLGGH4QGHW",
"source": "msstore"
},
{
"name": "Power Automate",
"id": "9NFTCH6J7FHV",
"source": "msstore"
},
{
"name": "Snipping Tool",
"id": "9MZ95KL8MR0L",
"source": "msstore"
},
{
"name": "Phone Link",
"id": "9NMPJ99VJBWV",
"source": "msstore"
},
{
"name": "Microsoft 365 (Office Hub)",
"id": "9WZDNCRFJBH4",
"source": "msstore"
},
{
"name": "App Installer",
"id": "9NBLGGH4NNS1",
"source": "msstore"
},
{
"name": "Microsoft Clipchamp",
"id": "9P1J8S7CCWWT",
"source": "msstore"
},
{
"name": "Webp Image Extensions",
"id": "9PG2DK419DRG",
"source": "msstore"
},
{
"name": "Windows Web Experience Pack",
"id": "9MSSGKG348SP",
"source": "msstore"
},
{
"name": "Xbox",
"id": "9MV0B5HZVK9Z",
"source": "msstore"
},
{
"name": "Paint",
"id": "9PCFS5B6T72H",
"source": "msstore"
},
{
"name": "Windows Camera",
"id": "9WZDNCRFJBBG",
"source": "msstore"
},
{
"name": "Windows Notepad",
"id": "9MSMLRH6LZF3",
"source": "msstore"
},
{
"name": "Windows Sound Recorder",
"id": "9WZDNCRFHWKN",
"source": "msstore"
},
{
"name": "Windows Calculator",
"id": "9WZDNCRFHVN5",
"source": "msstore"
},
{
"name": "Feedback Hub",
"id": "9NBLGGH4R32N",
"source": "msstore"
},
{
"name": "Xbox Identity Provider",
"id": "9WZDNCRD1HKW",
"source": "msstore"
},
{
"name": "Windows Media Player",
"id": "9WZDNCRFJ3PT",
"source": "msstore"
},
{
"name": "MSN Weather",
"id": "9WZDNCRFJ3Q2",
"source": "msstore"
},
{
"name": "Game Bar",
"id": "9NZKPSTSNW4P",
"source": "msstore"
},
{
"name": "Web Media Extensions",
"id": "9N5TDP8VCMHS",
"source": "msstore"
},
{
"name": "Get Help",
"id": "9PKDZBMV1H3T",
"source": "msstore"
},
{
"name": "Raw Image Extension",
"id": "9NCTDW2W1BH8",
"source": "msstore"
},
{
"name": "Store Experience Host",
"id": "9NBLGGH4LS1F",
"source": "msstore"
},
{
"name": "Windows Maps",
"id": "9WZDNCRDTBVB",
"source": "msstore"
},
{
"name": "Windows Clock",
"id": "9WZDNCRFJ3PR",
"source": "msstore"
},
{
"name": "Microsoft To Do",
"id": "9NBLGGH5R558",
"source": "msstore"
},
{
"name": "Cortana",
"id": "9NFFX4SZZ23L",
"source": "msstore"
},
{
"name": "Quick Assist",
"id": "9P7BP5VNWKX5",
"source": "msstore"
},
{
"name": "HEIF Image Extensions",
"id": "9PMMSR1CGPWG",
"source": "msstore"
},
{
"name": "VP9 Video Extensions",
"id": "9N4D0MSMP0PT",
"source": "msstore"
},
{
"name": "Xbox Live in-game experience",
"id": "9NKNC0LD5NN6",
"source": "msstore"
},
{
"name": "Xbox Game Speech Window",
"id": "9P086NHDNB9W",
"source": "msstore"
},
{
"name": "Microsoft News",
"id": "9WZDNCRFHVFW",
"source": "msstore"
},
{
"name": "Microsoft Store",
"id": "9WZDNCRFJBMP",
"source": "msstore"
},
{
"name": "Microsoft Tips",
"id": "9WZDNCRDTBJJ",
"source": "msstore"
}
]
}
+14
View File
@@ -0,0 +1,14 @@
{
"apps": [
{
"name": "Company Portal",
"id": "9WZDNCRFJ3PZ",
"source": "msstore"
},
{
"name": "Microsoft Teams",
"id": "Microsoft.Teams",
"source": "winget"
}
]
}
+67 -3
View File
@@ -1,22 +1,86 @@
setlocal enabledelayedexpansion
REM Put each app install on a separate line
REM M365 Apps/Office ProPlus
REM d:\Office\setup.exe /configure d:\office\DeployFFU.xml
REM Install Defender Platform Update
REM Install Defender Definitions
REM Install Windows Security Platform Update
REM Install Windows Malicious Software Removal Tool
REM Install OneDrive Per Machine
REM Install Edge Stable
REM Winget Win32 Apps
REM START Batch variables placeholder
REM END Batch variables placeholder
REM Add additional apps below here
REM Contoso App (Example)
REM msiexec /i d:\Contoso\setup.msi /qn /norestart
set "INSTALL_STOREAPPS=false"
if /i "%INSTALL_STOREAPPS%"=="false" (
echo Skipping MS Store installation due to INSTALL_STOREAPPS flag.
goto :remaining
)
set "basepath=D:\MSStore"
for /d %%D in ("%basepath%\*") do (
set "appfolder=%%D"
set "mainpackage="
set "dependenciesfolder=!appfolder!\Dependencies"
for %%F in ("!appfolder!\*") do (
if not "%%~dpF"=="!dependenciesfolder!\" (
if /i not "%%~xF"==".xml" (
if /i not "%%~xF"==".yaml" (
set "mainpackage=%%F"
)
)
)
)
if defined mainpackage (
set "dism_command=DISM /Online /Add-ProvisionedAppxPackage /PackagePath:"!mainpackage!" /Region:all /StubPackageOption:installfull"
if exist "!dependenciesfolder!" (
for %%G in ("!dependenciesfolder!\*") do (
set "dism_command=!dism_command! /DependencyPackagePath:"%%G""
)
)
for %%F in ("!appfolder!\*.xml") do (
set "licensefile=%%F"
)
if defined licensefile (
set "dism_command=!dism_command! /LicensePath:"!licensefile!""
) else (
set "dism_command=!dism_command! /SkipLicense"
)
echo !dism_command!
!dism_command!
)
)
:remaining
endlocal
for /r "D:\" %%G in (.) do (
if exist "%%G\Notepad++" (
powershell -Command "Remove-AppxPackage -Package NotepadPlusPlus_1.0.0.0_neutral__7njy0v32s6xk6"
)
)
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 10
REM Run Component Cleanup since dism /online /cleanup-image /analyzecomponentcleanup recommends it
REM If adding latest CU, definitely need to do this to keep FFU size smaller
dism /online /cleanup-image /startcomponentcleanup /resetbase
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
set rootkey=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches
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
c:\windows\system32\sysprep\sysprep.exe /quiet /generalize /oobe
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="auditUser">
<component name="Microsoft-Windows-Deployment" processorArchitecture="arm64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RunAsynchronous>
<RunAsynchronousCommand wcm:action="add">
<Order>1</Order>
<Path>d:\InstallAppsandSysprep.cmd</Path>
</RunAsynchronousCommand>
</RunAsynchronous>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Deployment" processorArchitecture="arm64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Reseal>
<Mode>Audit</Mode>
</Reseal>
</component>
</settings>
<cpi:offlineImage cpi:source="wim:c:/wimtoffu/win11_22h2_feb2023_consumer.wim#Windows 11 Pro" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>
File diff suppressed because it is too large Load Diff
+209
View File
@@ -0,0 +1,209 @@
param (
[string]$FFUDevelopmentPath = $PSScriptRoot,
[string]$adkPath = 'C:\Program Files (x86)\Windows Kits\10\',
[string]$WindowsArch = 'x64',
[bool]$CopyPEDrivers = $false,
[string]$CaptureISO = "$PSScriptRoot\WinPE_FFU_Capture_x64.iso",
[string]$DeployISO = "$PSScriptRoot\WinPE_FFU_Deploy_x64.iso",
[string]$LogFile = "$PSScriptRoot\Create-PEMedia.log",
[bool]$Capture,
[bool]$Deploy = $true
)
function WriteLog($LogText) {
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText" -Force -ErrorAction SilentlyContinue
Write-Verbose $LogText
}
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 = $true;
}
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 - $Logfile for more info"
throw $_
}
finally {
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
}
}
function New-PEMedia {
param (
[Parameter()]
[bool]$Capture,
[Parameter()]
[bool]$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") {
WriteLog "Removing old WinPE path at $WinPEFFUPath"
Remove-Item -Path "$WinPEFFUPath" -Recurse -Force | out-null
}
WriteLog "Copying WinPE files to $WinPEFFUPath"
if($WindowsArch -eq 'x64') {
& cmd /c """$DandIEnv"" && copype amd64 $WinPEFFUPath" | Out-Null
}
elseif($WindowsArch -eq 'arm64') {
& cmd /c """$DandIEnv"" && copype arm64 $WinPEFFUPath" | Out-Null
}
#Invoke-Process cmd "/c ""$DandIEnv"" && copype amd64 $WinPEFFUPath"
WriteLog 'Files copied successfully'
WriteLog 'Mounting WinPE media to add WinPE optional components'
Mount-WindowsImage -ImagePath "$WinPEFFUPath\media\sources\boot.wim" -Index 1 -Path "$WinPEFFUPath\mount" | Out-Null
WriteLog 'Mounting complete'
$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"
)
if($WindowsArch -eq 'x64'){
$PackagePathBase = "$adkPath`Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\"
}
elseif($WindowsArch -eq 'arm64'){
$PackagePathBase = "$adkPath`Assessment and Deployment Kit\Windows Preinstallation Environment\arm64\WinPE_OCs\"
}
foreach ($Package in $Packages) {
$PackagePath = Join-Path $PackagePathBase $Package
WriteLog "Adding Package $Package"
Add-WindowsPackage -Path "$WinPEFFUPath\mount" -PackagePath $PackagePath | Out-Null
WriteLog "Adding package complete"
}
If ($Capture) {
WriteLog "Copying $FFUDevelopmentPath\WinPECaptureFFUFiles\* to WinPE capture media"
Copy-Item -Path "$FFUDevelopmentPath\WinPECaptureFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force | out-null
WriteLog "Copy complete"
#Remove Bootfix.bin - for BIOS systems, shouldn't be needed, but doesn't hurt to remove for our purposes
#Remove-Item -Path "$WinPEFFUPath\media\boot\bootfix.bin" -Force | Out-null
# $WinPEISOName = 'WinPE_FFU_Capture.iso'
$WinPEISOFile = $CaptureISO
# $Capture = $false
}
If ($Deploy) {
WriteLog "Copying $FFUDevelopmentPath\WinPEDeployFFUFiles\* to WinPE deploy media"
Copy-Item -Path "$FFUDevelopmentPath\WinPEDeployFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force | Out-Null
WriteLog 'Copy complete'
#If $CopyPEDrivers = $true, add drivers to WinPE media using dism
if ($CopyPEDrivers) {
WriteLog "Adding drivers to WinPE media"
try {
Add-WindowsDriver -Path "$WinPEFFUPath\Mount" -Driver "$FFUDevelopmentPath\PEDrivers" -Recurse -ErrorAction SilentlyContinue | Out-null
}
catch {
WriteLog 'Some drivers failed to be added to the FFU. This can be expected. Continuing.'
}
WriteLog "Adding drivers complete"
}
# $WinPEISOName = 'WinPE_FFU_Deploy.iso'
$WinPEISOFile = $DeployISO
# $Deploy = $false
}
WriteLog 'Dismounting WinPE media'
Dismount-WindowsImage -Path "$WinPEFFUPath\mount" -Save | Out-Null
WriteLog 'Dismount complete'
#Make ISO
if ($WindowsArch -eq 'x64') {
$OSCDIMGPath = "$adkPath`Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg"
}
elseif ($WindowsArch -eq 'arm64') {
$OSCDIMGPath = "$adkPath`Assessment and Deployment Kit\Deployment Tools\arm64\Oscdimg"
}
$OSCDIMG = "$OSCDIMGPath\oscdimg.exe"
WriteLog "Creating WinPE ISO at $WinPEISOFile"
# & "$OSCDIMG" -m -o -u2 -udfver102 -bootdata:2`#p0,e,b$OSCDIMGPath\etfsboot.com`#pEF,e,b$OSCDIMGPath\Efisys_noprompt.bin $WinPEFFUPath\media $FFUDevelopmentPath\$WinPEISOName | Out-null
if($WindowsArch -eq 'x64'){
if($Capture){
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:2`#p0,e,b`"$OSCDIMGPath\etfsboot.com`"`#pEF,e,b`"$OSCDIMGPath\Efisys_noprompt.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
}
if($Deploy){
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:2`#p0,e,b`"$OSCDIMGPath\etfsboot.com`"`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
}
}
elseif($WindowsArch -eq 'arm64'){
if($Capture){
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:1`#pEF,e,b`"$OSCDIMGPath\Efisys_noprompt.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
}
if($Deploy){
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:1`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
}
}
Invoke-Process $OSCDIMG $OSCDIMGArgs
WriteLog "ISO created successfully"
WriteLog "Cleaning up $WinPEFFUPath"
Remove-Item -Path "$WinPEFFUPath" -Recurse -Force
WriteLog 'Cleanup complete'
}
if($Capture){
New-PEMedia -Capture $Capture
}
if($Deploy){
New-PEMedia -Deploy $Deploy
}
Binary file not shown.
Binary file not shown.
+244
View File
@@ -0,0 +1,244 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $True, Position = 0)]
$DeployISOPath,
[Switch]$DisableAutoPlay
)
$Host.UI.RawUI.WindowTitle = 'Imaging Tool USB Creator'
if($DeployISOPath){
$DevelopmentPath = $DeployISOPath | Split-Path
$ImagesPath = "$DevelopmentPath\FFU"
function WriteLog($LogText) {
$LogFileName = '\Script.log'
$LogFile = $DevelopmentPath + $LogFilename
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText" -Force -ErrorAction SilentlyContinue
Write-Verbose $LogText
}
function Write-ProgressLog {
param(
[string]$Activity,
[string]$Status
)
Write-Progress -Activity $Activity -Status $Status -PercentComplete (($currentStep / $totalSteps) * 100)
WriteLog $Status
$script:currentStep++
}
Function Get-RemovableDrive {
writelog "Get information for all removable drives"
$USBDrives = Get-WmiObject Win32_DiskDrive | Where-Object {$_.MediaType -eq "Removable media"}
If($USBDrives -and ($null -eq $USBDrives.count)) {
$USBDrivesCount = 1
} else {
$USBDrivesCount = $USBDrives.Count
}
WriteLog "Found $USBDrivesCount USB drives"
if ($null -eq $USBDrives) {
WriteLog "No removable USB drive found. Exiting"
Write-Error "No removable USB drive found. Exiting"
Pause
exit 1
}
return $USBDrives, $USBDrivesCount
}
Function Build-DeploymentUSB{
param(
[Array]$Drives
)
writelog "Creating list of FFU image files"
$Images = Get-ChildItem -Path $ImagesPath -Filter "*.ffu" -File -Recurse
writelog "Checking if drivers are present in the drivers folder"
$Drivers = Get-ChildItem -Path $DriversPath -Recurse
$DrivesCount = $Drives.Count
Write-ProgressLog "Create Imaging Tool" "Creating partitions..."
writelog "Create job to partition each usb drive"
foreach ($USBDrive in $Drives) {
$DriveNumber = $USBDrive.DeviceID.Replace("\\.\PHYSICALDRIVE", "")
$Model = $USBDrive.model
$ScriptBlock = {
param($DriveNumber)
Clear-Disk -Number $DriveNumber -RemoveData -RemoveOEM -Confirm:$false
$Disk = Get-Disk -Number $DriveNumber
$PartitionStyle = $Disk.PartitionStyle
if($PartitionStyle -ne 'MBR'){
$Disk | Set-Disk -PartitionStyle MBR
}
$BootPartition = New-Partition -DiskNumber $DriveNumber -Size 2GB -IsActive -AssignDriveLetter
$DeployPartition = New-Partition -DiskNumber $DriveNumber -UseMaximumSize -AssignDriveLetter
Format-Volume -Partition $BootPartition -FileSystem FAT32 -NewFileSystemLabel "Boot" -Confirm:$false
Format-Volume -Partition $DeployPartition -FileSystem NTFS -NewFileSystemLabel "Deploy" -Confirm:$false
}
WriteLog "Start job to create BOOT and Deploy partitions on drive number $DriveNumber"
Start-Job -ScriptBlock $ScriptBlock -ArgumentList $DriveNumber | Out-Null
}
writelog "Wait for partitioning jobs to complete"
Get-Job | Wait-Job | Out-Null
if($DrivesCount -gt 1){
writelog "Get file system information for all drives"
$Partitions = Get-Partition | Get-Volume
} else {
writelog "Get file system information for drive number $DiskNumber"
$Partitions = Get-Partition -DiskNumber $DriveNumber | Get-Volume
}
writelog "Get drive letter for all volumes labeled:BOOT"
$BootDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "BOOT"}).DriveLetter
writelog "Get drive letter for all volumes labeled:Deploy"
$DeployDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "Deploy"}).DriveLetter
writelog "Mount Deployment .iso image"
$ISOMountPoint = (Mount-DiskImage -ImagePath "$DeployISOPath" -PassThru | Get-Volume).DriveLetter + ":\"
writelog "Copying boot files to all drives labeled BOOT concurrently"
foreach ($Drive in $BootDrives) {
$Destination = $Drive + ":\"
$jobScriptBlock = {
param (
[string]$SFolder,
[string]$DFolder
)
Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
}
WriteLog "Start job to copy all boot files to $Destination"
Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $ISOMountPoint, $Destination | Out-Null
}
if($Images){
writelog "Copying FFU image files to all drives labeled deploy concurrently"
foreach ($Drive in $DeployDrives) {
$Destination = $Drive + ":\"
$jobScriptBlock = {
param (
[string]$SFolder,
[string]$DFolder
)
New-Item -Path $DFolder -ItemType Directory -Force -Confirm: $false | Out-Null
Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
}
WriteLog "Start job to copy all FFU files to $Destination"
Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $ImagesPath, $Destination | Out-Null
}
}
if(!($Images)){
foreach ($Drive in $DeployDrives) {
WriteLog "Create images directory"
$drivepath = $Drive + ":\"
New-Item -Path "$drivepath" -Name Images -ItemType Directory -Force -Confirm: $false | Out-Null
}
}
if($Drivers){
writelog "Copying driver files to all drives labeled deploy concurrently"
foreach ($Drive in $DeployDrives) {
$Destination = $Drive + ":\Drivers"
$jobScriptBlock = {
param (
[string]$SFolder,
[string]$DFolder
)
New-Item -Path $DFolder -ItemType Directory -Force -Confirm: $false | Out-Null
Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
}
WriteLog "Start job to copy all drivers to $Destination"
Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $DriversPath, $Destination | Out-Null
}
}
if(!($Drivers)){
foreach ($Drive in $DeployDrives) {
WriteLog "Create drivers directory"
$drivepath = $Drive + ":\"
New-Item -Path "$drivepath" -Name Drivers -ItemType Directory -Force -Confirm: $false | Out-Null
}
}
if($DrivesCount -gt 1){
Write-ProgressLog "Create Imaging Tool" "Building $DrivesCount drives concurrently...Please be patient..."
} else {
Write-ProgressLog "Create Imaging Tool" "Building the imaging tool on $model...Please be patient..."
}
Get-Job | Wait-Job | Out-Null
Dismount-DiskImage -ImagePath $DeployISOPath | Out-Null
Write-ProgressLog "Create Imaging Tool" "Drive creation jobs completed..."
}
Function New-DeploymentUSB {
param(
[Array]$Drives,
[int]$Count,
[String]$FFUPath = "$DevelopmentPath\FFU",
[String]$DriversPath = "$DevelopmentPath\Drivers"
)
$Drivelist = @()
writelog "Creating a USB drive selection list"
for($i=0;$i -le $Count -1;$i++){
$DriveModel = $Drives[$i].Model
$DriveSize = [math]::round($Drives[$i].size/1GB, 2)
$DiskNumber = $Drives[$i].DeviceID.Replace("\\.\PHYSICALDRIVE", "")
$Properties = [ordered]@{Number = $i + 1 ; DriveNumber = $DiskNumber ; DriveModel = $driveModel ; 'Size (GB)' = $DriveSize}
$Drivelist += New-Object PSObject -Property $Properties
}
if($Count -gt 1){
$Last = $Count+1
$Drivelist += New-Object -TypeName PSObject -Property @{ Number = "$last"; DriveModel = "Select this option to use all ($count) inserted USB Drives" }
}
$Drivelist | Format-Table -AutoSize -Property Number, DriveModel , 'Size (GB)'
do {
try {
$var = $true
$DriveSelected = Read-Host 'Enter the drive number to apply the .iso to'
$DriveSelected = ($DriveSelected -as [int]) -1
writelog "Drive $DriveSelected selected"
}
catch {
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
$var = $false
}
} until (($DriveSelected -le $Count -1 -or $last) -and $var)
if($DisableAutoPlay){
WriteLog "Setting the registry key to disable autoplay for all drives"
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value 1 -Type DWORD
}
WriteLog "Closing all MMC windows to prevent drive lock errors"
Stop-Process -Name mmc -ErrorAction SilentlyContinue
WriteLog "Closing all Diskpart windows to prevent drive lock errors"
Stop-Process -Name diskpart -ErrorAction SilentlyContinue
$Selection = $Drivelist[$DriveSelected].Number
$totalSteps = 5
if($Selection -eq $last){
Read-Host -Prompt "ALL DRIVES SELECTED! WILL ERASE ALL CURRENTLY CONNECTED USB DRIVES!! Press ENTER to continue"
Build-DeploymentUSB -Drives $Drives
} else {
Read-Host -Prompt "Drive number $Selection was selected. Press ENTER to continue"
Build-DeploymentUSB -Drives $Drives[$DriveSelected]
}
WriteLog "Setting the registry key to re-enable autoplay for all drives"
if($DisableAutoPlay){
Write-ProgressLog "Create Imaging Tool" "Enabling Autoplay"
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value 0 -Type DWORD
}
Write-ProgressLog "Create Imaging Tool" "Completed!"
}
#Get USB Drive and create log file
if(Test-Path "$DevelopmentPath\Script.log"){
Remove-Item -Path "$DevelopmentPath\Script.log" -Force -Confirm:$false
New-item -Path $DevelopmentPath -Name 'Script.log' -ItemType "file" -Force | Out-Null
}
WriteLog 'Begin Logging'
WriteLog 'Getting USB drive information and usb drive count'
$USBDrives,$USBDrivesCount = Get-RemovableDrive
WriteLog 'Setting first step for percentage progress bar'
$currentStep = 1
New-DeploymentUSB -Drives $USBDrives -Count $USBDrivesCount
read-host -Prompt "USB drive creation complete. Press ENTER to exit"
Exit
} else {
Write-Host "No .ISO file selected..."
read-host "Press ENTER to Exit..."
Exit
}
@@ -1,5 +1,6 @@
#Modify the net use W: \\192.168.1.158\FFUCaptureShare /user:ffu_user ddb1f077-3eed-433c-b4d9-7b8cd54ce727
net use W: \\192.168.1.158\FFUCaptureShare /user:ffu_user ddb1f077-3eed-433c-b4d9-7b8cd54ce727
#Custom naming placeholder
$AssignDriveLetter = 'x:\AssignDriveLetter.txt'
Start-Process -FilePath diskpart.exe -ArgumentList "/S $AssignDriveLetter" -Wait -ErrorAction Stop | Out-Null
@@ -11,53 +12,89 @@ reg load "HKLM\FFU" $Software
$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'
if ($CurrentBuild -notin 14393, 17763) {
$WindowsVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
}
$InstallationType = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'InstallationType'
$BuildDate = Get-Date -uformat %b%Y
$SKU = switch ($SKU) {
Core { 'Home' }
CoreN { 'HomeN'}
CoreSingleLanguage { 'HomeSL'}
CoreN { 'HomeN' }
CoreSingleLanguage { 'HomeSL' }
Professional { 'Pro' }
ProfessionalN { 'ProN'}
ProfessionalN { 'ProN' }
ProfessionalEducation { 'Pro_Edu' }
ProfessionalEducationN { 'Pro_EduN' }
Enterprise { 'Ent' }
EnterpriseN { 'EntN'}
EnterpriseN { 'EntN' }
Education { 'Edu' }
EducationN { 'EduN'}
EducationN { 'EduN' }
ProfessionalWorkstation { 'Pro_Wks' }
ProfessionalWorkstationN { 'Pro_WksN' }
ServerStandard { 'Srv_Std' }
ServerDatacenter { 'Srv_Dtc' }
}
if($CurrentBuild -ge 22000){
$Name = 'Win11'
if ($InstallationType -eq "Client") {
if ($CurrentBuild -ge 22000) {
$WindowsRelease = 'Win11'
}
else {
$WindowsRelease = 'Win10'
}
}
else{
$Name = 'Win10'
else {
$WindowsRelease = switch ($CurrentBuild) {
26100 { '2025' }
20348 { '2022' }
17763 { '2019' }
14393 { '2016' }
Default { $WindowsVersion }
}
if ($InstallationType -eq "Server Core") {
$SKU += "_Core"
}
}
#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' -ErrorAction SilentlyContinue
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`_Apps`_$BuildDate.ffu"
$dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
if ($CustomFFUNameTemplate) {
$CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{WindowsRelease}', $WindowsRelease
$CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{WindowsVersion}', $WindowsVersion
$CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{SKU}', $SKU
$CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{BuildDate}', $BuildDate
$CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{yyyy}', (Get-Date -UFormat '%Y')
$CustomFFUNameTemplate = $CustomFFUNameTemplate -creplace '{MM}', (Get-Date -UFormat '%m')
$CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{dd}', (Get-Date -UFormat '%d')
$CustomFFUNameTemplate = $CustomFFUNameTemplate -creplace '{HH}', (Get-Date -UFormat '%H')
$CustomFFUNameTemplate = $CustomFFUNameTemplate -creplace '{hh}', (Get-Date -UFormat '%I')
$CustomFFUNameTemplate = $CustomFFUNameTemplate -creplace '{mm}', (Get-Date -UFormat '%M')
$CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{tt}', (Get-Date -UFormat '%p')
if($CustomFFUNameTemplate -notlike '*.ffu') {
$CustomFFUNameTemplate += '.ffu'
}
$dismArgs = "/capture-ffu /imagefile=W:\$CustomFFUNameTemplate /capturedrive=\\.\PhysicalDrive0 /name:$WindowsRelease$WindowsVersion$SKU /Compress:Default"
} else {
#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' -ErrorAction SilentlyContinue
if ($Office) {
$ffuFilePath = "W:\$WindowsRelease`_$WindowsVersion`_$SKU`_Office`_$BuildDate.ffu"
} else {
$ffuFilePath = "W:\$WindowsRelease`_$WindowsVersion`_$SKU`_Apps`_$BuildDate.ffu"
}
$dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$WindowsRelease$WindowsVersion$SKU /Compress:Default"
}
#Unload Registry
Set-Location X:\
Remove-Variable SKU
Remove-Variable CurrentBuild
Remove-Variable DisplayVersion
Remove-Variable Office
if ($CurrentBuild -notin 14393, 17763) {
Remove-Variable WindowsVersion
}
if($Office) {
Remove-Variable Office
}
reg unload "HKLM\FFU"
#This prevents Critical Process Died errors you can have during deployment of the FFU - may not happen during capture from WinPE, but adding here to be consistent with VHDX capture
Write-Host "Sleeping for 60 seconds to allow registry to unload prior to capture"
@@ -65,5 +102,4 @@ Start-sleep 60
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
wpeutil Shutdown
+87 -97
View File
@@ -15,8 +15,34 @@ function Get-USBDrive(){
}
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
$SystemInfo = Get-WmiObject -Class 'Win32_ComputerSystem'
$Manufacturer = $SystemInfo.Manufacturer
$Model = $SystemInfo.Model
WriteLog "Device Manufacturer: $Manufacturer"
WriteLog "Device Model: $Model"
WriteLog 'Getting Hard Drive info'
if ($Manufacturer -eq 'Microsoft Corporation' -and $Model -eq 'Virtual Machine'){
WriteLog 'Running in a Hyper-V VM. Getting virtual disk on Index 0 and SCSILogicalUnit 0'
$DiskDrive = Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' `
-and $_.Model -eq 'Microsoft Virtual Disk' `
-and $_.Index -eq 0 `
-and $_.SCSILogicalUnit -eq 0
}
}
else{
WriteLog 'Not running in a VM. Getting physical disk drive'
$DiskDrive = Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' -and $_.Model -ne 'Microsoft Virtual Disk'}
}
$DeviceID = $DiskDrive.DeviceID
$BytesPerSector = $Diskdrive.BytesPerSector
# Create a custom object to return both values
$result = New-Object PSObject -Property @{
DeviceID = $DeviceID
BytesPerSector = $BytesPerSector
}
return $result
}
function WriteLog($LogText){
@@ -29,11 +55,18 @@ function Set-DiskpartAnswerFiles($DiskpartFile,$DiskID){
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
$components = $xml.unattend.settings.component
$found = $false
foreach ($component in $components) {
if ($component.ComputerName) {
$component.ComputerName = $computername
$found = $true
break
}
}
if (-not $found) {
WriteLog 'ComputerName element not found in unattend.xml.'
throw 'ComputerName element not found in unattend.xml.'
}
$xml.Save($UnattendFile)
return $computername
@@ -97,60 +130,32 @@ function Invoke-Process {
}
# 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
$version = '2404.2'
$version = '2412.3'
WriteLog 'Begin Logging'
WriteLog "Script version: $version"
#Find PhysicalDrive
$PhysicalDeviceID = Get-HardDrive
# $PhysicalDeviceID = Get-HardDrive
$hardDrive = Get-HardDrive
if($null -eq $hardDrive){
WriteLog 'No hard drive found. Exiting'
WriteLog 'Try adding storage drivers to the PE boot image (you can re-create your FFU and USB drive and add the PE drivers to the PEDrivers folder and add -CopyPEDrivers $true to the command line, or manually add them via DISM)'
Exit
}
$PhysicalDeviceID = $hardDrive.DeviceID
$BytesPerSector = $hardDrive.BytesPerSector
WriteLog "Physical BytesPerSector is $BytesPerSector"
WriteLog "Physical DeviceID is $PhysicalDeviceID"
#Parse DiskID Number
$DiskID = $PhysicalDeviceID.substring($PhysicalDeviceID.length - 1,1)
WriteLog "DiskID is $DiskID"
#COMMENT THIS WHOLE BLOCK OUT ONCE FFUPROVIDER FIX IS IN
# #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
@@ -420,17 +425,13 @@ If (Test-Path -Path $Drivers)
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
if ($Disk.PartitionStyle -ne "RAW") {
$Disk | clear-disk -RemoveData -RemoveOEM -Confirm:$false
}
}
catch {
WriteLog 'Cleaning disk failed. Exiting'
@@ -444,50 +445,41 @@ 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
$recoveryPartition = Get-Partition -Disk $Disk | Where-Object PartitionNumber -eq 4
if ($recoveryPartition) {
WriteLog 'Setting recovery partition attributes'
$diskpartScript = @(
"SELECT DISK $($Disk.Number)",
"SELECT PARTITION $($recoveryPartition.PartitionNumber)",
"GPT ATTRIBUTES=0x8000000000000001",
"EXIT"
)
$diskpartScript | diskpart.exe | Out-Null
WriteLog 'Setting recovery partition attributes complete'
}
if($LASTEXITCODE -eq 0){
WriteLog 'Successfully applied FFU'
}
elseif($LASTEXITCODE -eq 1393){
WriteLog "Failed to apply FFU - LastExitCode = $LastExitCode"
WriteLog "This is likely due to a mismatched LogicalSectorByteSize"
WriteLog "BytesPerSector value from Win32_Diskdrive is $BytesPerSector"
if ($BytesPerSector -eq 4096){
WriteLog "The FFU build process by default uses a 512 LogicalSectorByteSize. Rebuild the FFU by adding -LogicalSectorByteSize 4096 to the command line"
}
elseif($BytesPerSector -eq 512){
WriteLog "This FFU was likely built with a LogicalSectorByteSize of 4096. Rebuild the FFU by adding -LogicalSectorByteSize 512 to the command line"
}
#Copy DISM log to USBDrive
invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
exit
}
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
# }
# }
#COMMENT THIS WHOLE BLOCK OUT AFTER FFUPROVIDER FIX IS IN
# # 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"
# }
#UNCOMMENT THIS AFTER FFUPROVIDER FIX IS IN
# 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
@@ -495,22 +487,14 @@ $WinRE = $USBDrive + "WinRE\winre.wim"
If (Test-Path -Path $WinRE)
{
WriteLog 'Copying modified WinRE to Recovery directory'
Get-Disk | Where-Object Number -eq $DiskID | Get-Partition | Where-Object Type -eq Recovery | Set-Partition -NewDriveLetter R
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"
Get-Disk | Where-Object Number -eq $DiskID | Get-Partition | Where-Object Type -eq Recovery | Remove-PartitionAccessPath -AccessPath R:
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"
@@ -577,6 +561,12 @@ If (Test-Path -Path $Drivers)
WriteLog 'Copying drivers succeeded'
}
WriteLog "Setting Windows Boot Manager to be first in the display order."
Invoke-Process bcdedit.exe "/set {fwbootmgr} displayorder {bootmgr} /addfirst"
WriteLog "Windows Boot Manager has been set to be first in the display order."
WriteLog "Setting default Windows boot loader to be first in the display order."
Invoke-Process bcdedit.exe "/set {bootmgr} displayorder {default} /addfirst"
WriteLog "The default Windows boot loader has been set to be first in the display order."
#Copy DISM log to USBDrive
WriteLog "Copying dism log to $USBDrive"
invoke-process xcopy "X:\Windows\logs\dism\dism.log $USBDrive /Y"
@@ -1,10 +0,0 @@
select disk 0
select partition 3
Assign letter="W"
shrink minimum=1000
create partition primary
format quick fs=ntfs label="Recovery"
assign letter="R"
set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac"
gpt attributes=0x8000000000000001
exit
Binary file not shown.
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<settings pass="specialize">
<!--<ComputerName> must be in the first Component Element "Microsoft-Windows-Shell-Setup" . Do not change the order or remove it -->
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComputerName>MYCOMPUTER</ComputerName><!--Leave Default will be renamed-->
<TimeZone>Eastern Standard Time</TimeZone><!--Add Your Local TimeZone-->
</component>
<!-- Place additional Components Elements and Settings below here: -->
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RunASynchronous>
<RunASynchronousCommand wcm:action="add">
<Order>1</Order>
<Path>cmd.exe /c date 09-07-2024</Path> <!--Set the device clock to the current date. Helpful when BIOS clocks out of sync. -->
<Description>Set system date to a specific date</Description>
</RunASynchronousCommand>
</RunASynchronous>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<InputLocale>0409:00000409</InputLocale><!--Set your Keybaord and System Local https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-8.1-and-8/hh825682(v=win.10) -->
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<OOBE>
<ProtectYourPC>3</ProtectYourPC> <!--Disable Diagnostic Data sent to Microsoft-->
<HideEULAPage>true</HideEULAPage><!--Hide the End User License agreement -->
<HideWirelessSetupInOOBE>false</HideWirelessSetupInOOBE> <!--Show Wifi Setup -->
</OOBE>
</component>
</settings>
</unattend>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="specialize">
<!--<ComputerName> must be in the first Component Element "Microsoft-Windows-Shell-Setup" . Do not change the order or remove it -->
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="arm64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComputerName>MyComputer</ComputerName>
</component>
<!--Place addtional Components Elements and settings below here. -->
</settings>
</unattend>
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="specialize">
<!--<ComputerName> must be in the first Component Element "Microsoft-Windows-Shell-Setup" . Do not change the order or remove it -->
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComputerName>MyComputer</ComputerName>
</component>
<!--Place addtional Components Elements and settings below here. -->
</settings>
</unattend>
+101 -112
View File
@@ -1,124 +1,113 @@
# Using Full Flash Update (FFU) files to speed up Windows deployment
This repo contains the full FFU process that we use in US Education at Microsoft to help customers with large deployments of Windows as they prepare for the new school year. This process isn't limited to only large deployments at the start of the year, but is the most common.
What if you could have a Windows image (Windows 10/11 or Server) that has:
This process will copy Windows in about 2-3 minutes to the target device, optionally copy drivers, provisioning packages, Autopilot, etc. School technicians have even given the USB sticks to teachers and teachers calling them their "Magic USB sticks" to quickly get student devices reimaged in the event of an issue with their Windows PC.
- The latest Windows cumulative update
- The latest .NET cumulative update
- The latest Windows Defender Platform and Definition Updates
- The latest version of Microsoft Edge
- The latest version of OneDrive (Per-Machine)
- The latest version of Microsoft 365 Apps/Office
- The latest drivers from any of the major OEMs (Dell, HP, Lenovo, Microsoft) (yes, the latest, not some out of date enterprise CAB file from years ago)
- Winget support so you can integrate any app available from Winget directly in your image
- ARM64 support for the latest Copilot+ PCs
- The ability to bring your own drivers and apps if necessary
- Custom WinRE support
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.
And the best part: it takes less than two minutes to apply the image, even with all of these updates added to the media. After setting Windows up and going through Autopilot or a provisioning package, total elapsed time ~10 minutes (depending on what Intune or your device management tool is deploying).
The Full-Flash update (FFU) process can automatically download the latest release of Windows 11, the updates mentioned above, and creates a USB drive that can be used to quickly reimage a machine.
# Updates
**2404.2**
- If setting -installdrivers to $true and -logicalsectorsizebytes to 4096, the script will now set $copyDrivers to $true. This will create a drivers folder on the deploy partition of the USB drive with the drivers that were supposed to be added to the FFU. There's currently a bug with servicing FFUs with 4096 logical sector byte sizes. Prior to this fix, the script would tell the user to manually set -copydrivers to $true as workaround. This fix just does the workaround automatically.
**2404.1**
There's a big change with this release related to the ADK. The ADK will now be automatically updated to the latest ADK release. This is required in order to fix an issue with optimized FFUs not applying due to an issue with DISM/FFUProvider.dll. The FFUProvider.dll fix was added to the Sept 2023 ADK. Since we now have the ability to auto upgrade the ADK, I'm more confident in having the BuildFFUVM script creating a complete FFU now (prior it was only creating 3 partitions instead of 4 with the recovery partition - at deployment time, the ApplyFFU.ps1 script would create an empty recovery partition and Windows would populate it on first boot). Please open an issue if this creates a problem for you. I do realize that any new ADK release can have it's own challenges and issues and I do suspect we'll see a new ADK released later this year.
- Allow for ISOs with single index WIMs to work [Issue 10](https://github.com/rbalsleyMSFT/FFU/issues/10) - [Commit](https://github.com/rbalsleyMSFT/FFU/commit/9e2da741d53652e6e600ca19cfd38f507bd01fde)
- Added more robust ADK handling. Will now check for the latest ADK and download it if not installed. Thanks to [Zehadi Alam](https://github.com/zehadialam) [PR 18](https://github.com/rbalsleyMSFT/FFU/pull/18)
- Revert code back to allow optimized FFUs to be applied via ApplyFFU.ps1 now that Sept 2023 ADK release has FFUProvider.dll fix. [Commit](https://github.com/rbalsleyMSFT/FFU/commit/79364e334d6d09ff150e70dab7bfb2637d0ad8a8)
- Changed how the script searches for the latest CU. Instead of relying on the Windows release info page to grab the KB number, will just use the MU Catalog, the same as what we do for the .NET Framework. Windows release info page is updated manually and is unknown as to when it will be updated. [Commit](https://github.com/rbalsleyMSFT/FFU/commit/6fd5a4a41fd9ce2f842f43dc3a69bda264c29fa6)
- Added fix to not allow computer names with spaces. Thanks to [JoeMama54 (Rob)](https://github.com/JoeMama54) [PR 20](https://github.com/rbalsleyMSFT/FFU/pull/20)
**2403.1**
Fixed an issue with the SecurityHealthSetup.exe file giving an error when building the VM if -UpdateLatestDefender was set to $true. A new update for this came out on 3/21 which included a x64 and ARM64 binary. This file doesn't have an architecture designation to it, so it's impossible to know which file is for which architecture. Investigating to see if we can fix this in the Microsoft Update catalog. There is a web site to pull this from, but the support article is out of date.
Included ADK functions from Zehadi Alam [Introduce Automated ADK Retrieval and Installation Functions #14](https://github.com/rbalsleyMSFT/FFU/pull/14) to automate the installation of the ADK if it's not present. Thanks, Zehadi!
**2402.1**
**New functionality**
* If -BuildUSBDrve $true, script will now check for USB drive before continuing. If not present, script exits
* Added a number of new parameters.
| Parameter | Type | Description |
| -------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| CopyPEDrivers | Bool | When set to\$true, will copy the drivers from the \$FFUDevelopmentPath\PEDrivers folder to the WinPE deployment media. Default is \$false. |
| RemoveFFU | Bool | When set to\$true, will remove the FFU file from the\$FFUDevelopmentPath\FFU folder after it has been copied to the USB drive. Default is \$false. |
| UpdateLatestCU | Bool | When set to\$true, will download and install the latest cumulative update for Windows 10/11. Default is \$false. |
| UpdateLatestNet | Bool | When set to\$true, will download and install the latest .NET Framework for Windows 10/11. Default is \$false. |
| UpdateLatestDefender | Bool | When set to\$true, will download and install the latest Windows Defender definitions and Defender platform update. Default is \$false. |
| UpdateEdge | Bool | When set to\$true, will download and install the latest Microsoft Edge for Windows 10/11. Default is \$false. |
| UpdateOneDrive | Bool | When set to\$true, will download and install the latest OneDrive for Windows 10/11 and install it as a per machine installation instead of per user. Default is \$false. |
| CopyPPKG | Bool | When set to\$true, will copy the provisioning package from the \$FFUDevelopmentPath\PPKG folder to the Deployment partition of the USB drive. Default is \$false. |
| CopyUnattend | Bool | When set to\$true, will copy the \$FFUDevelopmentPath\Unattend folder to the Deployment partition of the USB drive. Default is \$false. |
| CopyAutopilot | Bool | When set to\$true, will copy the \$FFUDevelopmentPath\Autopilot folder to the Deployment partition of the USB drive. Default is \$false. |
| CompactOS | Bool | When set to\$true, will compact the OS when building the FFU. Default is \$true. |
| CleanupCaptureISO | Bool | When set to\$true, will remove the WinPE capture ISO after the FFU has been captured. Default is \$true. |
| CleanupDeployISO | Bool | When set to\$true, will remove the WinPE deployment ISO after the FFU has been captured. Default is \$true. |
| CleanupAppsISO | Bool | When set to\$true, will remove the Apps ISO after the FFU has been captured. Default is \$true. |
* Updated the docs with the new variables and made some minor modifications.
* Changed version variable to 2402.1
**2401.1**
- Added -CopyDrivers boolean parameter to control the ability to copy drivers to the USB drive in the deploy partition drivers folder.
- Changed version varaible to 2401.1
- When creating the scratch VHDX, switched it to create a dynamic VHDX instead of fixed
- Fixed an issue where adding drivers to the FFU would sometimes fail and would cause the script to exit unexpectedly
- Added -optimize boolean parameter to control whether the FFU is optimized or not. This defaults to $true and in most cases should be left this way.
- Fixed an issue where if the script failed to create the FFU and the old VM was left behind, it wouldn't clean it up if the VM was in the running state. Will now turn off any running VM with a name prefix of _FFU- and then remove any VMs with a name _FFU- if the environment is flagged as dirty.
- Fixed an issue where devices that ship with UFS drives were unable to image due to the script setting a LogicalSectorSizeBytes value of 512. If you're creating a FFU for devices that have UFS drives, you'll need to set -LogicalSectorSizeBytes 4096.
- There's a known issue where adding drivers to a FFU that has a LogicalSectorSizeBytes value of 4096. Added some code to prevent allowing this to happen. Please use -copydrivers $true as a workaround for now. We're investigating whether this is a bug or not.
- Fixed an issue where VHDX only captures (i.e. where -installapps $false) would not install Windows updates.
- Changed Office deployment to use Current channel instead of Monthly enterprise. If you want to change to Monthly Enterprise channel, it's recommended to leverage Intune.
**2309.2**
New Features
**Multiple USB Drive Support**
You can now plug in multiple USB drives (even using a USB hub) to create multiple USB drives for deployment. This is great for partners or customers who need to provide USB drives to their employees to image a large number of devices. It will copy the content to one USB drive at a time. The most USB drives we've seen created so far is 23 via a USB hub. Open an issue if you see any problems with this.
**Robocopy support**
Replaced Copy-Item with Robocopy when copying content to the USB drive(s). Copy-Item uses buffered IO, which can take a long time to copy large files. Robocopy with the /J switch allows for unbuffered IO support, which reduces the amount of time to copy.
**Better error handling**
Prior to 2309.2, if the script failed or you manually killed the script (ctrl+c, or closing the PowerShell window), the environment would end up in a bad state and you had to do a number of things to manually clean up the environment. Added a new function called Get-FFUEnvironment and a new text file called dirty.txt that gets created in the FFUDevelopment folder. When the script starts, it checks for the dirty.txt file and if it sees it, Get-FFUEnvironment runs and cleans out a number of things to help ensure the next run will complete successfully. Open an issue if you still see problems when the script fails and the next run of the script fails. 
Bug Fixes
- In 2309.1, added a 15 second sleep to allow for the registry to unload to fix a Critical Process Died error on deployment. In this build, increased that to 60 seconds.
- Fixed an issue where the script was incorrectly detecting the USB drive boot and deploy drive letters which caused issues when attempting to copy the WinPE files to the boot partition.
**2309.1**
- Fixed an issue with a Critical Process Died BSOD that would happen when using -installapps $false. More detailed information in the [commit](https://github.com/rbalsleyMSFT/FFU/pull/2/commits/34efbda7ec56dc7cb43ac42b058725d56c8b8899)
**2306.1.2**
- Fixed an issue where manually entering a name wouldn't name the computer as expected
**2306.1.1**
- Included some better error handling if defining optionalfeatures that require source folders (netfx3). ESD files don't have source folders like ISO media, which means installing .net 3.5 as an optional feature would fail. Also cleaned up some formatting.
**2306.1**
- Added support to automatically download the latest Windows 10 or 11 media via the media creation tool (thanks to [Michael](https://oofhours.com/2022/09/14/want-your-own-windows-11-21h2-arm64-isos/) for the idea). This also allows for different architecture, language, and media type support. If you omit the -ISOPath, the script will download the Windows 11 x64 English (US) consumer media.
An example command to download Windows 11 Pro x64 English (US) consumer media with Office and install drivers (it won't download drivers, you'll put those in your c:\FFUDevelopment\Drivers folder)
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'Name of your VM Switch in Hyper-V' -VMHostIPAddress 'Your IP Address' -CreateCaptureMedia $true -CreateDeploymentMedia $true -BuildUSBDrive $true -verbose
An example command to download Windows 11 Pro x64 French (CA) consumer media with Office and install drivers
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'Name of your VM Switch in Hyper-V' -VMHostIPAddress 'Your IP Address' -CreateCaptureMedia $true -CreateDeploymentMedia $true -BuildUSBDrive $true -WindowsRelease 11 -WindowsArch 'x64' -WindowsLang 'fr-ca' -MediaType 'consumer' -verbose
- Changed default size of System/EFI partition to 260MB from 256MB to accomodate 4Kn drives. 4Kn support needs more testing. I'm not confident yet that this can be done with VMs and FFUs.
- Added versioning with a new version parameter. Using YYMM as the format followed by a point release.
2412.1 has been released! Check out the changes in the [Change Log](ChangeLog.md)
# Getting Started
If you're not familiar with Github, you can click the Green code button above and select download zip. Extract the zip file and make sure to copy the FFUDevelopment folder to the root of your C: drive. That will make it easy to follow the guide and allow the scripts to work properly.
- Download the latest [release](https://github.com/rbalsleyMSFT/FFU/releases)
- Extract the FFUDevelopment folder from the ZIP file (recommend to C:\FFUDevelopment)
- Follow the doc: C:\FFUDevelopment\Docs\BuildDeployFFU.docx
If extracted correctly, your c:\FFUDevelopment folder should look like the following. If it does, go to c:\FFUDevelopment\Docs\BuildDeployFFU.docx to get started.
## YouTube Detailed Walkthrough
![image](https://github.com/rbalsleyMSFT/FFU/assets/53497092/5400a203-9c2e-42b2-b24c-ab8dfd922ba1)
The first 15 minutes of the following video includes a quick start demo to get started. Below the video are a list of chapters. This video was taken with the 2407.2 build. Features released after that will not be demonstrated in the video.
[![Reimage Windows Fast with Full-Flash Update (FFU))](https://img.youtube.com/vi/rqXRbgeeKSQ/maxresdefault.jpg)](https://www.youtube.com/watch?v=rqXRbgeeKSQ "Reimage Windows Fast with Full-Flash Update (FFU))")
Chapters:
[00:00](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=0s) Begin
[03:21](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=201s) Quick Start Prereqs
[07:19](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=439s) Quick Start Demo
[14:12](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=852s) Script Parameters
[17:22](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=1042s) Obtaining Windows Media
[25:55](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=1555s) Adding Applications
[26:59](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=1619s) Adding M365 Apps/Office
[29:21](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=1761s) Adding Applications via Winget
[34:59](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=2099s) Bring your own Applications
[36:01](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=2161s) Customizing InstallAppsAndSysprep.cmd
[38:34](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=2314s) Demo - Application Configuration
[49:43](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=2983s) Drivers
[55:39](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=3339s) Automatically downloading drivers
[57:28](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=3448s) Microsoft Surface drivers
[58:55](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=3535s) Dell drivers
[01:01:45](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=3705s) Lenovo drivers
[01:03:16](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=3796s) HP drivers
[01:05:25](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=3925s) Bring your own drivers
[01:06:24](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=3984s) Demo - Drivers
[01:11:55](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=4315s) Multi-model driver support
[01:13:21](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=4401s) Device naming
[01:18:30](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=4710s) Device enrollment
[01:21:43](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=4903s) Autopilot
[01:24:57](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=5097s) Provisioning packages
[01:26:54](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=5214s) Custom WinRE
[01:29:59](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=5399s) Demo - Putting it all together (Deep dive)
[01:32:06](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=5526s) Downloading Lenovo 500w drivers
[01:33:28](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=5608s) Downloading apps via Winget
[01:36:54](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=5814s) Downloading Office, Defender, Edge, OneDrive
[01:38:15](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=5895s) Building the Apps.iso
[01:39:08](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=5948s) Applying Windows to the VHDX
[01:40:16](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=6016s) Downloading and applying cumulative updates
[01:41:44](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=6104s) Building the VM
[01:48:13](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=6493s) Capturing the FFU
[01:53:38](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=6818s) Creating USB drive
[01:58:41](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=7121s) Deploying FFU
[02:11:48](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=7908s) Troubleshooting
[02:14:30](https://www.youtube.com/watch?v=rqXRbgeeKSQ&t=8070s) EDU Endpoint Office Hours
Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB