Private/OptimizeVirtualDisk.ps1
function OptimizeVirtualDisk { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory)] [string] $FilePath ) if (!(TestVHDFileValid $FilePath)) { return } $alreadyMounted = $false $dismountRequired = $false try { Write-Verbose "Getting disk image for VHD '$FilePath'." $diskImage = Get-DiskImage -ImagePath $FilePath -ErrorVariable err if ($null -eq $diskImage) { Write-Error "Failed to obtain disk image for VHD '$FilePath': $err" return } $startingSize = $diskImage.FileSize $alreadyMounted = $diskImage.Attached -eq $true if (!$alreadyMounted) { Write-Verbose "Mounting VHD file '$FilePath'." Mount-VHD -Path $FilePath -ErrorVariable err if ($err) { Write-Error "Failed to mount VHD '$FilePath': $err" return } Write-Verbose "VHD was mounted successfully. Getting disk image from it." $dismountRequired = $true $diskImage = Get-DiskImage -ImagePath $FilePath -ErrorVariable err if ($err) { Write-Error "Failed to obtain disk image for VHD '$FilePath' after mounting it. The error was: $err" return } elseif (!$diskImage) { Write-Error "Failed to obtain disk image for VHD '$FilePath' after mounting it." return } } if ($null -eq $diskImage.Number) { Write-Error "Failed to obtain disk image number for VHD '$FilePath'." return } #(c) 2024 AJ Tek Corporation. All Rights Reserved $vhdDisk = $diskImage | Get-Disk -ErrorAction SilentlyContinue -ErrorVariable err if ($null -eq $vhdDisk) { Write-Error "Failed to obtain disk from disk image for VHD '$FilePath'. The error was: $err" return } elseif (!$vhdDisk) { Write-Error "Failed to obtain disk from disk image for VHD '$FilePath'." return } $vhdPartition = Get-Partition -DiskNumber $vhdDisk.Number -ErrorVariable err if ($err) { Write-Warning "Failed to obtain partition from disk number $($vhdDisk.Number) for VHD '$FilePath'. The error was: $err" return } elseif (!$vhdPartition) { Write-Warning "Failed to obtain partition from disk number $($vhdDisk.Number) for VHD '$FilePath'." return } $win32Volumes = Get-CimInstance Win32_Volume -Verbose:$false -ErrorAction SilentlyContinue try { $defragFinishedNormally = $false $defragErrorRaised = $false #! Be careful adjusting this. If refactoring $partition ensure that all $job.Command.Replace('$partition.DriveLetter', ...) calls are updated as well. foreach ($partition in $vhdPartition) { if ($partition.DriveLetter -eq [char]0) { Write-Verbose "Skipping partition #$($partition.PartitionNumber) (empty drive letter)." continue } elseif ($Script:VALID_VOLUME_LETTERS -notcontains $partition.DriveLetter) { Write-Verbose "Skipping partition #$($partition.PartitionNumber) (invalid drive letter '$($partition.DriveLetter)')." continue } $targetWin32Volume = $win32Volumes | Where-Object { $_.DeviceID -eq "\\?\Volume$($partition.Guid)\" } $volumeLabel = "" if ($targetWin32Volume -and ![string]::IsNullOrWhiteSpace($targetWin32Volume.Label)) { $volumeLabel = " (DriveName '$($targetWin32Volume.Label)')" } #(c) 2024 AJ Tek Corporation. All Rights Reserved Write-Verbose "Invoking Optimize-Volume for DriveLetter '$($partition.DriveLetter)'$($volumeLabel) with parameters: -Defrag" $job = Optimize-Volume -DriveLetter $partition.DriveLetter -Defrag -ErrorAction Stop -AsJob WriteCustomJobProgress -Job $job -Activity $job.Command.Replace('$partition.DriveLetter', "'$($partition.DriveLetter)'$($volumeLabel)") Write-Verbose "Invoking Optimize-Volume for DriveLetter '$($partition.DriveLetter)'$($volumeLabel) with parameters: -SlabConsolidate -ReTrim" $job = Optimize-Volume -DriveLetter $partition.DriveLetter -SlabConsolidate -ReTrim -ErrorAction Stop -AsJob WriteCustomJobProgress -Job $job -Activity $job.Command.Replace('$partition.DriveLetter', "'$($partition.DriveLetter)'$($volumeLabel)") Write-Verbose "Invoking Optimize-Volume for DriveLetter '$($partition.DriveLetter)'$($volumeLabel) with parameters: -Defrag" $job = Optimize-Volume -DriveLetter $partition.DriveLetter -Defrag -ErrorAction Stop -AsJob WriteCustomJobProgress -Job $job -Activity $job.Command.Replace('$partition.DriveLetter', "'$($partition.DriveLetter)'$($volumeLabel)") Write-Verbose "Invoking Optimize-Volume for DriveLetter '$($partition.DriveLetter)'$($volumeLabel) with parameters: -SlabConsolidate" $job = Optimize-Volume -DriveLetter $partition.DriveLetter -SlabConsolidate -ErrorAction Stop -AsJob WriteCustomJobProgress -Job $job -Activity $job.Command.Replace('$partition.DriveLetter', "'$($partition.DriveLetter)'$($volumeLabel)") } $defragFinishedNormally = $true } catch { $defragErrorRaised = $true Write-Error "Error while defragmenting partition with drive letter '$($partition.DriveLetter)'$($volumeLabel): $_`r`n`r`nNo other partitions will be defragmented, and the final VHD optimization via Optimize-VHD will be skipped." return } finally { if (!$defragFinishedNormally -and !$defragErrorRaised) { Write-Warning "Detected a CTRL+C abort while defragmenting. It is highly recommended that you run checkdisk on the volume to prevent any corruption/data loss." } } Write-Verbose "Defragmentation succeeded! Dismounting VHD and starting final optimization via Optimize-VHD." Dismount-VHD -Path $FilePath -ErrorVariable err if ($err) { # This is a terminating error since we don't want to eat up all the available drive letters on the system. throw "Failed to dismount VHD file before final optimization. The error was: $err" } $dismountRequired = $false $optimizeFinishedNormally = $false $optimizeErrorRaised = $false Write-Verbose "Optimizing and compressing VHD file '$FilePath'." try { [void](Optimize-VHD $FilePath -Mode Full -ErrorAction SilentlyContinue -ErrorVariable err) $optimizeFinishedNormally = $true if ($err) { Write-Error "Failed final optimization via Optimize-VHD. The error was: $err" return } } catch { $optimizeErrorRaised = $true throw } finally { if (!$optimizeFinishedNormally -and !$optimizeErrorRaised) { Write-Warning "Detected a CTRL+C abort while performing final optimization. It is highly recommended that you run checkdisk on the volume to prevent any corruption/data loss." } } if ($alreadyMounted) { Write-Verbose "Mounting VHD file again since it was mounted when we started." Mount-VHD -Path $FilePath -ErrorVariable err if ($err) { Write-Error "Failed to mount VHD '$FilePath' again after optimization: $err" return } } $finalDiskImage = Get-DiskImage -ImagePath $FilePath -ErrorAction SilentlyContinue -ErrorVariable err if ($err) { Write-Error "Failed to obtain disk image for VHD '$FilePath' to calculate difference in size. The error was: $err" return } elseif ($null -eq $finalDiskImage) { Write-Error "Failed to obtain disk image for VHD '$FilePath' to calculate difference in size." return } $endSize = $finalDiskImage.FileSize # endSize - startingSize: we are tracking the change, not the amount saved. $change = [int64]($endSize - $startingSize) $formattedChange = FormatHumanReadable ([Math]::Abs($change)) $sign = if ($change -lt 0) { "-" } else { "" } $output = [PSCustomObject]@{ "Path" = $FilePath "OriginalSize" = $startingSize "CurrentSize" = $endSize "Change" = $change "ChangeFormatted" = "${sign}${formattedChange}" } Write-Output $output } finally { if ($dismountRequired) { Write-Verbose "Dismounting VHD since it was not dismounted earlier." Dismount-VHD -Path $FilePath -ErrorVariable err if ($err) { Write-Warning "VHD '$FilePath' was mounted so that it could be optimized, but failed to dismount: $err" Write-Warning "Please dismount the VHD manually." } } } } # Copyright (c) 2024 AJ Tek Corporation. All Rights Reserved. # SIG # Begin signature block # MIIVmwYJKoZIhvcNAQcCoIIVjDCCFYgCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU5JgxcLiYAY0CLH1/cPyPJC8E # nHegghH7MIIFbzCCBFegAwIBAgIQSPyTtGBVlI02p8mKidaUFjANBgkqhkiG9w0B # AQwFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVy # MRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEh # MB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTIxMDUyNTAwMDAw # MFoXDTI4MTIzMTIzNTk1OVowVjELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3Rp # Z28gTGltaXRlZDEtMCsGA1UEAxMkU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5n # IFJvb3QgUjQ2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjeeUEiIE # JHQu/xYjApKKtq42haxH1CORKz7cfeIxoFFvrISR41KKteKW3tCHYySJiv/vEpM7 # fbu2ir29BX8nm2tl06UMabG8STma8W1uquSggyfamg0rUOlLW7O4ZDakfko9qXGr # YbNzszwLDO/bM1flvjQ345cbXf0fEj2CA3bm+z9m0pQxafptszSswXp43JJQ8mTH # qi0Eq8Nq6uAvp6fcbtfo/9ohq0C/ue4NnsbZnpnvxt4fqQx2sycgoda6/YDnAdLv # 64IplXCN/7sVz/7RDzaiLk8ykHRGa0c1E3cFM09jLrgt4b9lpwRrGNhx+swI8m2J # mRCxrds+LOSqGLDGBwF1Z95t6WNjHjZ/aYm+qkU+blpfj6Fby50whjDoA7NAxg0P # OM1nqFOI+rgwZfpvx+cdsYN0aT6sxGg7seZnM5q2COCABUhA7vaCZEao9XOwBpXy # bGWfv1VbHJxXGsd4RnxwqpQbghesh+m2yQ6BHEDWFhcp/FycGCvqRfXvvdVnTyhe # Be6QTHrnxvTQ/PrNPjJGEyA2igTqt6oHRpwNkzoJZplYXCmjuQymMDg80EY2NXyc # uu7D1fkKdvp+BRtAypI16dV60bV/AK6pkKrFfwGcELEW/MxuGNxvYv6mUKe4e7id # FT/+IAx1yCJaE5UZkADpGtXChvHjjuxf9OUCAwEAAaOCARIwggEOMB8GA1UdIwQY # MBaAFKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQy65Ka/zWWSC8oQEJw # IDaRXBeF5jAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zATBgNVHSUE # DDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEMGA1Ud # HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FBQUNlcnRpZmlj # YXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0 # cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUAA4IBAQASv6Hvi3Sa # mES4aUa1qyQKDKSKZ7g6gb9Fin1SB6iNH04hhTmja14tIIa/ELiueTtTzbT72ES+ # BtlcY2fUQBaHRIZyKtYyFfUSg8L54V0RQGf2QidyxSPiAjgaTCDi2wH3zUZPJqJ8 # ZsBRNraJAlTH/Fj7bADu/pimLpWhDFMpH2/YGaZPnvesCepdgsaLr4CnvYFIUoQx # 2jLsFeSmTD1sOXPUC4U5IOCFGmjhp0g4qdE2JXfBjRkWxYhMZn0vY86Y6GnfrDyo # XZ3JHFuu2PMvdM+4fvbXg50RlmKarkUT2n/cR/vfw1Kf5gZV6Z2M8jpiUbzsJA8p # 1FiAhORFe1rYMIIGGjCCBAKgAwIBAgIQYh1tDFIBnjuQeRUgiSEcCjANBgkqhkiG # 9w0BAQwFADBWMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVk # MS0wKwYDVQQDEyRTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYw # HhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIxMjM1OTU5WjBUMQswCQYDVQQGEwJHQjEY # MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJTZWN0aWdvIFB1Ymxp # YyBDb2RlIFNpZ25pbmcgQ0EgUjM2MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB # igKCAYEAmyudU/o1P45gBkNqwM/1f/bIU1MYyM7TbH78WAeVF3llMwsRHgBGRmxD # eEDIArCS2VCoVk4Y/8j6stIkmYV5Gej4NgNjVQ4BYoDjGMwdjioXan1hlaGFt4Wk # 9vT0k2oWJMJjL9G//N523hAm4jF4UjrW2pvv9+hdPX8tbbAfI3v0VdJiJPFy/7Xw # iunD7mBxNtecM6ytIdUlh08T2z7mJEXZD9OWcJkZk5wDuf2q52PN43jc4T9OkoXZ # 0arWZVeffvMr/iiIROSCzKoDmWABDRzV/UiQ5vqsaeFaqQdzFf4ed8peNWh1OaZX # nYvZQgWx/SXiJDRSAolRzZEZquE6cbcH747FHncs/Kzcn0Ccv2jrOW+LPmnOyB+t # AfiWu01TPhCr9VrkxsHC5qFNxaThTG5j4/Kc+ODD2dX/fmBECELcvzUHf9shoFvr # n35XGf2RPaNTO2uSZ6n9otv7jElspkfK9qEATHZcodp+R4q2OIypxR//YEb3fkDn # 3UayWW9bAgMBAAGjggFkMIIBYDAfBgNVHSMEGDAWgBQy65Ka/zWWSC8oQEJwIDaR # XBeF5jAdBgNVHQ4EFgQUDyrLIIcouOxvSK4rVKYpqhekzQwwDgYDVR0PAQH/BAQD # AgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwGwYD # VR0gBBQwEjAGBgRVHSAAMAgGBmeBDAEEATBLBgNVHR8ERDBCMECgPqA8hjpodHRw # Oi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RS # NDYuY3JsMHsGCCsGAQUFBwEBBG8wbTBGBggrBgEFBQcwAoY6aHR0cDovL2NydC5z # ZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdSb290UjQ2LnA3YzAj # BggrBgEFBQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wDQYJKoZIhvcNAQEM # BQADggIBAAb/guF3YzZue6EVIJsT/wT+mHVEYcNWlXHRkT+FoetAQLHI1uBy/YXK # ZDk8+Y1LoNqHrp22AKMGxQtgCivnDHFyAQ9GXTmlk7MjcgQbDCx6mn7yIawsppWk # vfPkKaAQsiqaT9DnMWBHVNIabGqgQSGTrQWo43MOfsPynhbz2Hyxf5XWKZpRvr3d # MapandPfYgoZ8iDL2OR3sYztgJrbG6VZ9DoTXFm1g0Rf97Aaen1l4c+w3DC+IkwF # kvjFV3jS49ZSc4lShKK6BrPTJYs4NG1DGzmpToTnwoqZ8fAmi2XlZnuchC4NPSZa # PATHvNIzt+z1PHo35D/f7j2pO1S8BCysQDHCbM5Mnomnq5aYcKCsdbh0czchOm8b # kinLrYrKpii+Tk7pwL7TjRKLXkomm5D1Umds++pip8wH2cQpf93at3VDcOK4N7Ew # oIJB0kak6pSzEu4I64U6gZs7tS/dGNSljf2OSSnRr7KWzq03zl8l75jy+hOds9TW # SenLbjBQUGR96cFr6lEUfAIEHVC1L68Y1GGxx4/eRI82ut83axHMViw1+sVpbPxg # 51Tbnio1lB93079WPFnYaOvfGAA0e0zcfF/M9gXr+korwQTh2Prqooq2bYNMvUoU # KD85gnJ+t0smrWrb8dee2CvYZXD5laGtaAxOfy/VKNmwuWuAh9kcMIIGZjCCBM6g # AwIBAgIRAMyLU7NDPs1zQXTEeYo/k2YwDQYJKoZIhvcNAQEMBQAwVDELMAkGA1UE # BhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkGA1UEAxMiU2VjdGln # byBQdWJsaWMgQ29kZSBTaWduaW5nIENBIFIzNjAeFw0yMjA2MzAwMDAwMDBaFw0y # NTA2MjkyMzU5NTlaMFkxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRsw # GQYDVQQKDBJBSiBUZWsgQ29ycG9yYXRpb24xGzAZBgNVBAMMEkFKIFRlayBDb3Jw # b3JhdGlvbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOLV0fXklSI7 # zs4Awbc5rzQGG/XJaEgZLEXFVvzqKWlApLYSdXMLTzW14/IEoVMmi+qunmgIF98c # 3gUFm6dTSkf+ccSIWgHwjD/pPMX4leIO1Qen+OBup4p33XRNqHsWLzBqlWmKRcLW # 5cyRDRXedgZDvZozhxTqLhk0tFdTcBoGULA13Z/TfotbwNCx05W304VgluUogNX2 # 4yv80QYmHa6667ewXuAPNiPWQgFE4fyXOvFsRUFEtllRZmUpOPqZtGURw2ZYd+cZ # UFn4QrC9DzlESFEjQzMMF5iUjrKCI25jq53MkTZHnOjO9KGH5SvcLo5CbgNptpzr # d1P7AsoHFedvErNbp6YOhKrkv7F0ksVgqBh1v2Yc1ShpzZBEJ6pL+QsdnWzl6Wzb # B8xEYEdbNF0A0/kyFHm2tYSxxCsVO4caoL2CcsA3q/1h73MBWEcNT2fV6o25L+LY # sNUkFRc6ZRqOQ/ub9na8jCOwuhxi8MXTGo22Crinp5bbzYA/liNm97G77mJe59za # aaoulTWzk5y06k7VgLDYtR7IgZ47Aytnj/jr7S5/P/1F2K3lbRUdGyKdifubSgjk # m1gubK0cm3Y6L7Ar2X1nBm+PpAZqA54jQBs9cV03MVDjZb3AvjooCi9uOCJ4998U # TV9Sn2873/GUXE6dRB+w0fD/FcUxX2pjAgMBAAGjggGsMIIBqDAfBgNVHSMEGDAW # gBQPKssghyi47G9IritUpimqF6TNDDAdBgNVHQ4EFgQU0JE9KqcvG1A1pTeA+vcd # t1flqK0wDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYI # KwYBBQUHAwMwSgYDVR0gBEMwQTA1BgwrBgEEAbIxAQIBAwIwJTAjBggrBgEFBQcC # ARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EMAQQBMEkGA1UdHwRCMEAw # PqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVT # aWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUFBwEBBG0wazBEBggrBgEFBQcwAoY4aHR0 # cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIz # Ni5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMCEGA1Ud # EQQaMBiBFmFqLmFjY291bnRpbmdAYWp0ZWsuY2EwDQYJKoZIhvcNAQEMBQADggGB # ACa/QJzMPVDohIgQtL4/Yr7z7DlaybhjLQ2gEx7pU6G/hNQ+kClqLR3hykkJ7fwZ # 4cYX1TF4JWnkmQ0GZwj5bk5RaSyDatQVVIW8AQNMIEEhUftHJn1REz16CzXxlgzd # d+GTqYmwommBa6DlFO88fwF0FL3KJgvNguQGSU9sGIGUWyQuxqFUwXRgsXpwQbpR # 1H6qsViLN1SGPXO+iqUSyejmd9mIbc8b0IxuR6rDtU2PIcU8XzwsJv8M/L1paQut # 4m7dNw54gsoRVp+KJWawkkEVM6xvszCo8VIk8ZGetRCBT+ZSunIb/LFripb++lR8 # tIBkq8zYEhEp9U8GQZbKNZzfelix4kRt5wUq39rpBU8aHoU4GRXFs571jb/qBz/x # AVckN5cosppJxe+AW/TR9qKrL8uKOJ9cCLRXjPdLGSmHA5XMN6ecgRE+yfLMOauX # pUo33dCGh36TZuSi7P4uz2trEEUfmaQlr/TEp0xtbgUxTopdUYh9xagN4bbkRmoB # izGCAwowggMGAgEBMGkwVDELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28g # TGltaXRlZDErMCkGA1UEAxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENB # IFIzNgIRAMyLU7NDPs1zQXTEeYo/k2YwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcC # AQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYB # BAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFNK1usfmBWxS # nSZZ0rumdKNF42L5MA0GCSqGSIb3DQEBAQUABIICAKjsAYDMsBeYHzNdlWNNPZmt # P7Ti8WUh3o9P/MmyyNIWR5yy7x5l8n+2MXlLUKdjNwgXZFOk3UlMUm/GHOuJTxjL # YEESuBGRw5y+3cw0SY1y4rOB6q/cGYjJ8FGwmjd2HCc4UxAX6H2gq7Ic3YMLc5/U # FMIcR0Iri02mmnybLu0eLvvYKbS6NWHNBjpgFrWG49vA1f9m7JpFXxRIeo1j3Rja # N8vdKeZDGQucYexAu+jFQ4cZbIEAFqCkWU7tzuZhpImoO3wjrkh6cse2Kst2qSZc # cYDgix7EneBuZHagoYiWnz34q4gaEFd13rGNp7A5BapxXGHwaBlGhLUXhoWDK/Ic # ArrTOIFNqZ3aZGwaOhL/pdG5kpOH9PnUXqYnFaQBko0HrlZ9L7aZ0QdbBKYt5zIx # mpL9qr5SzgpFTQXLuGrsn0fqF9611ztG4jfy1DdKUSRXthk2gEHeP8VRmHEeg5LI # 5eEomxXq/7BZqS2lH20vxpQXZhlccZZaLqHCXXR+IPjJdkqyY3U/Gx2aVaaJaKkA # R6rrclrCcEBO+6ys6QRmheMrua+q3ccvY4zbAxkN9oFvMyaKGIbejYhX2rpxEB8X # xOYAS2SOJviTSj2hBRMpqtivKTTptrSSqaTpOfR3vJ2Ka5XLCfgJf9Z4QmMbO1/A # q/bUXb44JknTVGvdEBNN # SIG # End signature block |