mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Reorganized files into FFUDevelopment folder
This commit is contained in:
@@ -1,54 +1,54 @@
|
||||
#Modify variables
|
||||
$rand = get-random
|
||||
$VMName = "_FFU-$rand"
|
||||
$VMPath = "c:\VM\$VMName"
|
||||
$VHDPath = "$VMPath\$VMName.vhdx"
|
||||
$ISOPath = "E:\software\ISOs\Windows\Windows 11\22H2\en-us_windows_11_consumer_editions_version_22h2_updated_jan_2023_x64_dvd_aafaf7fa.iso"
|
||||
$memory = 8GB
|
||||
$processors = 4
|
||||
|
||||
# 0. Delete old VMs
|
||||
|
||||
$vms = get-vm _ffu-* | ? {$_.state -ne 'running'}
|
||||
|
||||
If($null -ne $vms){
|
||||
Foreach ($vm in $vms){
|
||||
$OldVMName = $vm.VMName
|
||||
Remove-VM -Name $vm.name -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path "C:\VM\$OldVMName" -Force -Recurse -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# 1. Create Dynamic Hard Disk
|
||||
mkdir -Path $VMPath -Force
|
||||
#New-VHD -Path $VHDPath -Fixed -SizeBytes 30GB
|
||||
New-VHD -path $VHDPath -SizeBytes 128000000000 -LogicalSectorSizeBytes 512 -Dynamic
|
||||
|
||||
# 2. Create VM
|
||||
# - Name: _FFU
|
||||
# - Location: C:\VM\_FFU
|
||||
# - Generation: 2
|
||||
# - Memory: 4GB static
|
||||
# - Networking: Not connected
|
||||
# - Connect to existing VHD: c:\VM\_FFU\
|
||||
# - Mount ISO
|
||||
New-VM -Name $VMName -Path $VMPath -MemoryStartupBytes $memory -VHDPath $VHDPath -Generation 2
|
||||
Set-VMProcessor -VMName $VMName -Count $processors
|
||||
Add-VMDvdDrive -VMName $VMName -Path $ISOPath
|
||||
$VMDVDDrive = Get-VMDvdDrive -VMName $VMName
|
||||
|
||||
#Use for Win11
|
||||
Set-VMFirmware -VMName $VMName -FirstBootDevice $VMDVDDrive
|
||||
If(Get-HgsGuardian -Name 'Guardian'){
|
||||
Remove-HgsGuardian -Name 'Guardian'
|
||||
Get-ChildItem -Path 'Cert:\LocalMachine\Shielded VM Local Certificates\' | Remove-Item
|
||||
}
|
||||
New-HgsGuardian -Name 'Guardian' -GenerateCertificates
|
||||
$owner = get-hgsguardian -Name 'Guardian'
|
||||
$kp = New-HgsKeyProtector -Owner $owner -AllowUntrustedRoot
|
||||
Set-VMKeyProtector -VMName $VMName -KeyProtector $kp.RawData
|
||||
Enable-VMTPM -VMName $VMName
|
||||
|
||||
#Use if creating for MDT/SCCM Builds
|
||||
#Get-VM $VMName | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName "Intel(R) I350 Gigabit Network Connection - Virtual Switch"
|
||||
#Modify variables
|
||||
$rand = get-random
|
||||
$VMName = "_FFU-$rand"
|
||||
$VMPath = "c:\VM\$VMName"
|
||||
$VHDPath = "$VMPath\$VMName.vhdx"
|
||||
$ISOPath = "E:\software\ISOs\Windows\Windows 11\22H2\en-us_windows_11_consumer_editions_version_22h2_updated_jan_2023_x64_dvd_aafaf7fa.iso"
|
||||
$memory = 8GB
|
||||
$processors = 4
|
||||
|
||||
# 0. Delete old VMs
|
||||
|
||||
$vms = get-vm _ffu-* | ? {$_.state -ne 'running'}
|
||||
|
||||
If($null -ne $vms){
|
||||
Foreach ($vm in $vms){
|
||||
$OldVMName = $vm.VMName
|
||||
Remove-VM -Name $vm.name -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path "C:\VM\$OldVMName" -Force -Recurse -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# 1. Create Dynamic Hard Disk
|
||||
mkdir -Path $VMPath -Force
|
||||
#New-VHD -Path $VHDPath -Fixed -SizeBytes 30GB
|
||||
New-VHD -path $VHDPath -SizeBytes 128000000000 -LogicalSectorSizeBytes 512 -Dynamic
|
||||
|
||||
# 2. Create VM
|
||||
# - Name: _FFU
|
||||
# - Location: C:\VM\_FFU
|
||||
# - Generation: 2
|
||||
# - Memory: 4GB static
|
||||
# - Networking: Not connected
|
||||
# - Connect to existing VHD: c:\VM\_FFU\
|
||||
# - Mount ISO
|
||||
New-VM -Name $VMName -Path $VMPath -MemoryStartupBytes $memory -VHDPath $VHDPath -Generation 2
|
||||
Set-VMProcessor -VMName $VMName -Count $processors
|
||||
Add-VMDvdDrive -VMName $VMName -Path $ISOPath
|
||||
$VMDVDDrive = Get-VMDvdDrive -VMName $VMName
|
||||
|
||||
#Use for Win11
|
||||
Set-VMFirmware -VMName $VMName -FirstBootDevice $VMDVDDrive
|
||||
If(Get-HgsGuardian -Name 'Guardian'){
|
||||
Remove-HgsGuardian -Name 'Guardian'
|
||||
Get-ChildItem -Path 'Cert:\LocalMachine\Shielded VM Local Certificates\' | Remove-Item
|
||||
}
|
||||
New-HgsGuardian -Name 'Guardian' -GenerateCertificates
|
||||
$owner = get-hgsguardian -Name 'Guardian'
|
||||
$kp = New-HgsKeyProtector -Owner $owner -AllowUntrustedRoot
|
||||
Set-VMKeyProtector -VMName $VMName -KeyProtector $kp.RawData
|
||||
Enable-VMTPM -VMName $VMName
|
||||
|
||||
#Use if creating for MDT/SCCM Builds
|
||||
#Get-VM $VMName | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName "Intel(R) I350 Gigabit Network Connection - Virtual Switch"
|
||||
#$networkAdapter = Get-VMNetworkAdapter -VMName $VMName
|
||||
@@ -0,0 +1,20 @@
|
||||
rd c:\WinPE /S /Q
|
||||
md c:\WinPEOutput
|
||||
cmd /c copype amd64 c:\WinPE
|
||||
Dism /Mount-Image /ImageFile:"c:\WinPE\media\sources\boot.wim" /Index:1 /MountDir:"c:\WinPE\mount"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-WMI.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-WMI_en-us.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-NetFX.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-NetFX_en-us.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-Scripting.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-Scripting_en-us.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-PowerShell.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-PowerShell_en-us.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-StorageWMI.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-StorageWMI_en-us.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-DismCmdlets.cab"
|
||||
Dism /Add-Package /Image:"c:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-DismCmdlets_en-us.cab"
|
||||
xcopy "C:\FFU Development\WinPECaptureFFUFiles" c:\WinPE\mount /Y /E
|
||||
Dism /Unmount-Image /MountDir:c:\WinPE\mount /Commit
|
||||
MakeWinPEMedia /ISO /F c:\WinPE "C:\WinPEOutput\WinPE_FFU_Capture.iso"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
rd c:\WinPE /S /Q
|
||||
cmd /c copype amd64 C:\WinPE
|
||||
Dism /Mount-Image /ImageFile:"C:\WinPE\media\sources\boot.wim" /Index:1 /MountDir:"C:\WinPE\mount"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-WMI.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-WMI_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-NetFX.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-NetFX_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-Scripting.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-Scripting_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-PowerShell.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-PowerShell_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-StorageWMI.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-StorageWMI_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-DismCmdlets.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-DismCmdlets_en-us.cab"
|
||||
REM Dism /image:"C:\WinPE\mount" /add-driver /Driver:C:\temp\drivers\LGO\Battery /recurse
|
||||
xcopy "C:\FFU Development\WinPEDeployFFUFiles" c:\WinPE\mount /Y /E
|
||||
Dism /Unmount-Image /MountDir:C:\WinPE\mount /Commit
|
||||
MakeWinPEMedia /ISO /F C:\WinPE "C:\FFU Development\PE\WinPE_22H2.iso"
|
||||
@@ -0,0 +1,19 @@
|
||||
rd c:\WinPE /S /Q
|
||||
cmd /c copype amd64 C:\WinPE
|
||||
Dism /Mount-Image /ImageFile:"C:\WinPE\media\sources\boot.wim" /Index:1 /MountDir:"C:\WinPE\mount"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-WMI.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-WMI_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-NetFX.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-NetFX_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-Scripting.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-Scripting_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-PowerShell.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-PowerShell_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-StorageWMI.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-StorageWMI_en-us.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-DismCmdlets.cab"
|
||||
Dism /Add-Package /Image:"C:\WinPE\mount" /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-DismCmdlets_en-us.cab"
|
||||
REM Dism /image:"C:\WinPE\mount" /add-driver /Driver:C:\temp\drivers\LGO\Battery /recurse
|
||||
xcopy "C:\FFU Development\WinPEDeployFFUFilesVM" c:\WinPE\mount /Y /E
|
||||
Dism /Unmount-Image /MountDir:C:\WinPE\mount /Commit
|
||||
MakeWinPEMedia /ISO /F C:\WinPE "C:\FFU Development\PE\WinPE_21H2VM.iso"
|
||||
@@ -0,0 +1,19 @@
|
||||
#Modify variables
|
||||
$ISOPath = 'C:\ffu\WinPE_FFU_Capture.iso'
|
||||
|
||||
$vms = get-vm _ffu* | ? {$_.state -ne 'running'}
|
||||
|
||||
|
||||
If($null -ne $vms){
|
||||
Foreach ($vm in $vms){
|
||||
$VMName = $vm.name
|
||||
$VMDVDDrive = Get-VMDvdDrive -VMName $VMName
|
||||
Set-VMFirmware -VMName $VMName -FirstBootDevice $VMDVDDrive
|
||||
Set-VMDvdDrive -VMName $VMName -Path $ISOPath
|
||||
$VMSwitch = Get-VMSwitch -name *intel*
|
||||
get-vm $VMName | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName $VMSwitch.Name
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<Configuration ID="efa6df21-a106-428e-8eaa-d89a5dda6030">
|
||||
<Add OfficeClientEdition="64" Channel="MonthlyEnterprise">
|
||||
<Product ID="O365ProPlusRetail">
|
||||
<Language ID="MatchOS" />
|
||||
<ExcludeApp ID="Access" />
|
||||
<ExcludeApp ID="Lync" />
|
||||
<ExcludeApp ID="Publisher" />
|
||||
<ExcludeApp ID="Bing" />
|
||||
</Product>
|
||||
</Add>
|
||||
<Property Name="SharedComputerLicensing" Value="0" />
|
||||
<Property Name="FORCEAPPSHUTDOWN" Value="FALSE" />
|
||||
<Property Name="DeviceBasedLicensing" Value="0" />
|
||||
<Property Name="SCLCacheOverride" Value="0" />
|
||||
<Updates Enabled="TRUE" />
|
||||
<Display Level="None" AcceptEULA="TRUE" />
|
||||
</Configuration>
|
||||
@@ -1,3 +1,3 @@
|
||||
d:\setup.exe /configure d:\FFU.xml
|
||||
taskkill /IM sysprep.exe
|
||||
d:\setup.exe /configure d:\FFU.xml
|
||||
taskkill /IM sysprep.exe
|
||||
c:\windows\system32\sysprep\sysprep.exe /quiet /generalize /oobe
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
select disk 0
|
||||
select partition 3
|
||||
Assign letter="M"
|
||||
exit
|
||||
select disk 0
|
||||
select partition 3
|
||||
Assign letter="M"
|
||||
exit
|
||||
+58
-58
@@ -1,58 +1,58 @@
|
||||
#Modify the net use path to map the W: drive to the location you want to copy the FFU file to
|
||||
net use W: \\192.168.1.2\c$\temp /user:administrator p@ssw0rd
|
||||
|
||||
$AssignDriveLetter = 'x:\AssignDriveLetter.txt'
|
||||
Start-Process -FilePath diskpart.exe -ArgumentList "/S $AssignDriveLetter" -Wait -ErrorAction Stop | Out-Null
|
||||
#Load Registry Hive
|
||||
$Software = 'M:\Windows\System32\config\software'
|
||||
reg load "HKLM\FFU" $Software
|
||||
|
||||
#Find Windows version values
|
||||
|
||||
$SKU = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'EditionID'
|
||||
[int]$CurrentBuild = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'CurrentBuild'
|
||||
$DisplayVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
|
||||
$BuildDate = Get-Date -uformat %b%Y
|
||||
|
||||
$SKU = switch ($SKU){
|
||||
Home {'Home'}
|
||||
Professional {'Pro'}
|
||||
ProfessionalEducation {'Pro_Edu'}
|
||||
Enterprise {'Ent'}
|
||||
}
|
||||
|
||||
if($CurrentBuild -ge 22000){
|
||||
$Name = 'Win11'
|
||||
}
|
||||
else{
|
||||
$Name = 'Win10'
|
||||
}
|
||||
|
||||
#If Office is installed, modify the file name of the FFU
|
||||
$Office = Get-childitem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue | Out-Null
|
||||
if($Office){
|
||||
$ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_Office`_$BuildDate.ffu"
|
||||
$dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
|
||||
|
||||
|
||||
}
|
||||
else{
|
||||
$ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_$BuildDate.ffu"
|
||||
$dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
|
||||
|
||||
}
|
||||
|
||||
#Unload Registry
|
||||
Set-Location X:\
|
||||
Remove-Variable SKU
|
||||
Remove-Variable CurrentBuild
|
||||
Remove-Variable DisplayVersion
|
||||
Remove-Variable Office
|
||||
reg unload "HKLM\FFU"
|
||||
|
||||
Start-Process -FilePath dism.exe -ArgumentList $dismArgs -Wait -PassThru -ErrorAction Stop | Out-Null
|
||||
$dismOptArgs = "/optimize-ffu /imagefile:$ffuFilePath"
|
||||
Start-Process -FilePath dism.exe -ArgumentList $dismOptArgs -Wait -PassThru -ErrorAction Stop | Out-Null
|
||||
#Copy DISM log to Host
|
||||
xcopy X:\Windows\logs\dism\dism.log W:\ /Y | Out-Null
|
||||
shutdown /p
|
||||
#Modify the net use path to map the W: drive to the location you want to copy the FFU file to
|
||||
net use W: \\192.168.1.2\c$\temp /user:administrator p@ssw0rd
|
||||
|
||||
$AssignDriveLetter = 'x:\AssignDriveLetter.txt'
|
||||
Start-Process -FilePath diskpart.exe -ArgumentList "/S $AssignDriveLetter" -Wait -ErrorAction Stop | Out-Null
|
||||
#Load Registry Hive
|
||||
$Software = 'M:\Windows\System32\config\software'
|
||||
reg load "HKLM\FFU" $Software
|
||||
|
||||
#Find Windows version values
|
||||
|
||||
$SKU = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'EditionID'
|
||||
[int]$CurrentBuild = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'CurrentBuild'
|
||||
$DisplayVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
|
||||
$BuildDate = Get-Date -uformat %b%Y
|
||||
|
||||
$SKU = switch ($SKU){
|
||||
Home {'Home'}
|
||||
Professional {'Pro'}
|
||||
ProfessionalEducation {'Pro_Edu'}
|
||||
Enterprise {'Ent'}
|
||||
}
|
||||
|
||||
if($CurrentBuild -ge 22000){
|
||||
$Name = 'Win11'
|
||||
}
|
||||
else{
|
||||
$Name = 'Win10'
|
||||
}
|
||||
|
||||
#If Office is installed, modify the file name of the FFU
|
||||
$Office = Get-childitem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue | Out-Null
|
||||
if($Office){
|
||||
$ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_Office`_$BuildDate.ffu"
|
||||
$dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
|
||||
|
||||
|
||||
}
|
||||
else{
|
||||
$ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_$BuildDate.ffu"
|
||||
$dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
|
||||
|
||||
}
|
||||
|
||||
#Unload Registry
|
||||
Set-Location X:\
|
||||
Remove-Variable SKU
|
||||
Remove-Variable CurrentBuild
|
||||
Remove-Variable DisplayVersion
|
||||
Remove-Variable Office
|
||||
reg unload "HKLM\FFU"
|
||||
|
||||
Start-Process -FilePath dism.exe -ArgumentList $dismArgs -Wait -PassThru -ErrorAction Stop | Out-Null
|
||||
$dismOptArgs = "/optimize-ffu /imagefile:$ffuFilePath"
|
||||
Start-Process -FilePath dism.exe -ArgumentList $dismOptArgs -Wait -PassThru -ErrorAction Stop | Out-Null
|
||||
#Copy DISM log to Host
|
||||
xcopy X:\Windows\logs\dism\dism.log W:\ /Y | Out-Null
|
||||
shutdown /p
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
wpeinit
|
||||
powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
||||
powershell -Noprofile -ExecutionPolicy Bypass -File x:\CaptureFFU.ps1
|
||||
exit
|
||||
|
||||
wpeinit
|
||||
powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
||||
powershell -Noprofile -ExecutionPolicy Bypass -File x:\CaptureFFU.ps1
|
||||
exit
|
||||
|
||||
@@ -0,0 +1,579 @@
|
||||
function Get-USBDrive(){
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Removable' -and $_.FileSystemType -eq 'NTFS'}).DriveLetter
|
||||
if ($null -eq $USBDriveLetter){
|
||||
#Must be using a fixed USB drive - difficult to grab drive letter from win32_diskdrive. Assume user followed instructions and used Deploy as the friendly name for partition
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Fixed' -and $_.FileSystemType -eq 'NTFS' -and $_.FileSystemLabel -eq 'Deploy'}).DriveLetter
|
||||
#If we didn't get the drive letter, stop the script.
|
||||
if ($null -eq $USBDriveLetter){
|
||||
WriteLog 'Cannot find USB drive letter - most likely using a fixed USB drive. Name the 2nd partition with the FFU files as Deploy so the script can grab the drive letter. Exiting'
|
||||
Exit
|
||||
}
|
||||
|
||||
}
|
||||
$USBDriveLetter = $USBDriveLetter + ":\"
|
||||
return $USBDriveLetter
|
||||
}
|
||||
|
||||
function Get-HardDrive(){
|
||||
$DeviceID = (Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' -and $_.Model -ne 'Microsoft Virtual Disk'}).DeviceID
|
||||
return $DeviceID
|
||||
}
|
||||
|
||||
function WriteLog($LogText){
|
||||
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText"
|
||||
}
|
||||
|
||||
function Set-DiskpartAnswerFiles($DiskpartFile,$DiskID){
|
||||
(Get-Content $DiskpartFile).Replace('disk 0', "disk $DiskID") | Set-Content -Path $DiskpartFile
|
||||
}
|
||||
|
||||
function Set-Computername($computername){
|
||||
[xml]$xml = Get-Content $UnattendFile
|
||||
if($xml.unattend.settings.component.Count -ge 2){
|
||||
#Assumes that Computername is the first component element
|
||||
$xml.unattend.settings.component[0].ComputerName = $computername
|
||||
}else{
|
||||
$xml.unattend.settings.component.ComputerName = $computername
|
||||
}
|
||||
$xml.Save($UnattendFile)
|
||||
return $computername
|
||||
}
|
||||
|
||||
function Invoke-Process {
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param
|
||||
(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$FilePath,
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$ArgumentList
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
try {
|
||||
$stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
$stdErrTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
|
||||
$startProcessParams = @{
|
||||
FilePath = $FilePath
|
||||
ArgumentList = $ArgumentList
|
||||
RedirectStandardError = $stdErrTempFile
|
||||
RedirectStandardOutput = $stdOutTempFile
|
||||
Wait = $true;
|
||||
PassThru = $true;
|
||||
NoNewWindow = $false;
|
||||
}
|
||||
if ($PSCmdlet.ShouldProcess("Process [$($FilePath)]", "Run with args: [$($ArgumentList)]")) {
|
||||
$cmd = Start-Process @startProcessParams
|
||||
$cmdOutput = Get-Content -Path $stdOutTempFile -Raw
|
||||
$cmdError = Get-Content -Path $stdErrTempFile -Raw
|
||||
if ($cmd.ExitCode -ne 0) {
|
||||
if ($cmdError) {
|
||||
throw $cmdError.Trim()
|
||||
}
|
||||
if ($cmdOutput) {
|
||||
throw $cmdOutput.Trim()
|
||||
}
|
||||
} else {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
WriteLog $cmdOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
#$PSCmdlet.ThrowTerminatingError($_)
|
||||
WriteLog $_
|
||||
Write-Host 'Script failed - check scriptlog.txt on the USB drive for more info'
|
||||
throw $_
|
||||
|
||||
} finally {
|
||||
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# This function can be used in instances where battery level might matter (e.g. installing firmware for Surface). The problem is that WinPE doesn't have
|
||||
# a driver for the battery installed, so you'll need to inject drivers, which can be tricky because just injecting the battery driver might not be enough,
|
||||
# you might also need other drivers that the battery driver is dependent on.
|
||||
# function Get-Battery(){
|
||||
# while (($BattLev = (Get-CimInstance win32_battery).EstimatedChargeRemaining) -lt "35")
|
||||
# {
|
||||
# WriteLog "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Write-Host "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Start-Sleep 60
|
||||
# }
|
||||
|
||||
# WriteLog "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# Write-Host "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# }
|
||||
|
||||
#Get USB Drive and create log file
|
||||
$LogFileName = 'ScriptLog.txt'
|
||||
$USBDrive = Get-USBDrive
|
||||
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
|
||||
$LogFile = $USBDrive + $LogFilename
|
||||
WriteLog 'Begin Logging'
|
||||
|
||||
#Find PhysicalDrive
|
||||
$PhysicalDeviceID = Get-HardDrive
|
||||
WriteLog "Physical DeviceID is $PhysicalDeviceID"
|
||||
|
||||
#Parse DiskID Number
|
||||
$DiskID = $PhysicalDeviceID.substring($PhysicalDeviceID.length - 1,1)
|
||||
WriteLog "DiskID is $DiskID"
|
||||
|
||||
#Modify diskpart answer files if DiskID not 0
|
||||
# $UEFIFFUPartitions = 'x:\CreateUEFI-FFU-Partitions.txt'
|
||||
# $ExtendPartition = 'x:\ExtendPartition-UEFI.txt'
|
||||
|
||||
# If ($DiskID -ne '0'){
|
||||
# WriteLog 'DiskID is not 0. Need to modify diskpart answer files'
|
||||
# try {
|
||||
# Set-DiskpartAnswerFiles $UEFIFFUPartitions $DiskID
|
||||
# }
|
||||
# catch {
|
||||
# WriteLog "Modifying $UEFIFFUPartitions failed with error: $_"
|
||||
# }
|
||||
|
||||
# try {
|
||||
# Set-DiskpartAnswerFiles $ExtendPartition $DiskID
|
||||
# }
|
||||
# catch {
|
||||
# WriteLog "Modifying $ExtendPartition failed with error: $_"
|
||||
# }
|
||||
# }
|
||||
|
||||
#Find FFU Files
|
||||
[array]$FFUFiles = @(Get-ChildItem -Path $USBDrive*.ffu)
|
||||
$FFUCount = $FFUFiles.Count
|
||||
|
||||
#If multiple FFUs found, ask which to install
|
||||
If ($FFUCount -gt 1) {
|
||||
WriteLog "Found $FFUCount FFU Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $FFUCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; FFUFile = $FFUFiles[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, FFUFile
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$FFUSelected = Read-Host 'Enter the FFU number to install'
|
||||
$FFUSelected = $FFUSelected -1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($FFUSelected -le $FFUCount -1) -and $var)
|
||||
|
||||
$FFUFileToInstall = $array[$FFUSelected].FFUFile
|
||||
WriteLog "$FFUFileToInstall was selected"
|
||||
}
|
||||
elseif ($FFUCount -eq 1) {
|
||||
WriteLog "Found $FFUCount FFU File"
|
||||
$FFUFileToInstall = $FFUFiles[0].FullName
|
||||
WriteLog "$FFUFileToInstall will be installed"
|
||||
}
|
||||
else {
|
||||
Writelog 'No FFU files found'
|
||||
Write-Host 'No FFU files found'
|
||||
Exit
|
||||
}
|
||||
|
||||
#FindAP
|
||||
$APFolder = $USBDrive + "Autopilot\"
|
||||
If (Test-Path -Path $APFolder){
|
||||
[array]$APFiles = @(Get-ChildItem -Path $APFolder*.json)
|
||||
$APFilesCount = $APFiles.Count
|
||||
if ($APFilesCount -ge 1){
|
||||
$autopilot = $true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#FindPPKG
|
||||
$PPKGFolder = $USBDrive + "PPKG\"
|
||||
if (Test-Path -Path $PPKGFolder){
|
||||
[array]$PPKGFiles = @(Get-ChildItem -Path $PPKGFolder*.ppkg)
|
||||
$PPKGFilesCount = $PPKGFiles.Count
|
||||
if ($PPKGFilesCount -ge 1){
|
||||
$PPKG = $true
|
||||
}
|
||||
}
|
||||
|
||||
#FindUnattend
|
||||
$UnattendFolder = $USBDrive + "unattend\"
|
||||
$UnattendFilePath = $UnattendFolder + "unattend.xml"
|
||||
$UnattendPrefixPath = $UnattendFolder + "prefixes.txt"
|
||||
If (Test-Path -Path $UnattendFilePath){
|
||||
$UnattendFile = Get-ChildItem -Path $UnattendFilePath
|
||||
If ($UnattendFile){
|
||||
$Unattend = $true
|
||||
}
|
||||
}
|
||||
If (Test-Path -Path $UnattendPrefixPath){
|
||||
$UnattendPrefixFile = Get-ChildItem -Path $UnattendPrefixPath
|
||||
If ($UnattendPrefixFile){
|
||||
$UnattendPrefix = $true
|
||||
}
|
||||
}
|
||||
|
||||
#Ask for device name if unattend exists
|
||||
if ($Unattend -and $UnattendPrefix){
|
||||
Writelog 'Unattend file found with prefixes.txt. Getting prefixes.'
|
||||
$UnattendPrefixes = @(Get-content $UnattendPrefixFile)
|
||||
$UnattendPrefixCount = $UnattendPrefixes.Count
|
||||
If ($UnattendPrefixCount -gt 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefixes"
|
||||
$array = @()
|
||||
for($i=0;$i -le $UnattendPrefixCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; DeviceNamePrefix = $UnattendPrefixes[$i]}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, DeviceNamePrefix
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PrefixSelected = Read-Host 'Enter the prefix number to use for the device name'
|
||||
$PrefixSelected = $PrefixSelected -1
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid prefix number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PrefixSelected -le $UnattendPrefixCount -1) -and $var)
|
||||
$PrefixToUse = $array[$PrefixSelected].DeviceNamePrefix
|
||||
WriteLog "$PrefixToUse was selected"
|
||||
}
|
||||
elseif ($UnattendPrefixCount -eq 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefix"
|
||||
$PrefixToUse = $UnattendPrefixes[0]
|
||||
WriteLog "Will use $PrefixToUse as device name prefix"
|
||||
}
|
||||
#Get serial number to append. This can make names longer than 15 characters. Trim any leading or trailing whitespace
|
||||
$serial = (Get-CimInstance -ClassName win32_bios).SerialNumber.Trim()
|
||||
#Combine prefix with serial
|
||||
$computername = $PrefixToUse + $serial
|
||||
#If computername is longer than 15 characters, reduce to 15. Sysprep/unattend doesn't like ComputerName being longer than 15 characters even though Windows accepts it
|
||||
If ($computername.Length -gt 15){
|
||||
$computername = $computername.substring(0,15)
|
||||
}
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
elseif($Unattend){
|
||||
Writelog 'Unattend file found with no prefixes.txt, asking for name'
|
||||
[string]$computername = Read-Host 'Enter device name'
|
||||
Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
else {
|
||||
WriteLog 'No unattend folder found. Device name will be set via PPKG, AP JSON, or default OS name.'
|
||||
}
|
||||
|
||||
#If both AP and PPKG folder found with files, ask which to use.
|
||||
If($autopilot -eq $true -and $PPKG -eq $true){
|
||||
WriteLog 'Both PPKG and Autopilot json files found'
|
||||
Write-Host 'Both Autopilot JSON files and Provisioning packages were found.'
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APorPPKG = Read-Host 'Enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Incorrect value. Please enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APorPPKG -gt 0 -and $APorPPKG -lt 3) -and $var)
|
||||
If ($APorPPKG -eq 1){
|
||||
$PPKG = $false
|
||||
}
|
||||
else{
|
||||
$autopilot = $false
|
||||
}
|
||||
}
|
||||
|
||||
#If multiple AP json files found, ask which to install
|
||||
If ($APFilesCount -gt 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount Autopilot json Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $APFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; APFile = $APFiles[$i].FullName; APFileName = $APFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, APFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APFileSelected = Read-Host 'Enter the AP json file number to install'
|
||||
$APFileSelected = $APFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid AP json file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APFileSelected -le $APFilesCount -1) -and $var)
|
||||
|
||||
$APFileToInstall = $array[$APFileSelected].APFile
|
||||
$APFileName = $array[$APFileSelected].APFileName
|
||||
WriteLog "$APFileToInstall was selected"
|
||||
}
|
||||
elseif ($APFilesCount -eq 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount AP File"
|
||||
$APFileToInstall = $APFiles[0].FullName
|
||||
$APFileName = $APFiles[0].Name
|
||||
WriteLog "$APFileToInstall will be copied"
|
||||
}
|
||||
else {
|
||||
Writelog 'No AP files found or AP was not selected'
|
||||
}
|
||||
|
||||
#If multiple PPKG files found, ask which to install
|
||||
If ($PPKGFilesCount -gt 1 -and $PPKG -eq $true) {
|
||||
WriteLog "Found $PPKGFilesCount PPKG Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $PPKGFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; PPKGFile = $PPKGFiles[$i].FullName; PPKGFileName = $PPKGFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, PPKGFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PPKGFileSelected = Read-Host 'Enter the PPKG file number to install'
|
||||
$PPKGFileSelected = $PPKGFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid PPKG file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PPKGFileSelected -le $PPKGFilesCount -1) -and $var)
|
||||
|
||||
$PPKGFileToInstall = $array[$PPKGFileSelected].PPKGFile
|
||||
WriteLog "$PPKGFileToInstall was selected"
|
||||
}
|
||||
elseif ($PPKGFilesCount -eq 1 -and $PPKG -eq $true) {
|
||||
WriteLog "Found $PPKGFilesCount PPKG File"
|
||||
$PPKGFileToInstall = $PPKGFiles[0].FullName
|
||||
WriteLog "$PPKGFileToInstall will be used"
|
||||
}
|
||||
else {
|
||||
Writelog 'No PPKG files found or PPKG not selected.'
|
||||
}
|
||||
|
||||
#Find Drivers
|
||||
$Drivers = $USBDrive + "Drivers"
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
#Check if multiple driver folders found, if so, just select one folder to save time/space
|
||||
$DriverFolders = Get-ChildItem -Path $Drivers
|
||||
$DriverFoldersCount = $DriverFolders.count
|
||||
If ($DriverFoldersCount -gt 1)
|
||||
{
|
||||
WriteLog "Found $DriverFoldersCount driver folders"
|
||||
$array = @()
|
||||
|
||||
for($i=0; $i -le $DriverFoldersCount -1; $i++){
|
||||
$Properties = [ordered]@{Number = $i + 1; Drivers = $DriverFolders[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, Drivers
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$DriversSelected = Read-Host 'Enter the set of drivers to install'
|
||||
$DriversSelected = $DriversSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid driver folder number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($DriversSelected -le $DriverFoldersCount -1) -and $var)
|
||||
|
||||
$Drivers = $array[$DriversSelected].Drivers
|
||||
WriteLog "$Drivers was selected"
|
||||
}
|
||||
elseif ($DriverFoldersCount -eq 1) {
|
||||
WriteLog "Found $DriverFoldersCount driver folder"
|
||||
$Drivers = $DriverFolders.FullName
|
||||
WriteLog "$Drivers will be installed"
|
||||
}
|
||||
else {
|
||||
Writelog 'No driver folders found'
|
||||
}
|
||||
}
|
||||
|
||||
#If you want to enable battery level checking, uncomment the line below as well as the Get-Battery function near the top of the script
|
||||
#Get-Battery
|
||||
|
||||
#Partition drive
|
||||
Writelog 'Clean Disk'
|
||||
#Start-Process -FilePath diskpart.exe -ArgumentList "/S $UEFIFFUPartitions" -Wait -ErrorAction Stop | Out-File $Logfile -Append
|
||||
#Invoke-Process diskpart.exe "/S $UEFIFFUPartitions"
|
||||
try {
|
||||
$Disk = Get-Disk -Number $DiskID
|
||||
$Disk | clear-disk -RemoveData -RemoveOEM -Confirm:$false
|
||||
}
|
||||
catch {
|
||||
WriteLog 'Cleaning disk failed. Exiting'
|
||||
throw $_
|
||||
}
|
||||
|
||||
Writelog 'Cleaning Disk succeeded'
|
||||
|
||||
#Apply FFU
|
||||
WriteLog "Applying FFU to $PhysicalDeviceID"
|
||||
WriteLog "Running command dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID"
|
||||
#In order for Applying Image progress bar to show up, need to call dism directly. Might be a better way to handle, but must have progress bar show up on screen.
|
||||
dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID
|
||||
if($LASTEXITCODE -eq 0){
|
||||
WriteLog 'Successfully applied FFU'
|
||||
}
|
||||
else{
|
||||
Writelog "Failed to apply FFU - LastExitCode = $LASTEXITCODE also check dism.log on the USB drive for more info"
|
||||
#Copy DISM log to USBDrive
|
||||
invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
exit
|
||||
}
|
||||
|
||||
#Remove recovery partition - this is needed in order to extend the Windows partition so it uses the full disk size. If dism /optimize-ffu worked, this wouldn't be needed
|
||||
# $disk = get-disk -Number $DiskID
|
||||
# $RecoveryPartition = $disk | get-partition | Where-Object {$_.type -eq 'Recovery'}
|
||||
# if ($RecoveryPartition){
|
||||
# $RecoveryPartitionNumber = $RecoveryPartition.PartitionNumber
|
||||
# if ($RecoveryPartitionNumber -eq 4){
|
||||
# try {
|
||||
# WriteLog 'Removing recovery partition'
|
||||
# Remove-partition -DiskNumber $DiskID -PartitionNumber $RecoveryPartitionNumber -Confirm:$false
|
||||
# }
|
||||
# catch {
|
||||
# WriteLog 'Error removing recovery partition, exiting'
|
||||
# throw $_
|
||||
# }
|
||||
# }
|
||||
# else{
|
||||
# WriteLog 'Recovery partition not partition 4. Script will exit. Please create the FFU with the recovery partition as the last partition. This is the default and recommended way.'
|
||||
# exit
|
||||
# }
|
||||
# }
|
||||
|
||||
#Extend Windows partition and create recovery partition
|
||||
# Writelog 'Extending Windows partition'
|
||||
# Invoke-Process diskpart.exe "/S $ExtendPartition"
|
||||
# if($LASTEXITCODE -eq 0){
|
||||
# WriteLog 'Successfully extended Windows partition and created recovery partition'
|
||||
# }
|
||||
# else{
|
||||
# Writelog "Failed to extend Windows partition and/or create recovery partition - LastExitCode = $LASTEXITCODE"
|
||||
# }
|
||||
|
||||
#Copy modified WinRE if folder exists, else copy inbox WinRE
|
||||
$WinRE = $USBDrive + "WinRE\winre.wim"
|
||||
If (Test-Path -Path $WinRE)
|
||||
{
|
||||
WriteLog 'Copying modified WinRE to Recovery directory'
|
||||
Invoke-Process xcopy.exe "/h $WinRE R:\Recovery\WindowsRE\ /Y"
|
||||
WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
WriteLog 'Registering location of recovery tools'
|
||||
Invoke-Process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
WriteLog 'Registering location of recovery tools succeeded'
|
||||
}
|
||||
# else
|
||||
# {
|
||||
# WriteLog 'Copying default WinRE to Recovery directory'
|
||||
# Invoke-Process xcopy.exe "/h W:\Windows\System32\Recovery\Winre.wim R:\Recovery\WindowsRE\ /Y"
|
||||
# WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
# WriteLog 'Registering location of recovery tools'
|
||||
# Invoke-process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
# WriteLog 'Registering location of recovery tools succeeded'
|
||||
# }
|
||||
|
||||
#Autopilot JSON
|
||||
If ($APFileToInstall){
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot"
|
||||
Invoke-process xcopy.exe "$APFileToInstall W:\Windows\provisioning\autopilot\"
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot succeeded"
|
||||
# Rename file in W:\Windows\Provisioning\Autopilot to AutoPilotConfigurationFile.json
|
||||
try {
|
||||
Rename-Item -Path "W:\Windows\Provisioning\Autopilot\$APFileName" -NewName 'W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json'
|
||||
WriteLog "Renamed W:\Windows\Provisioning\Autopilot\$APFilename to W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json"
|
||||
}
|
||||
|
||||
catch{
|
||||
Writelog "Copying $APFileToInstall to W:\windows\provisioning\autopilot failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Apply PPKG
|
||||
If ($PPKGFileToInstall){
|
||||
try {
|
||||
#Make sure to delete any existing PPKG on the USB drive
|
||||
Get-Childitem -Path $USBDrive\*.ppkg | ForEach-Object {
|
||||
Remove-item -Path $_.FullName
|
||||
}
|
||||
WriteLog "Copying $PPKGFileToInstall to $USBDrive"
|
||||
Invoke-process xcopy.exe "$PPKGFileToInstall $USBDrive"
|
||||
WriteLog "Copying $PPKGFileToInstall to $USBDrive succeeded"
|
||||
}
|
||||
|
||||
catch{
|
||||
Writelog "Copying $PPKGFileToInstall to $USBDrive failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Set DeviceName
|
||||
If ($PrefixToUse){
|
||||
try{
|
||||
$PantherDir = 'w:\windows\panther'
|
||||
If (Test-Path -Path $PantherDir){
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-process xcopy "$UnattendFile $PantherDir /Y"
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
else{
|
||||
Writelog "$PantherDir doesn't exist, creating it"
|
||||
New-Item -Path $PantherDir -ItemType Directory -Force
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-Process xcopy.exe "$UnattendFile $PantherDir"
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
}
|
||||
catch{
|
||||
WriteLog "Copying Unattend.xml to name device failed"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
|
||||
#Add Drivers
|
||||
#Some drivers can sometimes fail to copy and dism ends up with a non-zero error code. Invoke-process will throw and terminate in these instances.
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
WriteLog 'Copying drivers'
|
||||
Write-Warning 'Copying Drivers - dism will pop a window with no progress. This can take a few minutes to complete. This is done so drivers are logged to the scriptlog.txt file. Please be patient.'
|
||||
Invoke-process dism.exe "/image:W:\ /Add-Driver /Driver:""$Drivers"" /Recurse"
|
||||
WriteLog 'Copying drivers succeeded'
|
||||
}
|
||||
|
||||
#Copy DISM log to USBDrive
|
||||
WriteLog "Copying dism log to $USBDrive"
|
||||
invoke-process xcopy "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
WriteLog "Copying dism log to $USBDrive succeeded"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,544 @@
|
||||
function Get-USBDrive(){
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Removable' -and $_.FileSystemType -eq 'NTFS'}).DriveLetter
|
||||
if ($null -eq $USBDriveLetter){
|
||||
#Must be using a fixed USB drive - difficult to grab drive letter from win32_diskdrive. Assume user followed instructions and used Deploy as the friendly name for partition
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.FileSystemLabel -eq 'Deploy'}).DriveLetter
|
||||
#If we didn't get the drive letter, stop the script.
|
||||
if ($null -eq $USBDriveLetter){
|
||||
# WriteLog 'Cannot find USB drive letter - most likely using a fixed USB drive. Name the 2nd partition with the FFU files as Deploy so the script can grab the drive letter. Exiting'
|
||||
Exit
|
||||
}
|
||||
|
||||
}
|
||||
$USBDriveLetter = $USBDriveLetter + ":\"
|
||||
return $USBDriveLetter
|
||||
}
|
||||
|
||||
function Get-HardDrive(){
|
||||
$DeviceID = (Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' -and $_.Model -ne 'Microsoft Virtual Disk'}).DeviceID
|
||||
return $DeviceID
|
||||
}
|
||||
|
||||
function WriteLog($LogText){
|
||||
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText"
|
||||
}
|
||||
|
||||
function Set-DiskpartAnswerFiles($DiskpartFile,$DiskID){
|
||||
(Get-Content $DiskpartFile).Replace('disk 0', "disk $DiskID") | Set-Content -Path $DiskpartFile
|
||||
}
|
||||
|
||||
function Set-Computername($computername){
|
||||
[xml]$xml = Get-Content $UnattendFile
|
||||
if($xml.unattend.settings.component.Count -ge 2){
|
||||
#Assumes that Computername is the first component element
|
||||
$xml.unattend.settings.component[0].ComputerName = $computername
|
||||
}else{
|
||||
$xml.unattend.settings.component.ComputerName = $computername
|
||||
}
|
||||
$xml.Save($UnattendFile)
|
||||
return $computername
|
||||
}
|
||||
|
||||
function Invoke-Process {
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param
|
||||
(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$FilePath,
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$ArgumentList
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
try {
|
||||
$stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
$stdErrTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
|
||||
$startProcessParams = @{
|
||||
FilePath = $FilePath
|
||||
ArgumentList = $ArgumentList
|
||||
RedirectStandardError = $stdErrTempFile
|
||||
RedirectStandardOutput = $stdOutTempFile
|
||||
Wait = $true;
|
||||
PassThru = $true;
|
||||
NoNewWindow = $false;
|
||||
}
|
||||
if ($PSCmdlet.ShouldProcess("Process [$($FilePath)]", "Run with args: [$($ArgumentList)]")) {
|
||||
$cmd = Start-Process @startProcessParams
|
||||
$cmdOutput = Get-Content -Path $stdOutTempFile -Raw
|
||||
$cmdError = Get-Content -Path $stdErrTempFile -Raw
|
||||
if ($cmd.ExitCode -ne 0) {
|
||||
if ($cmdError) {
|
||||
throw $cmdError.Trim()
|
||||
}
|
||||
if ($cmdOutput) {
|
||||
throw $cmdOutput.Trim()
|
||||
}
|
||||
} else {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
# WriteLog $cmdOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
#$PSCmdlet.ThrowTerminatingError($_)
|
||||
# WriteLog $_
|
||||
Write-Host 'Script failed - check scriptlog.txt on the USB drive for more info'
|
||||
throw $_
|
||||
|
||||
} finally {
|
||||
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# This function can be used in instances where battery level might matter (e.g. installing firmware for Surface). The problem is that WinPE doesn't have
|
||||
# a driver for the battery installed, so you'll need to inject drivers, which can be tricky in that just injecting the battery driver might not be enough,
|
||||
# you might also need other drivers that the battery driver is dependent on.
|
||||
# function Get-Battery(){
|
||||
# while (($BattLev = (Get-CimInstance win32_battery).EstimatedChargeRemaining) -lt "35")
|
||||
# {
|
||||
# WriteLog "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Write-Host "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Start-Sleep 60
|
||||
# }
|
||||
|
||||
# WriteLog "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# Write-Host "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# }
|
||||
|
||||
#Get USB Drive and create log file
|
||||
$LogFileName = 'ScriptLog.txt'
|
||||
$USBDrive = Get-USBDrive
|
||||
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
|
||||
$LogFile = $USBDrive + $LogFilename
|
||||
# WriteLog 'Begin Logging'
|
||||
|
||||
#Find PhysicalDrive
|
||||
$PhysicalDeviceID = Get-HardDrive
|
||||
# WriteLog "Physical DeviceID is $PhysicalDeviceID"
|
||||
|
||||
#Parse DiskID Number
|
||||
$DiskID = $PhysicalDeviceID.substring($PhysicalDeviceID.length - 1,1)
|
||||
# WriteLog "DiskID is $DiskID"
|
||||
|
||||
#Modify diskpart answer files if DiskID not 0
|
||||
$UEFIFFUPartitions = 'x:\CreateUEFI-FFU-Partitions.txt'
|
||||
$ExtendPartition = 'x:\ExtendPartition-UEFI.txt'
|
||||
|
||||
If ($DiskID -ne '0'){
|
||||
# WriteLog 'DiskID is not 0. Need to modify diskpart answer files'
|
||||
try {
|
||||
Set-DiskpartAnswerFiles $UEFIFFUPartitions $DiskID
|
||||
}
|
||||
catch {
|
||||
# WriteLog "Modifying $UEFIFFUPartitions failed with error: $_"
|
||||
}
|
||||
|
||||
try {
|
||||
Set-DiskpartAnswerFiles $ExtendPartition $DiskID
|
||||
}
|
||||
catch {
|
||||
# WriteLog "Modifying $ExtendPartition failed with error: $_"
|
||||
}
|
||||
}
|
||||
|
||||
#Partition drive
|
||||
# Writelog 'Creating partitions'
|
||||
#Start-Process -FilePath diskpart.exe -ArgumentList "/S $UEFIFFUPartitions" -Wait -ErrorAction Stop | Out-File $Logfile -Append
|
||||
Invoke-Process diskpart.exe "/S $UEFIFFUPartitions"
|
||||
# Writelog 'Creating partitions succeeded'
|
||||
|
||||
#Find FFU Files
|
||||
[array]$FFUFiles = @(Get-ChildItem -Path $USBDrive*.ffu)
|
||||
$FFUCount = $FFUFiles.Count
|
||||
|
||||
#If multiple FFUs found, ask which to install
|
||||
If ($FFUCount -gt 1) {
|
||||
# WriteLog "Found $FFUCount FFU Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $FFUCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; FFUFile = $FFUFiles[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, FFUFile
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$FFUSelected = Read-Host 'Enter the FFU number to install'
|
||||
$FFUSelected = $FFUSelected -1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($FFUSelected -le $FFUCount -1) -and $var)
|
||||
|
||||
$FFUFileToInstall = $array[$FFUSelected].FFUFile
|
||||
# WriteLog "$FFUFileToInstall was selected"
|
||||
}
|
||||
elseif ($FFUCount -eq 1) {
|
||||
# WriteLog "Found $FFUCount FFU File"
|
||||
$FFUFileToInstall = $FFUFiles[0].FullName
|
||||
# WriteLog "$FFUFileToInstall will be installed"
|
||||
}
|
||||
else {
|
||||
# Writelog 'No FFU files found'
|
||||
Write-Host 'No FFU files found'
|
||||
Exit
|
||||
}
|
||||
|
||||
#FindAP
|
||||
$APFolder = $USBDrive + "Autopilot\"
|
||||
If (Test-Path -Path $APFolder){
|
||||
[array]$APFiles = @(Get-ChildItem -Path $APFolder*.json)
|
||||
$APFilesCount = $APFiles.Count
|
||||
if ($APFilesCount -ge 1){
|
||||
$autopilot = $true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#FindPPKG
|
||||
$PPKGFolder = $USBDrive + "PPKG\"
|
||||
if (Test-Path -Path $PPKGFolder){
|
||||
[array]$PPKGFiles = @(Get-ChildItem -Path $PPKGFolder*.ppkg)
|
||||
$PPKGFilesCount = $PPKGFiles.Count
|
||||
if ($PPKGFilesCount -ge 1){
|
||||
$PPKG = $true
|
||||
}
|
||||
}
|
||||
|
||||
#FindUnattend
|
||||
$UnattendFolder = $USBDrive + "unattend\"
|
||||
$UnattendFilePath = $UnattendFolder + "unattend.xml"
|
||||
$UnattendPrefixPath = $UnattendFolder + "prefixes.txt"
|
||||
If (Test-Path -Path $UnattendFilePath){
|
||||
$UnattendFile = Get-ChildItem -Path $UnattendFilePath
|
||||
If ($UnattendFile){
|
||||
$Unattend = $true
|
||||
}
|
||||
}
|
||||
If (Test-Path -Path $UnattendPrefixPath){
|
||||
$UnattendPrefixFile = Get-ChildItem -Path $UnattendPrefixPath
|
||||
If ($UnattendPrefixFile){
|
||||
$UnattendPrefix = $true
|
||||
}
|
||||
}
|
||||
|
||||
#Ask for device name if unattend exists
|
||||
if ($Unattend -and $UnattendPrefix){
|
||||
# Writelog 'Unattend file found with prefixes.txt. Getting prefixes.'
|
||||
$UnattendPrefixes = @(Get-content $UnattendPrefixFile)
|
||||
$UnattendPrefixCount = $UnattendPrefixes.Count
|
||||
If ($UnattendPrefixCount -gt 1) {
|
||||
# WriteLog "Found $UnattendPrefixCount Prefixes"
|
||||
$array = @()
|
||||
for($i=0;$i -le $UnattendPrefixCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; DeviceNamePrefix = $UnattendPrefixes[$i]}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, DeviceNamePrefix
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PrefixSelected = Read-Host 'Enter the prefix number to use for the device name'
|
||||
$PrefixSelected = $PrefixSelected -1
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid prefix number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PrefixSelected -le $UnattendPrefixCount -1) -and $var)
|
||||
$PrefixToUse = $array[$PrefixSelected].DeviceNamePrefix
|
||||
# WriteLog "$PrefixToUse was selected"
|
||||
}
|
||||
elseif ($UnattendPrefixCount -eq 1) {
|
||||
# WriteLog "Found $UnattendPrefixCount Prefix"
|
||||
$PrefixToUse = $UnattendPrefixes[0]
|
||||
# WriteLog "Will use $PrefixToUse as device name prefix"
|
||||
}
|
||||
#Get serial number to append. This can make names longer than 15 characters. Trim any leading or trailing whitespace
|
||||
$serial = (Get-CimInstance -ClassName win32_bios).SerialNumber.Trim()
|
||||
#Combine prefix with serial
|
||||
$computername = $PrefixToUse + $serial
|
||||
#If computername is longer than 15 characters, reduce to 15. Sysprep/unattend doesn't like ComputerName being longer than 15 characters even though Windows accepts it
|
||||
If ($computername.Length -gt 15){
|
||||
$computername = $computername.substring(0,15)
|
||||
}
|
||||
$computername = Set-Computername($computername)
|
||||
# Writelog "Computer name set to $computername"
|
||||
}
|
||||
elseif($Unattend){
|
||||
# Writelog 'Unattend file found with no prefixes.txt, asking for name'
|
||||
[string]$computername = Read-Host 'Enter device name'
|
||||
Set-Computername($computername)
|
||||
# Writelog "Computer name set to $computername"
|
||||
}
|
||||
else {
|
||||
# WriteLog 'No unattend folder found. Device name will be set via PPKG, AP JSON, or default OS name.'
|
||||
}
|
||||
|
||||
#If both AP and PPKG folder found with files, ask which to use.
|
||||
If($autopilot -eq $true -and $PPKG -eq $true){
|
||||
# WriteLog 'Both PPKG and Autopilot json files found'
|
||||
Write-Host 'Both Autopilot JSON files and Provisioning packages were found.'
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APorPPKG = Read-Host 'Enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Incorrect value. Please enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APorPPKG -gt 0 -and $APorPPKG -lt 3) -and $var)
|
||||
If ($APorPPKG -eq 1){
|
||||
$PPKG = $false
|
||||
}
|
||||
else{
|
||||
$autopilot = $false
|
||||
}
|
||||
}
|
||||
|
||||
#If multiple AP json files found, ask which to install
|
||||
If ($APFilesCount -gt 1 -and $autopilot -eq $true) {
|
||||
# WriteLog "Found $APFilesCount Autopilot json Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $APFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; APFile = $APFiles[$i].FullName; APFileName = $APFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, APFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APFileSelected = Read-Host 'Enter the AP json file number to install'
|
||||
$APFileSelected = $APFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid AP json file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APFileSelected -le $APFilesCount -1) -and $var)
|
||||
|
||||
$APFileToInstall = $array[$APFileSelected].APFile
|
||||
$APFileName = $array[$APFileSelected].APFileName
|
||||
# WriteLog "$APFileToInstall was selected"
|
||||
}
|
||||
elseif ($APFilesCount -eq 1 -and $autopilot -eq $true) {
|
||||
# WriteLog "Found $APFilesCount AP File"
|
||||
$APFileToInstall = $APFiles[0].FullName
|
||||
$APFileName = $APFiles[0].Name
|
||||
# WriteLog "$APFileToInstall will be copied"
|
||||
}
|
||||
else {
|
||||
# Writelog 'No AP files found or AP was not selected'
|
||||
}
|
||||
|
||||
#If multiple PPKG files found, ask which to install
|
||||
If ($PPKGFilesCount -gt 1 -and $PPKG -eq $true) {
|
||||
# WriteLog "Found $PPKGFilesCount PPKG Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $PPKGFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; PPKGFile = $PPKGFiles[$i].FullName; PPKGFileName = $PPKGFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, PPKGFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PPKGFileSelected = Read-Host 'Enter the PPKG file number to install'
|
||||
$PPKGFileSelected = $PPKGFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid PPKG file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PPKGFileSelected -le $PPKGFilesCount -1) -and $var)
|
||||
|
||||
$PPKGFileToInstall = $array[$PPKGFileSelected].PPKGFile
|
||||
# WriteLog "$PPKGFileToInstall was selected"
|
||||
}
|
||||
elseif ($PPKGFilesCount -eq 1 -and $PPKG -eq $true) {
|
||||
# WriteLog "Found $PPKGFilesCount PPKG File"
|
||||
$PPKGFileToInstall = $PPKGFiles[0].FullName
|
||||
# WriteLog "$PPKGFileToInstall will be used"
|
||||
}
|
||||
else {
|
||||
# Writelog 'No PPKG files found or PPKG not selected.'
|
||||
}
|
||||
|
||||
#Find Drivers
|
||||
$Drivers = $USBDrive + "Drivers"
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
#Check if multiple driver folders found, if so, just select one folder to save time/space
|
||||
$DriverFolders = Get-ChildItem -Path $Drivers
|
||||
$DriverFoldersCount = $DriverFolders.count
|
||||
If ($DriverFoldersCount -gt 1)
|
||||
{
|
||||
# WriteLog "Found $DriverFoldersCount driver folders"
|
||||
$array = @()
|
||||
|
||||
for($i=0; $i -le $DriverFoldersCount -1; $i++){
|
||||
$Properties = [ordered]@{Number = $i + 1; Drivers = $DriverFolders[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, Drivers
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$DriversSelected = Read-Host 'Enter the set of drivers to install'
|
||||
$DriversSelected = $DriversSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid driver folder number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($DriversSelected -le $DriverFoldersCount -1) -and $var)
|
||||
|
||||
$Drivers = $array[$DriversSelected].Drivers
|
||||
# WriteLog "$Drivers was selected"
|
||||
}
|
||||
elseif ($DriverFoldersCount -eq 1) {
|
||||
# WriteLog "Found $DriverFoldersCount driver folder"
|
||||
$Drivers = $DriverFolders.FullName
|
||||
# WriteLog "$Drivers will be installed"
|
||||
}
|
||||
else {
|
||||
# Writelog 'No driver folders found'
|
||||
}
|
||||
}
|
||||
|
||||
#If you want to enable battery level checking, uncomment the line below as well as the Get-Battery function near the top of the script
|
||||
#Get-Battery
|
||||
|
||||
#Apply FFU
|
||||
# WriteLog "Applying FFU to $PhysicalDeviceID"
|
||||
# WriteLog "Running command dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID"
|
||||
#In order for Applying Image progress bar to show up, need to call dism directly. Might be a better way to handle, but must have progress bar show up on screen.
|
||||
dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID
|
||||
if($LASTEXITCODE -eq 0){
|
||||
# WriteLog 'Successfully applied FFU'
|
||||
}
|
||||
else{
|
||||
# Writelog "Failed to apply FFU - LastExitCode = $LASTEXITCODE also check dism.log on the USB drive for more info"
|
||||
#Copy DISM log to USBDrive
|
||||
invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
exit
|
||||
}
|
||||
|
||||
#Extend Windows partition and create recovery partition
|
||||
# Writelog 'Extending Windows partition'
|
||||
Invoke-Process diskpart.exe "/S $ExtendPartition"
|
||||
# Writelog 'Extending Windows partition succeeded'
|
||||
|
||||
|
||||
#Copy modified WinRE if folder exists, else copy inbox WinRE
|
||||
$WinRE = $USBDrive + "WinRE\winre.wim"
|
||||
If (Test-Path -Path $WinRE)
|
||||
{
|
||||
# WriteLog 'Copying modified WinRE to Recovery directory'
|
||||
Invoke-Process xcopy.exe "/h $WinRE R:\Recovery\WindowsRE\ /Y"
|
||||
# WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
# WriteLog 'Registering location of recovery tools'
|
||||
Invoke-Process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
# WriteLog 'Registering location of recovery tools succeeded'
|
||||
}
|
||||
else {
|
||||
# WriteLog 'Copying default WinRE to Recovery directory'
|
||||
Invoke-Process xcopy.exe "/h W:\Windows\System32\Recovery\Winre.wim R:\Recovery\WindowsRE\ /Y"
|
||||
# WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
# WriteLog 'Registering location of recovery tools'
|
||||
Invoke-process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
# WriteLog 'Registering location of recovery tools succeeded'
|
||||
}
|
||||
|
||||
#Autopilot JSON
|
||||
If ($APFileToInstall){
|
||||
# WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot"
|
||||
Invoke-process xcopy.exe "$APFileToInstall W:\Windows\provisioning\autopilot\"
|
||||
# WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot succeeded"
|
||||
# Rename file in W:\Windows\Provisioning\Autopilot to AutoPilotConfigurationFile.json
|
||||
try {
|
||||
Rename-Item -Path "W:\Windows\Provisioning\Autopilot\$APFileName" -NewName 'W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json'
|
||||
# WriteLog "Renamed W:\Windows\Provisioning\Autopilot\$APFilename to W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json"
|
||||
}
|
||||
|
||||
catch{
|
||||
# Writelog "Copying $APFileToInstall to W:\windows\provisioning\autopilot failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Apply PPKG
|
||||
If ($PPKGFileToInstall){
|
||||
try {
|
||||
#Make sure to delete any existing PPKG on the USB drive
|
||||
Get-Childitem -Path $USBDrive\*.ppkg | ForEach-Object {
|
||||
Remove-item -Path $_.FullName
|
||||
}
|
||||
# WriteLog "Copying $PPKGFileToInstall to $USBDrive"
|
||||
Invoke-process xcopy.exe "$PPKGFileToInstall $USBDrive"
|
||||
# WriteLog "Copying $PPKGFileToInstall to $USBDrive succeeded"
|
||||
}
|
||||
|
||||
catch{
|
||||
# Writelog "Copying $PPKGFileToInstall to $USBDrive failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Set DeviceName
|
||||
If ($PrefixToUse){
|
||||
try{
|
||||
$PantherDir = 'w:\windows\panther'
|
||||
If (Test-Path -Path $PantherDir){
|
||||
# Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-process xcopy "$UnattendFile $PantherDir /Y"
|
||||
# WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
else{
|
||||
# Writelog "$PantherDir doesn't exist, creating it"
|
||||
New-Item -Path $PantherDir -ItemType Directory -Force
|
||||
# Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-Process xcopy.exe "$UnattendFile $PantherDir"
|
||||
# WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
}
|
||||
catch{
|
||||
# WriteLog "Copying Unattend.xml to name device failed"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
|
||||
#Add Drivers
|
||||
#Some drivers can sometimes fail to copy and dism ends up with a non-zero error code. Invoke-process will throw and terminate in these instances.
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
# WriteLog 'Copying drivers'
|
||||
Write-Host 'Copying Drivers - dism will pop a window with no progress. This can take a few minutes to complete. Please be patient. '
|
||||
Invoke-process dism.exe "/image:W:\ /Add-Driver /Driver:""$Drivers"" /Recurse"
|
||||
# WriteLog 'Copying drivers succeeded'
|
||||
}
|
||||
|
||||
#Copy DISM log to USBDrive
|
||||
# WriteLog "Copying dism log to $USBDrive"
|
||||
# invoke-process xcopy "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
# WriteLog "Copying dism log to $USBDrive succeeded"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,579 @@
|
||||
function Get-USBDrive(){
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Removable' -and $_.FileSystemType -eq 'NTFS'}).DriveLetter
|
||||
if ($null -eq $USBDriveLetter){
|
||||
#Must be using a fixed USB drive - difficult to grab drive letter from win32_diskdrive. Assume user followed instructions and used Deploy as the friendly name for partition
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Fixed' -and $_.FileSystemType -eq 'NTFS' -and $_.FileSystemLabel -eq 'Deploy'}).DriveLetter
|
||||
#If we didn't get the drive letter, stop the script.
|
||||
if ($null -eq $USBDriveLetter){
|
||||
WriteLog 'Cannot find USB drive letter - most likely using a fixed USB drive. Name the 2nd partition with the FFU files as Deploy so the script can grab the drive letter. Exiting'
|
||||
Exit
|
||||
}
|
||||
|
||||
}
|
||||
$USBDriveLetter = $USBDriveLetter + ":\"
|
||||
return $USBDriveLetter
|
||||
}
|
||||
|
||||
function Get-HardDrive(){
|
||||
$DeviceID = (Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' -and $_.Model -ne 'Microsoft Virtual Disk'}).DeviceID
|
||||
return $DeviceID
|
||||
}
|
||||
|
||||
function WriteLog($LogText){
|
||||
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText"
|
||||
}
|
||||
|
||||
function Set-DiskpartAnswerFiles($DiskpartFile,$DiskID){
|
||||
(Get-Content $DiskpartFile).Replace('disk 0', "disk $DiskID") | Set-Content -Path $DiskpartFile
|
||||
}
|
||||
|
||||
function Set-Computername($computername){
|
||||
[xml]$xml = Get-Content $UnattendFile
|
||||
if($xml.unattend.settings.component.Count -ge 2){
|
||||
#Assumes that Computername is the first component element
|
||||
$xml.unattend.settings.component[0].ComputerName = $computername
|
||||
}else{
|
||||
$xml.unattend.settings.component.ComputerName = $computername
|
||||
}
|
||||
$xml.Save($UnattendFile)
|
||||
return $computername
|
||||
}
|
||||
|
||||
function Invoke-Process {
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param
|
||||
(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$FilePath,
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$ArgumentList
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
try {
|
||||
$stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
$stdErrTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
|
||||
$startProcessParams = @{
|
||||
FilePath = $FilePath
|
||||
ArgumentList = $ArgumentList
|
||||
RedirectStandardError = $stdErrTempFile
|
||||
RedirectStandardOutput = $stdOutTempFile
|
||||
Wait = $true;
|
||||
PassThru = $true;
|
||||
NoNewWindow = $false;
|
||||
}
|
||||
if ($PSCmdlet.ShouldProcess("Process [$($FilePath)]", "Run with args: [$($ArgumentList)]")) {
|
||||
$cmd = Start-Process @startProcessParams
|
||||
$cmdOutput = Get-Content -Path $stdOutTempFile -Raw
|
||||
$cmdError = Get-Content -Path $stdErrTempFile -Raw
|
||||
if ($cmd.ExitCode -ne 0) {
|
||||
if ($cmdError) {
|
||||
throw $cmdError.Trim()
|
||||
}
|
||||
if ($cmdOutput) {
|
||||
throw $cmdOutput.Trim()
|
||||
}
|
||||
} else {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
WriteLog $cmdOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
#$PSCmdlet.ThrowTerminatingError($_)
|
||||
WriteLog $_
|
||||
Write-Host 'Script failed - check scriptlog.txt on the USB drive for more info'
|
||||
throw $_
|
||||
|
||||
} finally {
|
||||
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# This function can be used in instances where battery level might matter (e.g. installing firmware for Surface). The problem is that WinPE doesn't have
|
||||
# a driver for the battery installed, so you'll need to inject drivers, which can be tricky because just injecting the battery driver might not be enough,
|
||||
# you might also need other drivers that the battery driver is dependent on.
|
||||
# function Get-Battery(){
|
||||
# while (($BattLev = (Get-CimInstance win32_battery).EstimatedChargeRemaining) -lt "35")
|
||||
# {
|
||||
# WriteLog "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Write-Host "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Start-Sleep 60
|
||||
# }
|
||||
|
||||
# WriteLog "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# Write-Host "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# }
|
||||
|
||||
#Get USB Drive and create log file
|
||||
$LogFileName = 'ScriptLog.txt'
|
||||
$USBDrive = Get-USBDrive
|
||||
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
|
||||
$LogFile = $USBDrive + $LogFilename
|
||||
WriteLog 'Begin Logging'
|
||||
|
||||
#Find PhysicalDrive
|
||||
$PhysicalDeviceID = Get-HardDrive
|
||||
WriteLog "Physical DeviceID is $PhysicalDeviceID"
|
||||
|
||||
#Parse DiskID Number
|
||||
$DiskID = $PhysicalDeviceID.substring($PhysicalDeviceID.length - 1,1)
|
||||
WriteLog "DiskID is $DiskID"
|
||||
|
||||
#Modify diskpart answer files if DiskID not 0
|
||||
$UEFIFFUPartitions = 'x:\CreateUEFI-FFU-Partitions.txt'
|
||||
$ExtendPartition = 'x:\ExtendPartition-UEFI.txt'
|
||||
|
||||
If ($DiskID -ne '0'){
|
||||
WriteLog 'DiskID is not 0. Need to modify diskpart answer files'
|
||||
try {
|
||||
Set-DiskpartAnswerFiles $UEFIFFUPartitions $DiskID
|
||||
}
|
||||
catch {
|
||||
WriteLog "Modifying $UEFIFFUPartitions failed with error: $_"
|
||||
}
|
||||
|
||||
try {
|
||||
Set-DiskpartAnswerFiles $ExtendPartition $DiskID
|
||||
}
|
||||
catch {
|
||||
WriteLog "Modifying $ExtendPartition failed with error: $_"
|
||||
}
|
||||
}
|
||||
|
||||
#Find FFU Files
|
||||
[array]$FFUFiles = @(Get-ChildItem -Path $USBDrive*.ffu)
|
||||
$FFUCount = $FFUFiles.Count
|
||||
|
||||
#If multiple FFUs found, ask which to install
|
||||
If ($FFUCount -gt 1) {
|
||||
WriteLog "Found $FFUCount FFU Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $FFUCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; FFUFile = $FFUFiles[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, FFUFile
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$FFUSelected = Read-Host 'Enter the FFU number to install'
|
||||
$FFUSelected = $FFUSelected -1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($FFUSelected -le $FFUCount -1) -and $var)
|
||||
|
||||
$FFUFileToInstall = $array[$FFUSelected].FFUFile
|
||||
WriteLog "$FFUFileToInstall was selected"
|
||||
}
|
||||
elseif ($FFUCount -eq 1) {
|
||||
WriteLog "Found $FFUCount FFU File"
|
||||
$FFUFileToInstall = $FFUFiles[0].FullName
|
||||
WriteLog "$FFUFileToInstall will be installed"
|
||||
}
|
||||
else {
|
||||
Writelog 'No FFU files found'
|
||||
Write-Host 'No FFU files found'
|
||||
Exit
|
||||
}
|
||||
|
||||
#FindAP
|
||||
$APFolder = $USBDrive + "Autopilot\"
|
||||
If (Test-Path -Path $APFolder){
|
||||
[array]$APFiles = @(Get-ChildItem -Path $APFolder*.json)
|
||||
$APFilesCount = $APFiles.Count
|
||||
if ($APFilesCount -ge 1){
|
||||
$autopilot = $true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#FindPPKG
|
||||
$PPKGFolder = $USBDrive + "PPKG\"
|
||||
if (Test-Path -Path $PPKGFolder){
|
||||
[array]$PPKGFiles = @(Get-ChildItem -Path $PPKGFolder*.ppkg)
|
||||
$PPKGFilesCount = $PPKGFiles.Count
|
||||
if ($PPKGFilesCount -ge 1){
|
||||
$PPKG = $true
|
||||
}
|
||||
}
|
||||
|
||||
#FindUnattend
|
||||
$UnattendFolder = $USBDrive + "unattend\"
|
||||
$UnattendFilePath = $UnattendFolder + "unattend.xml"
|
||||
$UnattendPrefixPath = $UnattendFolder + "prefixes.txt"
|
||||
If (Test-Path -Path $UnattendFilePath){
|
||||
$UnattendFile = Get-ChildItem -Path $UnattendFilePath
|
||||
If ($UnattendFile){
|
||||
$Unattend = $true
|
||||
}
|
||||
}
|
||||
If (Test-Path -Path $UnattendPrefixPath){
|
||||
$UnattendPrefixFile = Get-ChildItem -Path $UnattendPrefixPath
|
||||
If ($UnattendPrefixFile){
|
||||
$UnattendPrefix = $true
|
||||
}
|
||||
}
|
||||
|
||||
#Ask for device name if unattend exists
|
||||
if ($Unattend -and $UnattendPrefix){
|
||||
Writelog 'Unattend file found with prefixes.txt. Getting prefixes.'
|
||||
$UnattendPrefixes = @(Get-content $UnattendPrefixFile)
|
||||
$UnattendPrefixCount = $UnattendPrefixes.Count
|
||||
If ($UnattendPrefixCount -gt 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefixes"
|
||||
$array = @()
|
||||
for($i=0;$i -le $UnattendPrefixCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; DeviceNamePrefix = $UnattendPrefixes[$i]}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, DeviceNamePrefix
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PrefixSelected = Read-Host 'Enter the prefix number to use for the device name'
|
||||
$PrefixSelected = $PrefixSelected -1
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid prefix number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PrefixSelected -le $UnattendPrefixCount -1) -and $var)
|
||||
$PrefixToUse = $array[$PrefixSelected].DeviceNamePrefix
|
||||
WriteLog "$PrefixToUse was selected"
|
||||
}
|
||||
elseif ($UnattendPrefixCount -eq 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefix"
|
||||
$PrefixToUse = $UnattendPrefixes[0]
|
||||
WriteLog "Will use $PrefixToUse as device name prefix"
|
||||
}
|
||||
#Get serial number to append. This can make names longer than 15 characters. Trim any leading or trailing whitespace
|
||||
$serial = (Get-CimInstance -ClassName win32_bios).SerialNumber.Trim()
|
||||
#Combine prefix with serial
|
||||
$computername = $PrefixToUse + $serial
|
||||
#If computername is longer than 15 characters, reduce to 15. Sysprep/unattend doesn't like ComputerName being longer than 15 characters even though Windows accepts it
|
||||
If ($computername.Length -gt 15){
|
||||
$computername = $computername.substring(0,15)
|
||||
}
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
elseif($Unattend){
|
||||
Writelog 'Unattend file found with no prefixes.txt, asking for name'
|
||||
[string]$computername = Read-Host 'Enter device name'
|
||||
Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
else {
|
||||
WriteLog 'No unattend folder found. Device name will be set via PPKG, AP JSON, or default OS name.'
|
||||
}
|
||||
|
||||
#If both AP and PPKG folder found with files, ask which to use.
|
||||
If($autopilot -eq $true -and $PPKG -eq $true){
|
||||
WriteLog 'Both PPKG and Autopilot json files found'
|
||||
Write-Host 'Both Autopilot JSON files and Provisioning packages were found.'
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APorPPKG = Read-Host 'Enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Incorrect value. Please enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APorPPKG -gt 0 -and $APorPPKG -lt 3) -and $var)
|
||||
If ($APorPPKG -eq 1){
|
||||
$PPKG = $false
|
||||
}
|
||||
else{
|
||||
$autopilot = $false
|
||||
}
|
||||
}
|
||||
|
||||
#If multiple AP json files found, ask which to install
|
||||
If ($APFilesCount -gt 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount Autopilot json Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $APFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; APFile = $APFiles[$i].FullName; APFileName = $APFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, APFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APFileSelected = Read-Host 'Enter the AP json file number to install'
|
||||
$APFileSelected = $APFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid AP json file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APFileSelected -le $APFilesCount -1) -and $var)
|
||||
|
||||
$APFileToInstall = $array[$APFileSelected].APFile
|
||||
$APFileName = $array[$APFileSelected].APFileName
|
||||
WriteLog "$APFileToInstall was selected"
|
||||
}
|
||||
elseif ($APFilesCount -eq 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount AP File"
|
||||
$APFileToInstall = $APFiles[0].FullName
|
||||
$APFileName = $APFiles[0].Name
|
||||
WriteLog "$APFileToInstall will be copied"
|
||||
}
|
||||
else {
|
||||
Writelog 'No AP files found or AP was not selected'
|
||||
}
|
||||
|
||||
#If multiple PPKG files found, ask which to install
|
||||
If ($PPKGFilesCount -gt 1 -and $PPKG -eq $true) {
|
||||
WriteLog "Found $PPKGFilesCount PPKG Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $PPKGFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; PPKGFile = $PPKGFiles[$i].FullName; PPKGFileName = $PPKGFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, PPKGFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PPKGFileSelected = Read-Host 'Enter the PPKG file number to install'
|
||||
$PPKGFileSelected = $PPKGFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid PPKG file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PPKGFileSelected -le $PPKGFilesCount -1) -and $var)
|
||||
|
||||
$PPKGFileToInstall = $array[$PPKGFileSelected].PPKGFile
|
||||
WriteLog "$PPKGFileToInstall was selected"
|
||||
}
|
||||
elseif ($PPKGFilesCount -eq 1 -and $PPKG -eq $true) {
|
||||
WriteLog "Found $PPKGFilesCount PPKG File"
|
||||
$PPKGFileToInstall = $PPKGFiles[0].FullName
|
||||
WriteLog "$PPKGFileToInstall will be used"
|
||||
}
|
||||
else {
|
||||
Writelog 'No PPKG files found or PPKG not selected.'
|
||||
}
|
||||
|
||||
#Find Drivers
|
||||
$Drivers = $USBDrive + "Drivers"
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
#Check if multiple driver folders found, if so, just select one folder to save time/space
|
||||
$DriverFolders = Get-ChildItem -Path $Drivers
|
||||
$DriverFoldersCount = $DriverFolders.count
|
||||
If ($DriverFoldersCount -gt 1)
|
||||
{
|
||||
WriteLog "Found $DriverFoldersCount driver folders"
|
||||
$array = @()
|
||||
|
||||
for($i=0; $i -le $DriverFoldersCount -1; $i++){
|
||||
$Properties = [ordered]@{Number = $i + 1; Drivers = $DriverFolders[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, Drivers
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$DriversSelected = Read-Host 'Enter the set of drivers to install'
|
||||
$DriversSelected = $DriversSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid driver folder number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($DriversSelected -le $DriverFoldersCount -1) -and $var)
|
||||
|
||||
$Drivers = $array[$DriversSelected].Drivers
|
||||
WriteLog "$Drivers was selected"
|
||||
}
|
||||
elseif ($DriverFoldersCount -eq 1) {
|
||||
WriteLog "Found $DriverFoldersCount driver folder"
|
||||
$Drivers = $DriverFolders.FullName
|
||||
WriteLog "$Drivers will be installed"
|
||||
}
|
||||
else {
|
||||
Writelog 'No driver folders found'
|
||||
}
|
||||
}
|
||||
|
||||
#If you want to enable battery level checking, uncomment the line below as well as the Get-Battery function near the top of the script
|
||||
#Get-Battery
|
||||
|
||||
#Partition drive
|
||||
Writelog 'Clean Disk'
|
||||
#Start-Process -FilePath diskpart.exe -ArgumentList "/S $UEFIFFUPartitions" -Wait -ErrorAction Stop | Out-File $Logfile -Append
|
||||
#Invoke-Process diskpart.exe "/S $UEFIFFUPartitions"
|
||||
try {
|
||||
$Disk = Get-Disk -Number $DiskID
|
||||
$Disk | clear-disk -RemoveData -RemoveOEM -Confirm:$false
|
||||
}
|
||||
catch {
|
||||
WriteLog 'Cleaning disk failed. Exiting'
|
||||
throw $_
|
||||
}
|
||||
|
||||
Writelog 'Cleaning Disk succeeded'
|
||||
|
||||
#Apply FFU
|
||||
WriteLog "Applying FFU to $PhysicalDeviceID"
|
||||
WriteLog "Running command dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID"
|
||||
#In order for Applying Image progress bar to show up, need to call dism directly. Might be a better way to handle, but must have progress bar show up on screen.
|
||||
dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID
|
||||
if($LASTEXITCODE -eq 0){
|
||||
WriteLog 'Successfully applied FFU'
|
||||
}
|
||||
else{
|
||||
Writelog "Failed to apply FFU - LastExitCode = $LASTEXITCODE also check dism.log on the USB drive for more info"
|
||||
#Copy DISM log to USBDrive
|
||||
invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
exit
|
||||
}
|
||||
|
||||
#Remove recovery partition - this is needed in order to extend the Windows partition so it uses the full disk size. If dism /optimize-ffu worked, this wouldn't be needed
|
||||
$disk = get-disk -Number $DiskID
|
||||
$RecoveryPartition = $disk | get-partition | Where-Object {$_.type -eq 'Recovery'}
|
||||
if ($RecoveryPartition){
|
||||
$RecoveryPartitionNumber = $RecoveryPartition.PartitionNumber
|
||||
if ($RecoveryPartitionNumber -eq 4){
|
||||
try {
|
||||
WriteLog 'Removing recovery partition'
|
||||
Remove-partition -DiskNumber $DiskID -PartitionNumber $RecoveryPartitionNumber -Confirm:$false
|
||||
}
|
||||
catch {
|
||||
WriteLog 'Error removing recovery partition, exiting'
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
else{
|
||||
WriteLog 'Recovery partition not partition 4. Script will exit. Please create the FFU with the recovery partition as the last partition. This is the default and recommended way.'
|
||||
exit
|
||||
}
|
||||
}
|
||||
|
||||
#Extend Windows partition and create recovery partition
|
||||
Writelog 'Extending Windows partition'
|
||||
Invoke-Process diskpart.exe "/S $ExtendPartition"
|
||||
if($LASTEXITCODE -eq 0){
|
||||
WriteLog 'Successfully extended Windows partition and created recovery partition'
|
||||
}
|
||||
else{
|
||||
Writelog "Failed to extend Windows partition and/or create recovery partition - LastExitCode = $LASTEXITCODE"
|
||||
}
|
||||
|
||||
#Copy modified WinRE if folder exists, else copy inbox WinRE
|
||||
$WinRE = $USBDrive + "WinRE\winre.wim"
|
||||
If (Test-Path -Path $WinRE)
|
||||
{
|
||||
WriteLog 'Copying modified WinRE to Recovery directory'
|
||||
Invoke-Process xcopy.exe "/h $WinRE R:\Recovery\WindowsRE\ /Y"
|
||||
WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
WriteLog 'Registering location of recovery tools'
|
||||
Invoke-Process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
WriteLog 'Registering location of recovery tools succeeded'
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog 'Copying default WinRE to Recovery directory'
|
||||
Invoke-Process xcopy.exe "/h W:\Windows\System32\Recovery\Winre.wim R:\Recovery\WindowsRE\ /Y"
|
||||
WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
WriteLog 'Registering location of recovery tools'
|
||||
Invoke-process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
WriteLog 'Registering location of recovery tools succeeded'
|
||||
}
|
||||
|
||||
#Autopilot JSON
|
||||
If ($APFileToInstall){
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot"
|
||||
Invoke-process xcopy.exe "$APFileToInstall W:\Windows\provisioning\autopilot\"
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot succeeded"
|
||||
# Rename file in W:\Windows\Provisioning\Autopilot to AutoPilotConfigurationFile.json
|
||||
try {
|
||||
Rename-Item -Path "W:\Windows\Provisioning\Autopilot\$APFileName" -NewName 'W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json'
|
||||
WriteLog "Renamed W:\Windows\Provisioning\Autopilot\$APFilename to W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json"
|
||||
}
|
||||
|
||||
catch{
|
||||
Writelog "Copying $APFileToInstall to W:\windows\provisioning\autopilot failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Apply PPKG
|
||||
If ($PPKGFileToInstall){
|
||||
try {
|
||||
#Make sure to delete any existing PPKG on the USB drive
|
||||
Get-Childitem -Path $USBDrive\*.ppkg | ForEach-Object {
|
||||
Remove-item -Path $_.FullName
|
||||
}
|
||||
WriteLog "Copying $PPKGFileToInstall to $USBDrive"
|
||||
Invoke-process xcopy.exe "$PPKGFileToInstall $USBDrive"
|
||||
WriteLog "Copying $PPKGFileToInstall to $USBDrive succeeded"
|
||||
}
|
||||
|
||||
catch{
|
||||
Writelog "Copying $PPKGFileToInstall to $USBDrive failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Set DeviceName
|
||||
If ($PrefixToUse){
|
||||
try{
|
||||
$PantherDir = 'w:\windows\panther'
|
||||
If (Test-Path -Path $PantherDir){
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-process xcopy "$UnattendFile $PantherDir /Y"
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
else{
|
||||
Writelog "$PantherDir doesn't exist, creating it"
|
||||
New-Item -Path $PantherDir -ItemType Directory -Force
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-Process xcopy.exe "$UnattendFile $PantherDir"
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
}
|
||||
catch{
|
||||
WriteLog "Copying Unattend.xml to name device failed"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
|
||||
#Add Drivers
|
||||
#Some drivers can sometimes fail to copy and dism ends up with a non-zero error code. Invoke-process will throw and terminate in these instances.
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
WriteLog 'Copying drivers'
|
||||
Write-Warning 'Copying Drivers - dism will pop a window with no progress. This can take a few minutes to complete. This is done so drivers are logged to the scriptlog.txt file. Please be patient.'
|
||||
Invoke-process dism.exe "/image:W:\ /Add-Driver /Driver:""$Drivers"" /Recurse"
|
||||
WriteLog 'Copying drivers succeeded'
|
||||
}
|
||||
|
||||
#Copy DISM log to USBDrive
|
||||
WriteLog "Copying dism log to $USBDrive"
|
||||
invoke-process xcopy "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
WriteLog "Copying dism log to $USBDrive succeeded"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
function Get-USBDrive(){
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Removable' -and $_.FileSystemType -eq 'NTFS'}).DriveLetter
|
||||
if ($null -eq $USBDriveLetter){
|
||||
#Must be using a fixed USB drive - difficult to grab drive letter from win32_diskdrive. Assume user followed instructions and used Deploy as the friendly name for partition
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Fixed' -and $_.FileSystemType -eq 'NTFS' -and $_.FileSystemLabel -eq 'Deploy'}).DriveLetter
|
||||
#If we didn't get the drive letter, stop the script.
|
||||
if ($null -eq $USBDriveLetter){
|
||||
WriteLog 'Cannot find USB drive letter - most likely using a fixed USB drive. Name the 2nd partition with the FFU files as Deploy so the script can grab the drive letter. Exiting'
|
||||
Exit
|
||||
}
|
||||
|
||||
}
|
||||
$USBDriveLetter = $USBDriveLetter + ":\"
|
||||
return $USBDriveLetter
|
||||
}
|
||||
|
||||
function Get-HardDrive(){
|
||||
$DeviceID = (Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' -and $_.Model -ne 'Microsoft Virtual Disk'}).DeviceID
|
||||
return $DeviceID
|
||||
}
|
||||
|
||||
function WriteLog($LogText){
|
||||
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText"
|
||||
}
|
||||
|
||||
function Set-DiskpartAnswerFiles($DiskpartFile,$DiskID){
|
||||
(Get-Content $DiskpartFile).Replace('disk 0', "disk $DiskID") | Set-Content -Path $DiskpartFile
|
||||
}
|
||||
|
||||
function Set-Computername($computername){
|
||||
[xml]$xml = Get-Content $UnattendFile
|
||||
if($xml.unattend.settings.component.Count -ge 2){
|
||||
#Assumes that Computername is the first component element
|
||||
$xml.unattend.settings.component[0].ComputerName = $computername
|
||||
}else{
|
||||
$xml.unattend.settings.component.ComputerName = $computername
|
||||
}
|
||||
$xml.Save($UnattendFile)
|
||||
return $computername
|
||||
}
|
||||
|
||||
function Invoke-Process {
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param
|
||||
(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$FilePath,
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$ArgumentList
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
try {
|
||||
$stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
$stdErrTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
|
||||
$startProcessParams = @{
|
||||
FilePath = $FilePath
|
||||
ArgumentList = $ArgumentList
|
||||
RedirectStandardError = $stdErrTempFile
|
||||
RedirectStandardOutput = $stdOutTempFile
|
||||
Wait = $true;
|
||||
PassThru = $true;
|
||||
NoNewWindow = $false;
|
||||
}
|
||||
if ($PSCmdlet.ShouldProcess("Process [$($FilePath)]", "Run with args: [$($ArgumentList)]")) {
|
||||
$cmd = Start-Process @startProcessParams
|
||||
$cmdOutput = Get-Content -Path $stdOutTempFile -Raw
|
||||
$cmdError = Get-Content -Path $stdErrTempFile -Raw
|
||||
if ($cmd.ExitCode -ne 0) {
|
||||
if ($cmdError) {
|
||||
throw $cmdError.Trim()
|
||||
}
|
||||
if ($cmdOutput) {
|
||||
throw $cmdOutput.Trim()
|
||||
}
|
||||
} else {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
WriteLog $cmdOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
#$PSCmdlet.ThrowTerminatingError($_)
|
||||
WriteLog $_
|
||||
Write-Host 'Script failed - check scriptlog.txt on the USB drive for more info'
|
||||
throw $_
|
||||
|
||||
} finally {
|
||||
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# This function can be used in instances where battery level might matter (e.g. installing firmware for Surface). The problem is that WinPE doesn't have
|
||||
# a driver for the battery installed, so you'll need to inject drivers, which can be tricky in that just injecting the battery driver might not be enough,
|
||||
# you might also need other drivers that the battery driver is dependent on.
|
||||
# function Get-Battery(){
|
||||
# while (($BattLev = (Get-CimInstance win32_battery).EstimatedChargeRemaining) -lt "35")
|
||||
# {
|
||||
# WriteLog "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Write-Host "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Start-Sleep 60
|
||||
# }
|
||||
|
||||
# WriteLog "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# Write-Host "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# }
|
||||
|
||||
#Get USB Drive and create log file
|
||||
$LogFileName = 'ScriptLog.txt'
|
||||
$USBDrive = Get-USBDrive
|
||||
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
|
||||
$LogFile = $USBDrive + $LogFilename
|
||||
WriteLog 'Begin Logging'
|
||||
|
||||
#Find PhysicalDrive
|
||||
$PhysicalDeviceID = Get-HardDrive
|
||||
WriteLog "Physical DeviceID is $PhysicalDeviceID"
|
||||
|
||||
#Parse DiskID Number
|
||||
$DiskID = $PhysicalDeviceID.substring($PhysicalDeviceID.length - 1,1)
|
||||
WriteLog "DiskID is $DiskID"
|
||||
|
||||
#Modify diskpart answer files if DiskID not 0
|
||||
$UEFIFFUPartitions = 'x:\CreateUEFI-FFU-Partitions.txt'
|
||||
$ExtendPartition = 'x:\ExtendPartition-UEFI.txt'
|
||||
$RecoveryPartition = 'x:\RecoveryPartition-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: $_"
|
||||
}
|
||||
try {
|
||||
Set-DiskpartAnswerFiles $RecoveryPartition $DiskID
|
||||
}
|
||||
catch {
|
||||
WriteLog "Modifying $RecoveryPartition failed with error: $_"
|
||||
}
|
||||
}
|
||||
|
||||
#Partition drive
|
||||
Writelog 'Creating partitions'
|
||||
#Start-Process -FilePath diskpart.exe -ArgumentList "/S $UEFIFFUPartitions" -Wait -ErrorAction Stop | Out-File $Logfile -Append
|
||||
Invoke-Process diskpart.exe "/S $UEFIFFUPartitions"
|
||||
Writelog 'Creating partitions succeeded'
|
||||
|
||||
#Find FFU Files
|
||||
[array]$FFUFiles = @(Get-ChildItem -Path $USBDrive*.ffu)
|
||||
$FFUCount = $FFUFiles.Count
|
||||
|
||||
#If multiple FFUs found, ask which to install
|
||||
If ($FFUCount -gt 1) {
|
||||
WriteLog "Found $FFUCount FFU Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $FFUCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; FFUFile = $FFUFiles[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, FFUFile
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$FFUSelected = Read-Host 'Enter the FFU number to install'
|
||||
$FFUSelected = $FFUSelected -1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($FFUSelected -le $FFUCount -1) -and $var)
|
||||
|
||||
$FFUFileToInstall = $array[$FFUSelected].FFUFile
|
||||
WriteLog "$FFUFileToInstall was selected"
|
||||
}
|
||||
elseif ($FFUCount -eq 1) {
|
||||
WriteLog "Found $FFUCount FFU File"
|
||||
$FFUFileToInstall = $FFUFiles[0].FullName
|
||||
WriteLog "$FFUFileToInstall will be installed"
|
||||
}
|
||||
else {
|
||||
Writelog 'No FFU files found'
|
||||
Write-Host 'No FFU files found'
|
||||
Exit
|
||||
}
|
||||
|
||||
#FindAP
|
||||
$APFolder = $USBDrive + "Autopilot\"
|
||||
If (Test-Path -Path $APFolder){
|
||||
[array]$APFiles = @(Get-ChildItem -Path $APFolder*.json)
|
||||
$APFilesCount = $APFiles.Count
|
||||
if ($APFilesCount -ge 1){
|
||||
$autopilot = $true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#FindPPKG
|
||||
$PPKGFolder = $USBDrive + "PPKG\"
|
||||
if (Test-Path -Path $PPKGFolder){
|
||||
[array]$PPKGFiles = @(Get-ChildItem -Path $PPKGFolder*.ppkg)
|
||||
$PPKGFilesCount = $PPKGFiles.Count
|
||||
if ($PPKGFilesCount -ge 1){
|
||||
$PPKG = $true
|
||||
}
|
||||
}
|
||||
|
||||
#FindUnattend
|
||||
$UnattendFolder = $USBDrive + "unattend\"
|
||||
$UnattendFilePath = $UnattendFolder + "unattend.xml"
|
||||
$UnattendPrefixPath = $UnattendFolder + "prefixes.txt"
|
||||
If (Test-Path -Path $UnattendFilePath){
|
||||
$UnattendFile = Get-ChildItem -Path $UnattendFilePath
|
||||
If ($UnattendFile){
|
||||
$Unattend = $true
|
||||
}
|
||||
}
|
||||
If (Test-Path -Path $UnattendPrefixPath){
|
||||
$UnattendPrefixFile = Get-ChildItem -Path $UnattendPrefixPath
|
||||
If ($UnattendPrefixFile){
|
||||
$UnattendPrefix = $true
|
||||
}
|
||||
}
|
||||
|
||||
#Ask for device name if unattend exists
|
||||
if ($Unattend -and $UnattendPrefix){
|
||||
Writelog 'Unattend file found with prefixes.txt. Getting prefixes.'
|
||||
$UnattendPrefixes = @(Get-content $UnattendPrefixFile)
|
||||
$UnattendPrefixCount = $UnattendPrefixes.Count
|
||||
If ($UnattendPrefixCount -gt 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefixes"
|
||||
$array = @()
|
||||
for($i=0;$i -le $UnattendPrefixCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; DeviceNamePrefix = $UnattendPrefixes[$i]}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, DeviceNamePrefix
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PrefixSelected = Read-Host 'Enter the prefix number to use for the device name'
|
||||
$PrefixSelected = $PrefixSelected -1
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid prefix number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PrefixSelected -le $UnattendPrefixCount -1) -and $var)
|
||||
$PrefixToUse = $array[$PrefixSelected].DeviceNamePrefix
|
||||
WriteLog "$PrefixToUse was selected"
|
||||
}
|
||||
elseif ($UnattendPrefixCount -eq 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefix"
|
||||
$PrefixToUse = $UnattendPrefixes[0]
|
||||
WriteLog "Will use $PrefixToUse as device name prefix"
|
||||
}
|
||||
#Get serial number to append. This can make names longer than 15 characters. Trim any leading or trailing whitespace
|
||||
$serial = (Get-CimInstance -ClassName win32_bios).SerialNumber.Trim()
|
||||
#Combine prefix with serial
|
||||
$computername = $PrefixToUse + $serial
|
||||
#If computername is longer than 15 characters, reduce to 15. Sysprep/unattend doesn't like ComputerName being longer than 15 characters even though Windows accepts it
|
||||
If ($computername.Length -gt 15){
|
||||
$computername = $computername.substring(0,15)
|
||||
}
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
elseif($Unattend){
|
||||
Writelog 'Unattend file found with no prefixes.txt, asking for name'
|
||||
[string]$computername = Read-Host 'Enter device name'
|
||||
Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
else {
|
||||
WriteLog 'No unattend folder found. Device name will be set via PPKG, AP JSON, or default OS name.'
|
||||
}
|
||||
|
||||
#If both AP and PPKG folder found with files, ask which to use.
|
||||
If($autopilot -eq $true -and $PPKG -eq $true){
|
||||
WriteLog 'Both PPKG and Autopilot json files found'
|
||||
Write-Host 'Both Autopilot JSON files and Provisioning packages were found.'
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APorPPKG = Read-Host 'Enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Incorrect value. Please enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APorPPKG -gt 0 -and $APorPPKG -lt 3) -and $var)
|
||||
If ($APorPPKG -eq 1){
|
||||
$PPKG = $false
|
||||
}
|
||||
else{
|
||||
$autopilot = $false
|
||||
}
|
||||
}
|
||||
|
||||
#If multiple AP json files found, ask which to install
|
||||
If ($APFilesCount -gt 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount Autopilot json Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $APFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; APFile = $APFiles[$i].FullName; APFileName = $APFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, APFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APFileSelected = Read-Host 'Enter the AP json file number to install'
|
||||
$APFileSelected = $APFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid AP json file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APFileSelected -le $APFilesCount -1) -and $var)
|
||||
|
||||
$APFileToInstall = $array[$APFileSelected].APFile
|
||||
$APFileName = $array[$APFileSelected].APFileName
|
||||
WriteLog "$APFileToInstall was selected"
|
||||
}
|
||||
elseif ($APFilesCount -eq 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount AP File"
|
||||
$APFileToInstall = $APFiles[0].FullName
|
||||
$APFileName = $APFiles[0].Name
|
||||
WriteLog "$APFileToInstall will be copied"
|
||||
}
|
||||
else {
|
||||
Writelog 'No AP files found or AP was not selected'
|
||||
}
|
||||
|
||||
#If multiple PPKG files found, ask which to install
|
||||
If ($PPKGFilesCount -gt 1 -and $PPKG -eq $true) {
|
||||
WriteLog "Found $PPKGFilesCount PPKG Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $PPKGFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; PPKGFile = $PPKGFiles[$i].FullName; PPKGFileName = $PPKGFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, PPKGFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PPKGFileSelected = Read-Host 'Enter the PPKG file number to install'
|
||||
$PPKGFileSelected = $PPKGFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid PPKG file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PPKGFileSelected -le $PPKGFilesCount -1) -and $var)
|
||||
|
||||
$PPKGFileToInstall = $array[$PPKGFileSelected].PPKGFile
|
||||
WriteLog "$PPKGFileToInstall was selected"
|
||||
}
|
||||
elseif ($PPKGFilesCount -eq 1 -and $PPKG -eq $true) {
|
||||
WriteLog "Found $PPKGFilesCount PPKG File"
|
||||
$PPKGFileToInstall = $PPKGFiles[0].FullName
|
||||
WriteLog "$PPKGFileToInstall will be used"
|
||||
}
|
||||
else {
|
||||
Writelog 'No PPKG files found or PPKG not selected.'
|
||||
}
|
||||
|
||||
#Find Drivers
|
||||
$Drivers = $USBDrive + "Drivers"
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
#Check if multiple driver folders found, if so, just select one folder to save time/space
|
||||
$DriverFolders = Get-ChildItem -Path $Drivers
|
||||
$DriverFoldersCount = $DriverFolders.count
|
||||
If ($DriverFoldersCount -gt 1)
|
||||
{
|
||||
WriteLog "Found $DriverFoldersCount driver folders"
|
||||
$array = @()
|
||||
|
||||
for($i=0; $i -le $DriverFoldersCount -1; $i++){
|
||||
$Properties = [ordered]@{Number = $i + 1; Drivers = $DriverFolders[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, Drivers
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$DriversSelected = Read-Host 'Enter the set of drivers to install'
|
||||
$DriversSelected = $DriversSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid driver folder number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($DriversSelected -le $DriverFoldersCount -1) -and $var)
|
||||
|
||||
$Drivers = $array[$DriversSelected].Drivers
|
||||
WriteLog "$Drivers was selected"
|
||||
}
|
||||
elseif ($DriverFoldersCount -eq 1) {
|
||||
WriteLog "Found $DriverFoldersCount driver folder"
|
||||
$Drivers = $DriverFolders.FullName
|
||||
WriteLog "$Drivers will be installed"
|
||||
}
|
||||
else {
|
||||
Writelog 'No driver folders found'
|
||||
}
|
||||
}
|
||||
|
||||
#If you want to enable battery level checking, uncomment the line below as well as the Get-Battery function near the top of the script
|
||||
#Get-Battery
|
||||
|
||||
#Apply FFU
|
||||
WriteLog "Applying FFU to $PhysicalDeviceID"
|
||||
WriteLog "Running command dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID"
|
||||
#In order for Applying Image progress bar to show up, need to call dism directly. Might be a better way to handle, but must have progress bar show up on screen.
|
||||
dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID
|
||||
if($LASTEXITCODE -eq 0){
|
||||
WriteLog 'Successfully applied FFU'
|
||||
}
|
||||
else{
|
||||
Writelog "Failed to apply FFU - LastExitCode = $LASTEXITCODE also check dism.log on the USB drive for more info"
|
||||
#Copy DISM log to USBDrive
|
||||
invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
exit
|
||||
}
|
||||
#Change code to allow for optimized FFUs
|
||||
|
||||
#Extend Windows partition and create recovery partition
|
||||
# Writelog 'Extending Windows partition'
|
||||
# Invoke-Process diskpart.exe "/S $ExtendPartition"
|
||||
# Writelog 'Extending Windows partition succeeded'
|
||||
|
||||
|
||||
#Copy modified WinRE if folder exists, else copy inbox WinRE
|
||||
$WinRE = $USBDrive + "WinRE\winre.wim"
|
||||
If (Test-Path -Path $WinRE)
|
||||
{
|
||||
WriteLog 'Assigning recovery partition drive letter R'
|
||||
Invoke-Process diskpart.exe "/S $RecoveryPartition"
|
||||
WriteLog 'Assigned recovery partition drive letter R successfully'
|
||||
WriteLog 'Copying modified WinRE to Recovery directory'
|
||||
Invoke-Process xcopy.exe "/h $WinRE R:\Recovery\WindowsRE\ /Y"
|
||||
WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
WriteLog 'Registering location of recovery tools'
|
||||
Invoke-Process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
WriteLog 'Registering location of recovery tools succeeded'
|
||||
}
|
||||
# else {
|
||||
# WriteLog 'Copying default WinRE to Recovery directory'
|
||||
# Invoke-Process xcopy.exe "/h W:\Windows\System32\Recovery\Winre.wim R:\Recovery\WindowsRE\ /Y"
|
||||
# WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
# WriteLog 'Registering location of recovery tools'
|
||||
# Invoke-process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
# WriteLog 'Registering location of recovery tools succeeded'
|
||||
# }
|
||||
|
||||
#Autopilot JSON
|
||||
If ($APFileToInstall){
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot"
|
||||
Invoke-process xcopy.exe "$APFileToInstall W:\Windows\provisioning\autopilot\"
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot succeeded"
|
||||
# Rename file in W:\Windows\Provisioning\Autopilot to AutoPilotConfigurationFile.json
|
||||
try {
|
||||
Rename-Item -Path "W:\Windows\Provisioning\Autopilot\$APFileName" -NewName 'W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json'
|
||||
WriteLog "Renamed W:\Windows\Provisioning\Autopilot\$APFilename to W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json"
|
||||
}
|
||||
|
||||
catch{
|
||||
Writelog "Copying $APFileToInstall to W:\windows\provisioning\autopilot failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Apply PPKG
|
||||
If ($PPKGFileToInstall){
|
||||
try {
|
||||
#Make sure to delete any existing PPKG on the USB drive
|
||||
Get-Childitem -Path $USBDrive\*.ppkg | ForEach-Object {
|
||||
Remove-item -Path $_.FullName
|
||||
}
|
||||
WriteLog "Copying $PPKGFileToInstall to $USBDrive"
|
||||
Invoke-process xcopy.exe "$PPKGFileToInstall $USBDrive"
|
||||
WriteLog "Copying $PPKGFileToInstall to $USBDrive succeeded"
|
||||
}
|
||||
|
||||
catch{
|
||||
Writelog "Copying $PPKGFileToInstall to $USBDrive failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Set DeviceName
|
||||
If ($PrefixToUse){
|
||||
try{
|
||||
$PantherDir = 'w:\windows\panther'
|
||||
If (Test-Path -Path $PantherDir){
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-process xcopy "$UnattendFile $PantherDir /Y"
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
else{
|
||||
Writelog "$PantherDir doesn't exist, creating it"
|
||||
New-Item -Path $PantherDir -ItemType Directory -Force
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-Process xcopy.exe "$UnattendFile $PantherDir"
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
}
|
||||
catch{
|
||||
WriteLog "Copying Unattend.xml to name device failed"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
|
||||
#Add Drivers
|
||||
#Some drivers can sometimes fail to copy and dism ends up with a non-zero error code. Invoke-process will throw and terminate in these instances.
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
WriteLog 'Copying drivers'
|
||||
Write-Host 'Copying Drivers - dism will pop a window with no progress. This can take a few minutes to complete. Please be patient. '
|
||||
Invoke-process dism.exe "/image:W:\ /Add-Driver /Driver:""$Drivers"" /Recurse"
|
||||
WriteLog 'Copying drivers succeeded'
|
||||
}
|
||||
|
||||
#Copy DISM log to USBDrive
|
||||
WriteLog "Copying dism log to $USBDrive"
|
||||
invoke-process xcopy "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
WriteLog "Copying dism log to $USBDrive succeeded"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,554 @@
|
||||
function Get-USBDrive(){
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Removable' -and $_.FileSystemType -eq 'NTFS'}).DriveLetter
|
||||
if ($null -eq $USBDriveLetter){
|
||||
#Must be using a fixed USB drive - difficult to grab drive letter from win32_diskdrive. Assume user followed instructions and used Deploy as the friendly name for partition
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Fixed' -and $_.FileSystemType -eq 'NTFS' -and $_.FileSystemLabel -eq 'Deploy'}).DriveLetter
|
||||
#If we didn't get the drive letter, stop the script.
|
||||
if ($null -eq $USBDriveLetter){
|
||||
WriteLog 'Cannot find USB drive letter - most likely using a fixed USB drive. Name the 2nd partition with the FFU files as Deploy so the script can grab the drive letter. Exiting'
|
||||
Exit
|
||||
}
|
||||
|
||||
}
|
||||
$USBDriveLetter = $USBDriveLetter + ":\"
|
||||
return $USBDriveLetter
|
||||
}
|
||||
|
||||
function Get-HardDrive(){
|
||||
$DeviceID = (Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' -and $_.Model -ne 'Microsoft Virtual Disk'}).DeviceID
|
||||
return $DeviceID
|
||||
}
|
||||
|
||||
function WriteLog($LogText){
|
||||
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText"
|
||||
}
|
||||
|
||||
function Set-DiskpartAnswerFiles($DiskpartFile,$DiskID){
|
||||
(Get-Content $DiskpartFile).Replace('disk 0', "disk $DiskID") | Set-Content -Path $DiskpartFile
|
||||
}
|
||||
|
||||
function Set-Computername($computername){
|
||||
[xml]$xml = Get-Content $UnattendFile
|
||||
if($xml.unattend.settings.component.Count -ge 2){
|
||||
#Assumes that Computername is the first component element
|
||||
$xml.unattend.settings.component[0].ComputerName = $computername
|
||||
}else{
|
||||
$xml.unattend.settings.component.ComputerName = $computername
|
||||
}
|
||||
$xml.Save($UnattendFile)
|
||||
return $computername
|
||||
}
|
||||
|
||||
function Invoke-Process {
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param
|
||||
(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$FilePath,
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$ArgumentList
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
try {
|
||||
$stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
$stdErrTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
|
||||
$startProcessParams = @{
|
||||
FilePath = $FilePath
|
||||
ArgumentList = $ArgumentList
|
||||
RedirectStandardError = $stdErrTempFile
|
||||
RedirectStandardOutput = $stdOutTempFile
|
||||
Wait = $true;
|
||||
PassThru = $true;
|
||||
NoNewWindow = $false;
|
||||
}
|
||||
if ($PSCmdlet.ShouldProcess("Process [$($FilePath)]", "Run with args: [$($ArgumentList)]")) {
|
||||
$cmd = Start-Process @startProcessParams
|
||||
$cmdOutput = Get-Content -Path $stdOutTempFile -Raw
|
||||
$cmdError = Get-Content -Path $stdErrTempFile -Raw
|
||||
if ($cmd.ExitCode -ne 0) {
|
||||
if ($cmdError) {
|
||||
throw $cmdError.Trim()
|
||||
}
|
||||
if ($cmdOutput) {
|
||||
throw $cmdOutput.Trim()
|
||||
}
|
||||
} else {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
WriteLog $cmdOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
#$PSCmdlet.ThrowTerminatingError($_)
|
||||
WriteLog $_
|
||||
Write-Host 'Script failed - check scriptlog.txt on the USB drive for more info'
|
||||
throw $_
|
||||
|
||||
} finally {
|
||||
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# This function can be used in instances where battery level might matter (e.g. installing firmware for Surface). The problem is that WinPE doesn't have
|
||||
# a driver for the battery installed, so you'll need to inject drivers, which can be tricky in that just injecting the battery driver might not be enough,
|
||||
# you might also need other drivers that the battery driver is dependent on.
|
||||
# function Get-Battery(){
|
||||
# while (($BattLev = (Get-CimInstance win32_battery).EstimatedChargeRemaining) -lt "35")
|
||||
# {
|
||||
# WriteLog "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Write-Host "Battery is currently at $BattLev`%. Waiting for 35`% to proceed..."
|
||||
# Start-Sleep 60
|
||||
# }
|
||||
|
||||
# WriteLog "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# Write-Host "Battery level is $BattLev `%, which is greater than 35'% applying FFU"
|
||||
# }
|
||||
|
||||
#Get USB Drive and create log file
|
||||
$LogFileName = 'ScriptLog.txt'
|
||||
$USBDrive = Get-USBDrive
|
||||
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
|
||||
$LogFile = $USBDrive + $LogFilename
|
||||
WriteLog 'Begin Logging'
|
||||
|
||||
#Find PhysicalDrive
|
||||
$PhysicalDeviceID = Get-HardDrive
|
||||
WriteLog "Physical DeviceID is $PhysicalDeviceID"
|
||||
|
||||
#Parse DiskID Number
|
||||
$DiskID = $PhysicalDeviceID.substring($PhysicalDeviceID.length - 1,1)
|
||||
WriteLog "DiskID is $DiskID"
|
||||
|
||||
#Modify diskpart answer files if DiskID not 0
|
||||
$UEFIFFUPartitions = 'x:\CreateUEFI-FFU-Partitions.txt'
|
||||
$ExtendPartition = 'x:\ExtendPartition-UEFI.txt'
|
||||
$RecoveryPartition = 'x:\RecoveryPartition-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: $_"
|
||||
}
|
||||
try {
|
||||
Set-DiskpartAnswerFiles $RecoveryPartition $DiskID
|
||||
}
|
||||
catch {
|
||||
WriteLog "Modifying $RecoveryPartition failed with error: $_"
|
||||
}
|
||||
}
|
||||
|
||||
#Find FFU Files
|
||||
[array]$FFUFiles = @(Get-ChildItem -Path $USBDrive*.ffu)
|
||||
$FFUCount = $FFUFiles.Count
|
||||
|
||||
#If multiple FFUs found, ask which to install
|
||||
If ($FFUCount -gt 1) {
|
||||
WriteLog "Found $FFUCount FFU Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $FFUCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; FFUFile = $FFUFiles[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, FFUFile
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$FFUSelected = Read-Host 'Enter the FFU number to install'
|
||||
$FFUSelected = $FFUSelected -1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($FFUSelected -le $FFUCount -1) -and $var)
|
||||
|
||||
$FFUFileToInstall = $array[$FFUSelected].FFUFile
|
||||
WriteLog "$FFUFileToInstall was selected"
|
||||
}
|
||||
elseif ($FFUCount -eq 1) {
|
||||
WriteLog "Found $FFUCount FFU File"
|
||||
$FFUFileToInstall = $FFUFiles[0].FullName
|
||||
WriteLog "$FFUFileToInstall will be installed"
|
||||
}
|
||||
else {
|
||||
Writelog 'No FFU files found'
|
||||
Write-Host 'No FFU files found'
|
||||
Exit
|
||||
}
|
||||
|
||||
#FindAP
|
||||
$APFolder = $USBDrive + "Autopilot\"
|
||||
If (Test-Path -Path $APFolder){
|
||||
[array]$APFiles = @(Get-ChildItem -Path $APFolder*.json)
|
||||
$APFilesCount = $APFiles.Count
|
||||
if ($APFilesCount -ge 1){
|
||||
$autopilot = $true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#FindPPKG
|
||||
$PPKGFolder = $USBDrive + "PPKG\"
|
||||
if (Test-Path -Path $PPKGFolder){
|
||||
[array]$PPKGFiles = @(Get-ChildItem -Path $PPKGFolder*.ppkg)
|
||||
$PPKGFilesCount = $PPKGFiles.Count
|
||||
if ($PPKGFilesCount -ge 1){
|
||||
$PPKG = $true
|
||||
}
|
||||
}
|
||||
|
||||
#FindUnattend
|
||||
$UnattendFolder = $USBDrive + "unattend\"
|
||||
$UnattendFilePath = $UnattendFolder + "unattend.xml"
|
||||
$UnattendPrefixPath = $UnattendFolder + "prefixes.txt"
|
||||
If (Test-Path -Path $UnattendFilePath){
|
||||
$UnattendFile = Get-ChildItem -Path $UnattendFilePath
|
||||
If ($UnattendFile){
|
||||
$Unattend = $true
|
||||
}
|
||||
}
|
||||
If (Test-Path -Path $UnattendPrefixPath){
|
||||
$UnattendPrefixFile = Get-ChildItem -Path $UnattendPrefixPath
|
||||
If ($UnattendPrefixFile){
|
||||
$UnattendPrefix = $true
|
||||
}
|
||||
}
|
||||
|
||||
#Ask for device name if unattend exists
|
||||
if ($Unattend -and $UnattendPrefix){
|
||||
Writelog 'Unattend file found with prefixes.txt. Getting prefixes.'
|
||||
$UnattendPrefixes = @(Get-content $UnattendPrefixFile)
|
||||
$UnattendPrefixCount = $UnattendPrefixes.Count
|
||||
If ($UnattendPrefixCount -gt 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefixes"
|
||||
$array = @()
|
||||
for($i=0;$i -le $UnattendPrefixCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; DeviceNamePrefix = $UnattendPrefixes[$i]}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, DeviceNamePrefix
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PrefixSelected = Read-Host 'Enter the prefix number to use for the device name'
|
||||
$PrefixSelected = $PrefixSelected -1
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid prefix number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PrefixSelected -le $UnattendPrefixCount -1) -and $var)
|
||||
$PrefixToUse = $array[$PrefixSelected].DeviceNamePrefix
|
||||
WriteLog "$PrefixToUse was selected"
|
||||
}
|
||||
elseif ($UnattendPrefixCount -eq 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefix"
|
||||
$PrefixToUse = $UnattendPrefixes[0]
|
||||
WriteLog "Will use $PrefixToUse as device name prefix"
|
||||
}
|
||||
#Get serial number to append. This can make names longer than 15 characters. Trim any leading or trailing whitespace
|
||||
$serial = (Get-CimInstance -ClassName win32_bios).SerialNumber.Trim()
|
||||
#Combine prefix with serial
|
||||
$computername = $PrefixToUse + $serial
|
||||
#If computername is longer than 15 characters, reduce to 15. Sysprep/unattend doesn't like ComputerName being longer than 15 characters even though Windows accepts it
|
||||
If ($computername.Length -gt 15){
|
||||
$computername = $computername.substring(0,15)
|
||||
}
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
elseif($Unattend){
|
||||
Writelog 'Unattend file found with no prefixes.txt, asking for name'
|
||||
[string]$computername = Read-Host 'Enter device name'
|
||||
Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
else {
|
||||
WriteLog 'No unattend folder found. Device name will be set via PPKG, AP JSON, or default OS name.'
|
||||
}
|
||||
|
||||
#If both AP and PPKG folder found with files, ask which to use.
|
||||
If($autopilot -eq $true -and $PPKG -eq $true){
|
||||
WriteLog 'Both PPKG and Autopilot json files found'
|
||||
Write-Host 'Both Autopilot JSON files and Provisioning packages were found.'
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APorPPKG = Read-Host 'Enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Incorrect value. Please enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APorPPKG -gt 0 -and $APorPPKG -lt 3) -and $var)
|
||||
If ($APorPPKG -eq 1){
|
||||
$PPKG = $false
|
||||
}
|
||||
else{
|
||||
$autopilot = $false
|
||||
}
|
||||
}
|
||||
|
||||
#If multiple AP json files found, ask which to install
|
||||
If ($APFilesCount -gt 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount Autopilot json Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $APFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; APFile = $APFiles[$i].FullName; APFileName = $APFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, APFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APFileSelected = Read-Host 'Enter the AP json file number to install'
|
||||
$APFileSelected = $APFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid AP json file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APFileSelected -le $APFilesCount -1) -and $var)
|
||||
|
||||
$APFileToInstall = $array[$APFileSelected].APFile
|
||||
$APFileName = $array[$APFileSelected].APFileName
|
||||
WriteLog "$APFileToInstall was selected"
|
||||
}
|
||||
elseif ($APFilesCount -eq 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount AP File"
|
||||
$APFileToInstall = $APFiles[0].FullName
|
||||
$APFileName = $APFiles[0].Name
|
||||
WriteLog "$APFileToInstall will be copied"
|
||||
}
|
||||
else {
|
||||
Writelog 'No AP files found or AP was not selected'
|
||||
}
|
||||
|
||||
#If multiple PPKG files found, ask which to install
|
||||
If ($PPKGFilesCount -gt 1 -and $PPKG -eq $true) {
|
||||
WriteLog "Found $PPKGFilesCount PPKG Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $PPKGFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; PPKGFile = $PPKGFiles[$i].FullName; PPKGFileName = $PPKGFiles[$i].Name}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, PPKGFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PPKGFileSelected = Read-Host 'Enter the PPKG file number to install'
|
||||
$PPKGFileSelected = $PPKGFileSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid PPKG file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PPKGFileSelected -le $PPKGFilesCount -1) -and $var)
|
||||
|
||||
$PPKGFileToInstall = $array[$PPKGFileSelected].PPKGFile
|
||||
WriteLog "$PPKGFileToInstall was selected"
|
||||
}
|
||||
elseif ($PPKGFilesCount -eq 1 -and $PPKG -eq $true) {
|
||||
WriteLog "Found $PPKGFilesCount PPKG File"
|
||||
$PPKGFileToInstall = $PPKGFiles[0].FullName
|
||||
WriteLog "$PPKGFileToInstall will be used"
|
||||
}
|
||||
else {
|
||||
Writelog 'No PPKG files found or PPKG not selected.'
|
||||
}
|
||||
|
||||
#Find Drivers
|
||||
$Drivers = $USBDrive + "Drivers"
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
#Check if multiple driver folders found, if so, just select one folder to save time/space
|
||||
$DriverFolders = Get-ChildItem -Path $Drivers
|
||||
$DriverFoldersCount = $DriverFolders.count
|
||||
If ($DriverFoldersCount -gt 1)
|
||||
{
|
||||
WriteLog "Found $DriverFoldersCount driver folders"
|
||||
$array = @()
|
||||
|
||||
for($i=0; $i -le $DriverFoldersCount -1; $i++){
|
||||
$Properties = [ordered]@{Number = $i + 1; Drivers = $DriverFolders[$i].FullName}
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, Drivers
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$DriversSelected = Read-Host 'Enter the set of drivers to install'
|
||||
$DriversSelected = $DriversSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid driver folder number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($DriversSelected -le $DriverFoldersCount -1) -and $var)
|
||||
|
||||
$Drivers = $array[$DriversSelected].Drivers
|
||||
WriteLog "$Drivers was selected"
|
||||
}
|
||||
elseif ($DriverFoldersCount -eq 1) {
|
||||
WriteLog "Found $DriverFoldersCount driver folder"
|
||||
$Drivers = $DriverFolders.FullName
|
||||
WriteLog "$Drivers will be installed"
|
||||
}
|
||||
else {
|
||||
Writelog 'No driver folders found'
|
||||
}
|
||||
}
|
||||
|
||||
#If you want to enable battery level checking, uncomment the line below as well as the Get-Battery function near the top of the script
|
||||
#Get-Battery
|
||||
|
||||
#Partition drive
|
||||
Writelog 'Creating partitions'
|
||||
#Start-Process -FilePath diskpart.exe -ArgumentList "/S $UEFIFFUPartitions" -Wait -ErrorAction Stop | Out-File $Logfile -Append
|
||||
Invoke-Process diskpart.exe "/S $UEFIFFUPartitions"
|
||||
Writelog 'Creating partitions succeeded'
|
||||
#Apply FFU
|
||||
WriteLog "Applying FFU to $PhysicalDeviceID"
|
||||
WriteLog "Running command dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID"
|
||||
#In order for Applying Image progress bar to show up, need to call dism directly. Might be a better way to handle, but must have progress bar show up on screen.
|
||||
dism /apply-ffu /ImageFile:$FFUFileToInstall /ApplyDrive:$PhysicalDeviceID
|
||||
if($LASTEXITCODE -eq 0){
|
||||
WriteLog 'Successfully applied FFU'
|
||||
}
|
||||
else{
|
||||
Writelog "Failed to apply FFU - LastExitCode = $LASTEXITCODE also check dism.log on the USB drive for more info"
|
||||
#Copy DISM log to USBDrive
|
||||
invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
exit
|
||||
}
|
||||
#Change code to allow for optimized FFUs
|
||||
|
||||
#Extend Windows partition and create recovery partition
|
||||
# Writelog 'Extending Windows partition'
|
||||
# Invoke-Process diskpart.exe "/S $ExtendPartition"
|
||||
# Writelog 'Extending Windows partition succeeded'
|
||||
|
||||
|
||||
#Copy modified WinRE if folder exists, else copy inbox WinRE
|
||||
$WinRE = $USBDrive + "WinRE\winre.wim"
|
||||
If (Test-Path -Path $WinRE)
|
||||
{
|
||||
WriteLog 'Assigning recovery partition drive letter R'
|
||||
Invoke-Process diskpart.exe "/S $RecoveryPartition"
|
||||
WriteLog 'Assigned recovery partition drive letter R successfully'
|
||||
WriteLog 'Copying modified WinRE to Recovery directory'
|
||||
Invoke-Process xcopy.exe "/h $WinRE R:\Recovery\WindowsRE\ /Y"
|
||||
WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
WriteLog 'Registering location of recovery tools'
|
||||
Invoke-Process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
WriteLog 'Registering location of recovery tools succeeded'
|
||||
}
|
||||
# else {
|
||||
# WriteLog 'Copying default WinRE to Recovery directory'
|
||||
# Invoke-Process xcopy.exe "/h W:\Windows\System32\Recovery\Winre.wim R:\Recovery\WindowsRE\ /Y"
|
||||
# WriteLog 'Copying WinRE to Recovery directory succeeded'
|
||||
# WriteLog 'Registering location of recovery tools'
|
||||
# Invoke-process W:\Windows\System32\Reagentc.exe "/Setreimage /Path R:\Recovery\WindowsRE /Target W:\Windows"
|
||||
# WriteLog 'Registering location of recovery tools succeeded'
|
||||
# }
|
||||
|
||||
#Autopilot JSON
|
||||
If ($APFileToInstall){
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot"
|
||||
Invoke-process xcopy.exe "$APFileToInstall W:\Windows\provisioning\autopilot\"
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot succeeded"
|
||||
# Rename file in W:\Windows\Provisioning\Autopilot to AutoPilotConfigurationFile.json
|
||||
try {
|
||||
Rename-Item -Path "W:\Windows\Provisioning\Autopilot\$APFileName" -NewName 'W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json'
|
||||
WriteLog "Renamed W:\Windows\Provisioning\Autopilot\$APFilename to W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json"
|
||||
}
|
||||
|
||||
catch{
|
||||
Writelog "Copying $APFileToInstall to W:\windows\provisioning\autopilot failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Apply PPKG
|
||||
If ($PPKGFileToInstall){
|
||||
try {
|
||||
#Make sure to delete any existing PPKG on the USB drive
|
||||
Get-Childitem -Path $USBDrive\*.ppkg | ForEach-Object {
|
||||
Remove-item -Path $_.FullName
|
||||
}
|
||||
WriteLog "Copying $PPKGFileToInstall to $USBDrive"
|
||||
Invoke-process xcopy.exe "$PPKGFileToInstall $USBDrive"
|
||||
WriteLog "Copying $PPKGFileToInstall to $USBDrive succeeded"
|
||||
}
|
||||
|
||||
catch{
|
||||
Writelog "Copying $PPKGFileToInstall to $USBDrive failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Set DeviceName
|
||||
If ($PrefixToUse){
|
||||
try{
|
||||
$PantherDir = 'w:\windows\panther'
|
||||
If (Test-Path -Path $PantherDir){
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-process xcopy "$UnattendFile $PantherDir /Y"
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
else{
|
||||
Writelog "$PantherDir doesn't exist, creating it"
|
||||
New-Item -Path $PantherDir -ItemType Directory -Force
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-Process xcopy.exe "$UnattendFile $PantherDir"
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
}
|
||||
catch{
|
||||
WriteLog "Copying Unattend.xml to name device failed"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
|
||||
#Add Drivers
|
||||
#Some drivers can sometimes fail to copy and dism ends up with a non-zero error code. Invoke-process will throw and terminate in these instances.
|
||||
If (Test-Path -Path $Drivers)
|
||||
{
|
||||
WriteLog 'Copying drivers'
|
||||
Write-Host 'Copying Drivers - dism will pop a window with no progress. This can take a few minutes to complete. Please be patient. '
|
||||
Invoke-process dism.exe "/image:W:\ /Add-Driver /Driver:""$Drivers"" /Recurse"
|
||||
WriteLog 'Copying drivers succeeded'
|
||||
}
|
||||
|
||||
#Copy DISM log to USBDrive
|
||||
WriteLog "Copying dism log to $USBDrive"
|
||||
invoke-process xcopy "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
WriteLog "Copying dism log to $USBDrive succeeded"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
select disk 0
|
||||
clean
|
||||
exit
|
||||
@@ -0,0 +1,11 @@
|
||||
select disk 0
|
||||
select partition 3
|
||||
Assign letter="W"
|
||||
extend
|
||||
shrink minimum=650
|
||||
create partition primary
|
||||
format quick fs=ntfs label="Recovery"
|
||||
assign letter="R"
|
||||
set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac"
|
||||
gpt attributes=0x8000000000000001
|
||||
exit
|
||||
@@ -0,0 +1,11 @@
|
||||
select disk 0
|
||||
select partition 3
|
||||
Assign letter="W"
|
||||
extend
|
||||
shrink minimum=650
|
||||
create partition primary
|
||||
format quick fs=ntfs label="Recovery"
|
||||
assign letter="R"
|
||||
set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac"
|
||||
gpt attributes=0x8000000000000001
|
||||
exit
|
||||
@@ -0,0 +1,5 @@
|
||||
wpeinit
|
||||
powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
||||
powershell -Noprofile -ExecutionPolicy Bypass -File x:\ApplyFFU.ps1
|
||||
exit
|
||||
|
||||
Reference in New Issue
Block a user