Projects/CaptureFFU.ps1
#======================================================================= # Clear Screen #======================================================================= #Clear-Host #======================================================================= # PowershellWindow Functions # Hide the PowerShell Window # https://community.spiceworks.com/topic/1710213-hide-a-powershell-console-window-when-running-a-script #======================================================================= $Script:showWindowAsync = Add-Type -MemberDefinition @" [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); "@ -Name "Win32ShowWindowAsync" -Namespace Win32Functions -PassThru function Show-Powershell() { $null = $showWindowAsync::ShowWindowAsync((Get-Process -Id $pid).MainWindowHandle, 10) } function Hide-Powershell() { $null = $showWindowAsync::ShowWindowAsync((Get-Process -Id $pid).MainWindowHandle, 2) } #Hide-Powershell #======================================================================= # MahApps.Metro #======================================================================= # Assign current script directory to a global variable $Global:MyScriptDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition) # Load presentationframework and Dlls for the MahApps.Metro theme [System.Reflection.Assembly]::LoadWithPartialName("presentationframework") | Out-Null [System.Reflection.Assembly]::LoadFrom("$Global:MyScriptDir\assembly\System.Windows.Interactivity.dll") | Out-Null [System.Reflection.Assembly]::LoadFrom("$Global:MyScriptDir\assembly\MahApps.Metro.dll") | Out-Null #======================================================================= # Console Title #======================================================================= #$host.ui.RawUI.WindowTitle = "Start-CaptureFFU" #======================================================================= # Test-InWinPE #======================================================================= function Test-InWinPE { return Test-Path -Path Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Control\MiniNT } #======================================================================= # LoadForm #======================================================================= function LoadForm { [CmdletBinding()] param( [Parameter(Mandatory = $True, Position = 1)] [string]$XamlPath ) # Import the XAML code [xml]$Global:xmlWPF = Get-Content -Path $XamlPath # Add WPF and Windows Forms assemblies try { Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms } catch { throw "Failed to load Windows Presentation Framework assemblies." } #Create the XAML reader using a new XML node reader $Global:xamGUI = [Windows.Markup.XamlReader]::Load((new-object System.Xml.XmlNodeReader $xmlWPF)) #Create hooks to each named object in the XAML $xmlWPF.SelectNodes("//*[@Name]") | ForEach { Set-Variable -Name ($_.Name) -Value $xamGUI.FindName($_.Name) -Scope Global } } #======================================================================= # LoadForm #======================================================================= LoadForm -XamlPath (Join-Path $Global:MyScriptDir 'CaptureFFU.xaml') #======================================================================= # Title #======================================================================= $TitleLabel.Content = 'Start-CaptureFFU' #======================================================================= # Variables #======================================================================= $Global:Manufacturer = Get-MyComputerManufacturer -Brief $Global:Model = Get-MyComputerModel -Brief $Global:SerialNumber = Get-MyBiosSerialNumber -Brief $Global:DismDescription = "$Global:Manufacturer $Global:Model $Global:SerialNumber" Write-Host -ForegroundColor Cyan "Description:$Global:DismDescription" $Global:DismCompress = 'Default' Write-Host -ForegroundColor Cyan "Compress:$Global:DismCompress" #======================================================================= # Title #======================================================================= #$TitleLabel.Content = 'CaptureFFU' #======================================================================= # CaptureDrives #======================================================================= # Create empty array of hard disk numbers $Global:ArrayOfDiskNumbers = @() #Get all the CaptureDrives $Global:CaptureDrives = Get-LocalDisk | Where-Object {$_.IsBoot -eq $false} # Populate the ComboBox $Global:CaptureDrives | foreach { $CaptureDriveComboBox.Items.Add("Disk $($_.DiskNumber) $($_.BusType) $($_.MediaType) - $($_.FriendlyName)") | Out-Null $Global:ArrayOfDiskNumbers += $_.Number } $CaptureDriveDetails.Content = '' #Select the first item $CaptureDriveComboBox.SelectedIndex = 0 #======================================================================= # Set-CaptureDriveComboBox #======================================================================= function Set-CaptureDriveComboBox { $CaptureDrive = Get-LocalDisk | Where-Object { $_.Number -eq $Global:ArrayOfDiskNumbers[$CaptureDriveComboBox.SelectedIndex] } # Work out if the size should be in GB or TB if ([math]::Round(($CaptureDrive.Size/1TB),2) -lt 1) { $CaptureDriveSize = "$([math]::Round(($CaptureDrive.Size/1000000000),0))GB" } else { $CaptureDriveSize = "$([math]::Round(($CaptureDrive.Size/1000000000000),2))TB" } $Global:DiskNumber = $CaptureDrive.DiskNumber $Global:DismCaptureDrive = "\\.\PhysicalDrive$($Global:DiskNumber)" Write-Host -ForegroundColor Cyan "CaptureDrive: $Global:DismCaptureDrive" $Global:DismName = "disk$($Global:DiskNumber)" Write-Host -ForegroundColor Cyan "Name: $Global:DismName" $CaptureDriveDetails.Content = @" $CaptureDriveSize $($CaptureDrive.PartitionStyle) $($CaptureDrive.NumberOfPartitions) Partitions "@ } #======================================================================= # Set-ImageFileComboBox #======================================================================= function Set-ImageFileComboBox { $Global:DestinationDrives = @() $Global:DestinationDrives = Get-DataDisk | Where-Object {$_.DiskNumber -ne $Global:DiskNumber} $ImageFile = $null $ImageFileComboBox.Items.Clear() if (-NOT ($Global:DestinationDrives)) { $ImageFileComboBox.IsEnabled = "False" $RunButton.IsEnabled = "False" } else { $RunButton.IsEnabled = "True" foreach ($DestinationDrive in $Global:DestinationDrives) { if ($DestinationDrive.DriveLetter -gt 0) { $ImageFileName = "disk$Global:DiskNumber" $ImageFile = "$($DestinationDrive.DriveLetter):\CaptureFFU\$Global:Manufacturer\$Global:Model\$($Global:SerialNumber)_$($ImageFileName).ffu" $ImageFileComboBox.Items.Add($ImageFile) | Out-Null } } $ImageFileComboBox.SelectedIndex = 0 } } #======================================================================= # Set-DismCommandText #======================================================================= function Set-DismCommandText { #Write-Host "Text: $($ImageFileComboBox.Text)" $Global:DismImageFile = $ImageFileComboBox.Text if ($Global:DismImageFile -gt 0) { Write-Host -ForegroundColor Cyan "ImageFile: $Global:DismImageFile" } $DismCommand.Text = "Dism.exe /Capture-FFU /ImageFile=`"$Global:DismImageFile`" /CaptureDrive=$Global:DismCaptureDrive /Name:`"$Global:DismName`" /Description:`"$Global:DismDescription`" /Compress:$Global:DismCompress" $ImageFileComboBox.IsEnabled = "True" } Set-CaptureDriveComboBox Set-ImageFileComboBox Set-DismCommandText <# $DestinationDetails.Content = @" DiskNumber: $($DestinationDrive.DiskNumber) FileSystemLabel: $($DestinationDrive.FileSystemLabel) FileSystem: $($DestinationDrive.FileSystem) Size: $($DestinationDrive.Size) SizeRemaining: $($DestinationDrive.SizeRemaining) "@ #> #======================================================================= # CaptureDriveComboBox Events #======================================================================= $CaptureDriveComboBox.add_SelectionChanged({ Set-CaptureDriveComboBox Set-ImageFileComboBox Set-DismCommandText }) $CaptureDriveComboBox.add_DropDownClosed({ Set-CaptureDriveComboBox Set-ImageFileComboBox Set-DismCommandText }) #======================================================================= # ImageFileComboBox Events #======================================================================= $ImageFileComboBox.add_SelectionChanged({ #Write-Host -ForegroundColor Magenta "add_SelectionChanged" Set-DismCommandText }) $ImageFileComboBox.add_DropDownClosed({ #Write-Host -ForegroundColor Magenta "add_DropDownClosed" Set-DismCommandText }) $ImageFileComboBox.add_IsKeyboardFocusedChanged({ #Write-Host -ForegroundColor Magenta "add_IsKeyboardFocusedChanged" Set-DismCommandText }) $ImageFileComboBox.add_IsKeyboardFocusWithinChanged({ #Write-Host -ForegroundColor Magenta "add_IsKeyboardFocusWithinChanged" Set-DismCommandText }) $ImageFileComboBox.add_KeyUp({ #Write-Host -ForegroundColor Magenta "add_KeyUp" Set-DismCommandText }) #======================================================================= # RunButton #======================================================================= $RunButton.add_Click({ if ($null -eq $Global:DismImageFile) { Write-Warning "DismImageFile value is null" } elseif ($Global:DismImageFile -eq '') { Write-Warning "DismImageFile value is nothing" } else { $ParentDirectory = Split-Path $Global:DismImageFile -Parent -ErrorAction Stop Write-Host "Dism.exe /Capture-FFU /ImageFile=`"$Global:DismImageFile`" /CaptureDrive=$Global:DismCaptureDrive /Name:`"$Global:DismName`" /Description:`"$Global:DismDescription`" /Compress:$Global:DismCompress" $xamGUI.Close() Show-Powershell #======================================================================= # Checks #======================================================================= if (Test-Path $Global:DismImageFile) { Write-Warning "ImageFile already exists. Rename the ImageFile and try again"; Break } if (-NOT (Test-InWinPE)) { Write-Warning "CaptureFFU must be run in WinPE" Break } #======================================================================= # Create FFU #======================================================================= if (!(Test-Path "$ParentDirectory")) { Try {New-Item -Path $ParentDirectory -ItemType Directory -Force -ErrorAction Stop} Catch {Write-Warning "Destination appears to be Read Only. Try another Destination Drive"; Break} } $CommandLine = "Dism.exe /Capture-FFU /ImageFile=`"$Global:DismImageFile`" /CaptureDrive=$Global:DismCaptureDrive /Name:`"$Global:DismName`" /Description:`"$Global:DismDescription`" /Compress:$Global:DismCompress" Write-Host "CommandLine: $CommandLine" Get-OSDPower -Property High Start-Process PowerShell.exe -Wait -WorkingDirectory $ParentDirectory -ArgumentList '-NoExit','-NoLogo','Dism.exe','/Capture-FFU',"/ImageFile='$Global:DismImageFile'","/CaptureDrive='$Global:DismCaptureDrive'","/Name:'$Global:DismName'","/Description:'$Global:DismDescription'","/Compress:$Global:DismCompress" Get-OSDPower -Property Balanced if (Test-Path $Global:DismImageFile) { Get-WindowsImage -ImagePath $Global:DismImageFile } #======================================================================= } }) #======================================================================= # ShowDialog #======================================================================= $xamGUI.ShowDialog() | Out-Null #======================================================================= |