Public/Backup/Backup-DiskToFFU.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
<#
.SYNOPSIS
Saves a Drive as Full Flash Update Windows Image (FFU)
 
.DESCRIPTION
Saves a Drive as Full Flash Update Windows Image (FFU)
 
.LINK
https://osd.osdeploy.com/module/functions/backup/backup-disktoffu
https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/deploy-windows-using-full-flash-update--ffu
 
.NOTES
21.1.27 Initial Release
#>

function Backup-DiskToFFU {
    [CmdletBinding()]
    Param (
        #Disk Number of the Drive to capture
        #Use Get-Disk to get the DiskNumber Property
        [Alias('Number')]
        [ValidateScript({$_ -in (Get-DiskToBackup | Select-Object -ExpandProperty DiskNumber)})]
        [int] $DiskNumber = (Get-DiskToBackup | Select-Object -ExpandProperty DiskNumber -First 1),

        [ValidateScript({$_ -in (Get-DriveForBackupFile | Where-Object {$_.DiskNumber -ne $DiskNumber} | Select-Object -ExpandProperty DriveLetter)})]
        [string] $DestinationDriveLetter = "$(Get-DriveForBackupFile | Where-Object {$_.DiskNumber -ne $DiskNumber} | Select-Object -ExpandProperty DriveLetter -First 1)",
        
        #Windows Image Property: Specifies the name of an image
        [string] $Name = "disk$DiskNumber",

        #Full path to save the Windows Image
        [Alias('ImagePath')]
        [string] $ImageFile = "$($DestinationDriveLetter):\BackupFFU\$(Get-EZComputerManufacturer)\$(Get-EZComputerModel)\$(Get-EZComputerSerialNumber)_$Name.ffu",

        #Windows Image Property: Specifies the description of the image
        [string] $Description = "$(Get-EZComputerManufacturer) $(Get-EZComputerModel) $(Get-EZComputerSerialNumber)",

        #Compression level. Default or None
        [ValidateSet('Default','None')]
        [string] $Compress = 'Default',

        #Executes the capture
        [switch] $Force
    )
    #======================================================================================================
    # Enable Verbose
    #======================================================================================================
    if ($Force -eq $false) {$VerbosePreference = 'Continue'}
    #======================================================================================================
    # Gather
    #======================================================================================================
    $GetCommandNoun = Get-Command -Name Backup-DiskToFFU | Select-Object -ExpandProperty Noun
    $GetCommandVersion = Get-Command -Name Backup-DiskToFFU | Select-Object -ExpandProperty Version
    $GetCommandHelpUri = Get-Command -Name Backup-DiskToFFU | Select-Object -ExpandProperty HelpUri
    $GetCommandModule = Get-Command -Name Backup-DiskToFFU | Select-Object -ExpandProperty Module
    $GetModuleDescription = Get-Module -Name $GetCommandModule | Select-Object -ExpandProperty Description
    $GetModuleProjectUri = Get-Module -Name $GetCommandModule | Select-Object -ExpandProperty ProjectUri
    $GetModulePath = Get-Module -Name $GetCommandModule | Select-Object -ExpandProperty Path

    $GetDriveForBackupFile = $(Get-DriveForBackupFile)
    $DiskIsBoot = $(Get-DiskIsBoot)
    $DiskToBackup = $(Get-DiskToBackup)
    $Volumes = $(Get-Volume)
    #======================================================================================================
    # Validate
    #======================================================================================================
    if ($ImageFile -like ":*") {
        $ImageFile = "C$ImageFile"
    }
    #======================================================================================================
    # Usage
    #======================================================================================================
    Write-Host -ForegroundColor DarkGray    '======================================================================================================'
    Write-Host -ForegroundColor White       "Backup-DiskToFFU " -NoNewline
    Write-Host -ForegroundColor Cyan        "$GetCommandVersion $GetModulePath"
    Write-Host -ForegroundColor DarkCyan    $GetCommandHelpUri
    Write-Host -ForegroundColor DarkGray    '======================================================================================================'
    Write-Host -ForegroundColor Yellow       "The following Partitions will be saved in the FFU:"
    foreach ($item in (Get-Partition | Where-Object {$_.DiskNumber -eq $DiskNumber})) {
        Write-Host -ForegroundColor White "DiskNumber:$($item.DiskNumber) Partition:$($item.PartitionNumber) DriveLetter:$($item.DriveLetter) Type:$($item.Type) $([math]::round($item.Size / 1000000000, 0)) GB"
    }
    
    if ($DiskToBackup -or $DiskIsBoot) {
        Write-Host -ForegroundColor DarkGray    '======================================================================================================'
        Write-Host -ForegroundColor Cyan        "-DiskNumber $DiskNumber"
        Write-Host -ForegroundColor White       "The Disk Number of the Disk to capture as an FFU. The default is the first available Disk"
        foreach ($item in $DiskToBackup) {
            Write-Host -ForegroundColor Cyan    "$($item.DiskNumber) " -NoNewline
            Write-Host -ForegroundColor Gray    "$($item.PartitionStyle) Partitions:$($item.NumberOfPartitions) $($item.FriendlyName) $($item.BusType) [$([math]::round($item.Size / 1000000000, 0))GB]"
        }
        if ($DiskIsBoot) {
            Write-Warning "Disks from a running OS cannot be selected"
            foreach ($item in $DiskIsBoot) {
                Write-Host -ForegroundColor Red "$($item.DiskNumber) $($item.PartitionStyle) Partitions:$($item.NumberOfPartitions) $($item.FriendlyName) $($item.BusType) [$([math]::round($item.Size / 1000000000, 0))GB]"
            }
        }
    }

    Write-Host -ForegroundColor DarkGray    '======================================================================================================'
    Write-Host -ForegroundColor Cyan        "-DestinationDriveLetter $DestinationDriveLetter"

    if ($GetDriveForBackupFile | Where-Object {$_.DiskNumber -ne $DiskNumber}) {
        Write-Host -ForegroundColor White   "Verify that the Drive you select below has plenty of space for your image"
        foreach ($item in ($GetDriveForBackupFile | Where-Object {$_.DiskNumber -ne $DiskNumber})) {
            Write-Host -ForegroundColor Cyan    "$($item.DriveLetter) " -NoNewline
            Write-Host -ForegroundColor Gray    "$($item.FileSystem) $($item.FileSystemLabel) [$($item.DriveType) TotalSize:$([math]::round($item.Size / 1000000000, 0))GB SizeRemaining:$([math]::round($item.SizeRemaining / 1000000000, 0))GB]"
        }
        foreach ($item in ($GetDriveForBackupFile | Where-Object {$_.DiskNumber -eq $DiskNumber})) {
            Write-Warning "Volumes that are being captured cannot be used as a Destination Drive"
            Write-Host -ForegroundColor Red    "$($item.DriveLetter) $($item.FileSystem) $($item.FileSystemLabel) [$($item.DriveType) TotalSize:$([math]::round($item.Size / 1000000000, 0))GB SizeRemaining:$([math]::round($item.SizeRemaining / 1000000000, 0))GB]"
        }
    } else {
        Write-Warning "Could not find any drives that you can backup to"
        Break
    }
    Write-Host -ForegroundColor DarkGray    '======================================================================================================'
    Write-Host -ForegroundColor Cyan        "-ImageFile $ImageFile"
    Write-Host -ForegroundColor White       'This path is generated automatically by combining the DestinationDriveLetter, ComputerManufacturer,'
    Write-Host -ForegroundColor White       'ComputerModel SerialNumber and DiskNumber. You can fully modify this path to override the'
    Write-Host -ForegroundColor White       'DestinationDriveLetter or to save to a Network share'
    $ParentDirectory = Split-Path $ImageFile -Parent
    if (!(Test-Path "$ParentDirectory")) {
        Write-Host -ForegroundColor Yellow "Directory '$ParentDirectory' does not exist and will be created automatically"
    }

    Write-Host -ForegroundColor DarkGray    '======================================================================================================'
    Write-Host -ForegroundColor Cyan        'Other Parameters'
    Write-Host -ForegroundColor White       ' -Name ' -NoNewline
    Write-Host -ForegroundColor Gray        'Windows Image Property: Specifies the name of an image'
    Write-Host -ForegroundColor White       ' -Description ' -NoNewline
    Write-Host -ForegroundColor Gray        'Windows Image Property: Specifies the description of the image'
    Write-Host -ForegroundColor White       ' -Compress ' -NoNewline
    Write-Host -ForegroundColor Gray        'Compression level | Values: Default None'
    Write-Host -ForegroundColor Yellow      ' -Force ' -NoNewline
    Write-Host -ForegroundColor Gray        'Executes the capture'
    Write-Host -ForegroundColor DarkGray    '======================================================================================================'
    Write-Host -ForegroundColor Cyan        'Cmd Syntax:'
    Write-Host -ForegroundColor White       "DISM.exe /Capture-FFU /ImageFile=`"$ImageFile`" /CaptureDrive=\\.\PhysicalDrive$DiskNumber /Name:`"$Name`" /Description:`"$Description`" /Compress:$Compress"
    Write-Host -ForegroundColor DarkCyan    ''
    Write-Host -ForegroundColor Cyan        "PowerShell Syntax:"
    Write-Host -ForegroundColor White       "Backup-DiskToFFU -ImageFile `"$ImageFile`" -DiskNumber $DiskNumber -Name `"$Name`" -Description `"$Description`" -Compress $Compress " -NoNewline
    Write-Host -ForegroundColor Yellow      "-Force"
    Write-Host -ForegroundColor DarkCyan    ''
    Write-Host -ForegroundColor Cyan        "PowerShell Splatting:"
    Write-Host -ForegroundColor White       '$FFU = @{'
    Write-Host -ForegroundColor White       " ImageFile = `"$ImageFile`""
    Write-Host -ForegroundColor White       " DiskNumber = $DiskNumber"
    Write-Host -ForegroundColor White       " Name = `"$Name`""
    Write-Host -ForegroundColor White       " Description = `"$Description`""
    Write-Host -ForegroundColor White       " Compress = `"$Compress`""
    Write-Host -ForegroundColor White       "}"
    Write-Host -ForegroundColor White       "Backup-DiskToFFU @FFU " -NoNewline
    Write-Host -ForegroundColor Yellow      "-Force"
    Write-Host -ForegroundColor DarkGray    '======================================================================================================'
    
    if ([string]::IsNullOrEmpty($DestinationDriveLetter)) {
        Write-Warning "Unable to find a proper DestinationDriveLetter to store the Windows Image FFU file"
        Write-Warning "-Destination Drive must be larger than 10 GB and formatted NTFS"
        Write-Warning "-Destination Drive must not exist on the disk you are capturing (DiskNumber: $DiskNumber)"
        Write-Warning "-Network Drives are not supported in this release"
        Write-Warning "To bypass these issues, adjust and use the Command Prompt Syntax"
        Break
    }

    
    if ($env:SystemDrive -ne 'X:') {
        Write-Warning "You should be in WinPE to capure a proper FFU. If you have issues, that's on you!"
    }

    if ($Force) {
        if (!(Test-Path "$ParentDirectory")) {
            New-Item -Path $ParentDirectory -ItemType Directory -Force -ErrorAction Stop | Out-Null
        }
        DISM.exe /Capture-FFU /ImageFile="$ImageFile" /CaptureDrive=\\.\PhysicalDrive$DiskNumber /Name:"$Name" /Description:"$Description" /Compress:$Compress
        #Return Get-WindowsImage -ImagePath $ImageFile
    } else {
        Write-Warning "If everything looks good, add the -Force parameter to capture the FFU"
    }
}

$ScriptBlock = {
    param($CommandName,$ParameterName,$stringMatch)
    Get-DriveForBackupFile | Select-Object -ExpandProperty DriveLetter 
}

Register-ArgumentCompleter -CommandName Backup-DiskToFFU -ParameterName DestinationDriveLetter -ScriptBlock $ScriptBlock