From 4ed08ebb9b617a3a21e418e418941d10a03ebf85 Mon Sep 17 00:00:00 2001
From: Alan Mason <1923621+2Shirt@users.noreply.github.com>
Date: Fri, 24 Nov 2017 20:54:40 -0800
Subject: [PATCH] 2016-02: Retroactive Updates
---
System32/Winpeshl.ini | 5 +
System32/menu.cmd | 9 +-
WK/ConEmu/ConEmu.xml | 643 ++++++++++++++++++++++++++++++
WK/{ => Explorer++}/config.xml | 3 +-
WK/FastCopy.ini | 165 --------
WK/{ => HWMonitor}/hwmonitorw.ini | 0
WK/{ => Notepad2}/Notepad2.ini | 0
WK/Scripts/WK.ps1 | 631 ++++-------------------------
WK/Scripts/imaging.ps1 | 463 +++++++++++++++++++++
WK/Scripts/init.ps1 | 91 +++++
WK/Scripts/servers.ps1 | 85 ++++
make-cd.cmd | 7 +-
make.cmd | 37 +-
13 files changed, 1399 insertions(+), 740 deletions(-)
create mode 100644 System32/Winpeshl.ini
create mode 100644 WK/ConEmu/ConEmu.xml
rename WK/{ => Explorer++}/config.xml (95%)
delete mode 100644 WK/FastCopy.ini
rename WK/{ => HWMonitor}/hwmonitorw.ini (100%)
rename WK/{ => Notepad2}/Notepad2.ini (100%)
create mode 100644 WK/Scripts/imaging.ps1
create mode 100644 WK/Scripts/servers.ps1
diff --git a/System32/Winpeshl.ini b/System32/Winpeshl.ini
new file mode 100644
index 0000000..313d0a7
--- /dev/null
+++ b/System32/Winpeshl.ini
@@ -0,0 +1,5 @@
+[LaunchApp]
+[LaunchApps]
+wpeinit
+wpeutil updatebootinfo
+"%SystemDrive%\WK\ConEmu\ConEmu64.exe", /cmd PowerShell -ExecutionPolicy Bypass "%SystemDrive%\WK\Scripts\WK.ps1" -new_console:n
\ No newline at end of file
diff --git a/System32/menu.cmd b/System32/menu.cmd
index 2de9884..eb9c80b 100644
--- a/System32/menu.cmd
+++ b/System32/menu.cmd
@@ -12,7 +12,7 @@ for %%f in (%*) do (
)
:LaunchMenu
-PowerShell -ExecutionPolicy Bypass %systemdrive%\WK\Scripts\WK.ps1
+"%SystemDrive%\WK\ConEmu\ConEmu64.exe" /cmd PowerShell -ExecutionPolicy Bypass "%SystemDrive%\WK\Scripts\WK.ps1" -new_console:n
goto Done
:Abort
@@ -27,10 +27,5 @@ goto Exit
:Exit
echo.
-rem echo Press any key to exit...
-rem pause>nul
popd
-endlocal
-cls
-echo Careful now...
-echo.
\ No newline at end of file
+endlocal
\ No newline at end of file
diff --git a/WK/ConEmu/ConEmu.xml b/WK/ConEmu/ConEmu.xml
new file mode 100644
index 0000000..26a3611
--- /dev/null
+++ b/WK/ConEmu/ConEmu.xml
@@ -0,0 +1,643 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WK/config.xml b/WK/Explorer++/config.xml
similarity index 95%
rename from WK/config.xml
rename to WK/Explorer++/config.xml
index 90a1914..342f0de 100644
--- a/WK/config.xml
+++ b/WK/Explorer++/config.xml
@@ -68,7 +68,7 @@
4
-
+
@@ -108,5 +108,6 @@
+
diff --git a/WK/FastCopy.ini b/WK/FastCopy.ini
deleted file mode 100644
index 6251717..0000000
--- a/WK/FastCopy.ini
+++ /dev/null
@@ -1,165 +0,0 @@
-[main]
-bufsize="128"
-max_transize="16"
-nonbuf_minsize_ntfs2="64"
-nonbuf_minsize_fat="128"
-is_readosbuf="0"
-max_history="10"
-default_copy_mode="1"
-skip_empty_dir="1"
-ignore_error="1"
-estimate_mode="0"
-disk_mode="0"
-is_toplevel="0"
-is_errlog="1"
-is_utf8log="1"
-filelog_mode="0"
-aclerr_log="0"
-streamerr_log="0"
-is_samedir_rename="1"
-shext_autoclose="1"
-shext_tasktray="0"
-shext_dd_noconfirm="0"
-shext_right_noconfirm="0"
-exec_confirm="0"
-force_start="0"
-lcid="-1"
-speed_level="11"
-overwrite_del="0"
-acl="0"
-stream="0"
-verify="0"
-nsa_del="0"
-deldir_with_filter="0"
-move_attr="0"
-serial_move="0"
-serial_verify_move="0"
-reparse2="1"
-extend_filter="0"
-taskbarMode="0"
-infoSpan="2"
-win_pos="-10000,-10000,-10000,-10000"
-driveMap=""
-[src_history]
-0=""
-1=""
-2=""
-3=""
-4=""
-5=""
-6=""
-7=""
-8=""
-9=""
-[dst_history]
-0=""
-1=""
-2=""
-3=""
-4=""
-5=""
-6=""
-7=""
-8=""
-9=""
-[del_history]
-0=""
-1=""
-2=""
-3=""
-4=""
-5=""
-6=""
-7=""
-8=""
-9=""
-[include_history]
-0=""
-1=""
-2=""
-3=""
-4=""
-5=""
-6=""
-7=""
-8=""
-9=""
-[exclude_history]
-0=""
-1=""
-2=""
-3=""
-4=""
-5=""
-6=""
-7=""
-8=""
-9=""
-[fromdate_history]
-0=""
-1=""
-2=""
-3=""
-4=""
-5=""
-6=""
-7=""
-8=""
-9=""
-[todate_history]
-0=""
-1=""
-2=""
-3=""
-4=""
-5=""
-6=""
-7=""
-8=""
-9=""
-[minsize_history]
-0=""
-1=""
-2=""
-3=""
-4=""
-5=""
-6=""
-7=""
-8=""
-9=""
-[maxsize_history]
-0=""
-1=""
-2=""
-3=""
-4=""
-5=""
-6=""
-7=""
-8=""
-9=""
-[finaction_0]
-title="Normal"
-sound=""
-cmd=""
-shutdown_time="-1"
-flags="1"
-[finaction_1]
-title="Standby"
-sound=""
-cmd=""
-shutdown_time="60"
-flags="65"
-[finaction_2]
-title="Hibernate"
-sound=""
-cmd=""
-shutdown_time="60"
-flags="129"
-[finaction_3]
-title="Shutdown"
-sound=""
-cmd=""
-shutdown_time="60"
-flags="257"
diff --git a/WK/hwmonitorw.ini b/WK/HWMonitor/hwmonitorw.ini
similarity index 100%
rename from WK/hwmonitorw.ini
rename to WK/HWMonitor/hwmonitorw.ini
diff --git a/WK/Notepad2.ini b/WK/Notepad2/Notepad2.ini
similarity index 100%
rename from WK/Notepad2.ini
rename to WK/Notepad2/Notepad2.ini
diff --git a/WK/Scripts/WK.ps1 b/WK/Scripts/WK.ps1
index e063361..f39f397 100644
--- a/WK/Scripts/WK.ps1
+++ b/WK/Scripts/WK.ps1
@@ -4,571 +4,100 @@
$wd = $(Split-Path $MyInvocation.MyCommand.Path)
pushd "$wd"
. .\init.ps1
+. .\servers.ps1
+. .\imaging.ps1
clear
$host.UI.RawUI.WindowTitle = "WK PE Tool"
$logpath = "$WKPath\Info\$date"
md "$logpath" 2>&1 | Out-Null
$log = "$logpath\winpe.log"
-$source_server = "10.0.0.10"
-$backup_servers = @(
- @{ "ip"="10.0.0.10";
- "letter"="Z";
- "name"="ServerOne";
- "path"="Backups"},
- @{ "ip"="10.0.0.11";
- "name"="ServerTwo";
- "letter"="Y";
- "path"="Backups"}
- )
-$backup_user = "backup"
-$backup_pass = "Abracadabra"
# Functions
-function apply-image {
- Param([string]$image, [int]$index)
- $path = ""
- $split_image = $false
- $split_image_pattern = ""
-
- # Check for local source
- $volumes = Get-Volume | Where-Object {$_.Size -ne 0 -and $_.DriveLetter -match '^[C-Z]$'}
- foreach ($v in $volumes) {
- $letter = $v.DriveLetter + ":"
- if (Test-Path "$letter\sources\$image.wim") {
- $path = "$letter\sources\$image.wim"
- } elseif (Test-Path "$letter\sources\$image.swm") {
- $path = "$letter\sources\$image.swm"
- $split_image = $true
- $split_image_pattern = "$letter\sources\$image*.swm"
- }
- }
-
- # Check for remote source (if necessary)
- if ($path -match '^$') {
- net use z: "\\$source_server\Windows" /user:guest notarealpassword
- if (Test-Path "Z:\$image.wim") {
- $path = "Z:\$image.wim"
- } elseif (Test-Path "Z:\$image.swm") {
- $path = "Z:\$image.swm"
- }
- }
-
- # Expand Image
- if ($path -match '\.wim$') {
- Expand-WindowsImage -ImagePath "$path" -Index $index -ApplyPath 'W:\'
- } elseif ($path -match '\.swm$') {
- Expand-WindowsImage -ImagePath "$path" -Index $index -ApplyPath 'W:\' -SplitImageFilePattern "$split_image_pattern"
- } else {
- net use z: /delete
- throw "Source image not found."
- }
- net use z: /delete
-}
-function format-gpt {
- Param($dest_disk)
- wk-write "Drive will use a GPT (UEFI) layout."
-
- # Double-check we have the right drive
- ## I don't trust the order will be the same for diskpart & PS Storage Cmdlets
- $_sel_uid = $dest_disk.Guid
- if ($dest_disk.PartitionStyle -imatch "MBR") {
- # MBR disks don't have GUIDs and use the signature in hex instead
- $_sel_uid = "{0:x}" -f $dest_disk.Signature
- }
- $diskpart_script = "select disk {0}`r`n" -f $dest_disk.DiskNumber
- $diskpart_script += "uniqueid disk"
- Out-File -encoding 'UTF8' -filepath "$wd\diskpart.script" -inputobject $diskpart_script
- Start-Process "diskpart" -argumentlist @("/s", "$wd\diskpart.script") -wait -nonewwindow -PassThru -RedirectStandardOutput "$wd\drive_uid" | Out-Null
- if (!(Get-Content "$wd\drive_uid" | Where-Object {$_ -imatch $_sel_uid})) {
- # GUIDs do not match
- wk-error "Diskpart failed to select the same disk for formatting, aborting setup."
- wk-warn "This system requires manual formatting & setup"
- wk-write ""
- throw "Failed to format disk"
- } else {
- wk-write ("Selecting Disk {0} ({1})" -f $dest_disk.DiskNumber, $_sel_uid)
- }
-
- # Generate Diskpart script and execute
- ## NOTE 1: PS Storage Cmdlets can't be used; See Keith Garner's response here:
- ## https://social.technet.microsoft.com/Forums/en-US/9d78da31-557f-4408-89e0-a1603f7ebe0d
- ##
- ## NOTE 2: This overwrites existing diskpart.script file without confirmation.
- $diskpart_script = "select disk {0}`r`n" -f $dest_disk.DiskNumber
- $diskpart_script += "clean`r`n"
- $diskpart_script += "convert gpt`r`n"
-
- # 1. Windows RE tools partition (Windows 8+)
- if ($dest_windows_version.Name -match '^Windows (8|10)') {
- $diskpart_script += "create partition primary size=300`r`n"
- $diskpart_script += "format quick fs=ntfs label='Windows RE tools'`r`n"
- $diskpart_script += "assign letter='T'`r`n"
- $diskpart_script += "set id='de94bba4-06d1-4d40-a16a-bfd50179d6ac'`r`n"
- $diskpart_script += "gpt attributes=0x8000000000000001`r`n"
- }
-
- # 2. System partition
- $diskpart_script += "create partition efi size=260`r`n"
- ## NOTE: Allows for Advanced Format 4Kn drives
- $diskpart_script += "format quick fs=fat32 label='System'`r`n"
- $diskpart_script += "assign letter='S'`r`n"
-
- # 3. Microsoft Reserved (MSR) partition
- $diskpart_script += "create partition msr size=128`r`n"
-
- # 4. Windows partition
- $diskpart_script += "create partition primary`r`n"
- $diskpart_script += "format quick fs=ntfs label='Windows'`r`n"
- $diskpart_script += "assign letter='W'`r`n"
-
- # Run script
- Out-File -encoding 'UTF8' -filepath "$wd\diskpart.script" -inputobject $diskpart_script
- Start-Process "diskpart" -argumentlist @("/s", "$wd\diskpart.script") -wait -nonewwindow
-}
-function format-mbr {
- Param($dest_disk)
- wk-write "Drive will use a MBR (legacy) layout."
-
- if ($dest_disk.PartitionStyle -notmatch '^RAW$') {
- # Only clean if necessary
- Clear-Disk $dest_disk.DiskNumber -RemoveData -RemoveOEM -Confirm:$false
- }
- Initialize-Disk $dest_disk.DiskNumber -PartitionStyle 'MBR'
- New-Partition -DiskNumber $dest_disk.DiskNumber -Size 100Mb -DriveLetter 'S' -IsActive:$true
- New-Partition -DiskNumber $dest_disk.DiskNumber -UseMaximumSize -DriveLetter 'W' -IsActive:$false
- Format-Volume -DriveLetter 'S' -FileSystem 'NTFS' -NewFileSystemLabel 'System Reserved'
- Format-Volume -DriveLetter 'W' -FileSystem 'NTFS' -NewFileSystemLabel 'Windows'
-}
-function select-disk {
- $_skipped_parts = 0
- # Check if any source drives were detected
- $disks = Get-Disk | Where-Object {$_.Size -ne 0} | Sort-Object -Property "Number"
- if ($disks.count -eq 0) {
- wk-error "No suitable source drives were detected."
- return $false
- } elseif ($disks.count -eq $null) {
- # Assuming only one disk is available
- $answer = $disks.DiskNumber
- } else {
- # Build source menu
- $menu_imaging_source = "For which drive are we creating backup image(s)?`r`n`r`n"
- $valid_answers = @("M", "m")
- foreach ($_ in $disks) {
- $valid_answers += $_.DiskNumber
- $menu_imaging_source += "{0}: {1,4:N0} Gb`t[{2}] ({3}) {4}`r`n" -f $_.DiskNumber, ($_.Size / 1GB), $_.PartitionStyle, $_.BusType, $_.FriendlyName
- }
- $menu_imaging_source += "`r`n"
- $menu_imaging_source += "M: Main Menu`r`n"
- $menu_imaging_source += "`r`n"
- $menu_imaging_source += "Please make a selection`r`n"
-
- # Select source
- do {
- clear
- $answer = read-host -prompt $menu_imaging_source
- } until ($valid_answers -contains $answer)
- }
-
- if ($answer -imatch '^\d+$') {
- # Valid disk selected
- clear
- $_d = Get-Disk -number $answer
- $_disk_details = "Disk:`t{0,4:N0} Gb`t[{1}] ({2}) {3}" -f ($_d.Size / 1GB), $_d.PartitionStyle, $_d.BusType, $_d.FriendlyName
- wk-write "$_disk_details"
- wk-write "Partition(s):"
-
- # Print partition info
- $partitions = Get-Partition -DiskNumber $_d.DiskNumber
- foreach ($_p in $partitions) {
- # Assign letter
- Add-PartitionAccessPath -DiskNumber $_d.DiskNumber -PartitionNumber $_p.PartitionNumber -AssignDriveLetter 2>&1 | Out-Null
-
- # Update partition info
- $_p = Get-Partition -DiskNumber $_d.DiskNumber -PartitionNumber $_p.PartitionNumber
- $_v = Get-Volume -Partition $_p
-
- # Set size label
- $_size = (human-size $_p.size 0)
- if ($_v -ne $null) {
- $_used = (human-size ($_v.Size - $_v.SizeRemaining) 0)
- }
-
- # Print partition info
- if ($_p.AccessPaths -eq $null) {
- # No drive letter
- $_msg = " *{0:N0}:`t ({1}) {2}" -f $_p.PartitionNumber, $_size, $_p.Type
- wk-error "$_msg"
- $_skipped_parts += 1
- } else {
- # Has drive letter
- $_path = $_p.AccessPaths | Where-Object {$_ -imatch '^\w:\\$'}
- $_label = " {0}" -f $_p.Type
- if ($_v -and $_v.FileSystemLabel -ne "") {
- $_label = '"{0}"' -f $_v.FileSystemLabel
- }
- $_msg = " {0:N0}:`t{1} ({2,6}) {3} ({4} used)" -f $_p.PartitionNumber, $_path, $_size, $_label, $_used
- wk-write "$_msg"
- }
- }
- if ($_skipped_parts -gt 0) {
- wk-warn " *`tUnable to backup these partition(s)"
- }
- wk-write ""
- if (ask " Backup these partition(s)?") {
- return $_d
- } else {
- return $false
- }
- }
-
- return $answer
-}
-function select-server {
- # Build server menu
- $avail_servers = @(gdr | Where-Object {$_.DisplayRoot -imatch '\\\\'})
- if ($avail_servers.count -eq 0) {
- wk-error "No suitable backup servers were detected."
- return $false
- }
- $menu_imaging_server = "Where are we saving the backup image(s)?`r`n`r`n"
- $valid_answers = @("M", "m")
- for ($i=0; $i -lt $avail_servers.length; $i++) {
- $valid_answers += ($i + 1)
- $menu_imaging_server += ("{0}: {1} ({2:N2} Gb free)`r`n" -f ($i + 1), $avail_servers[$i].Description, ($avail_servers[$i].Free / 1Gb))
- }
- $menu_imaging_server += "`r`n"
- $menu_imaging_server += "M: Main Menu`r`n"
- $menu_imaging_server += "`r`n"
- $menu_imaging_server += "Please make a selection`r`n"
-
- # Select server
- do {
-# clear
- $answer = read-host -prompt $menu_imaging_server
- } until ($valid_answers -contains $answer)
-
- if ($answer -imatch '^\d+$') {
- $answer -= 1
- return $avail_servers[$answer]
- }
- return $answer
-}
function wk-exit {
- popd
- if ($answer -match 'R') {
- #pause "Press Enter to Reboot... "
- wpeutil reboot
- } elseif ($answer -match 'S') {
- #pause "Press Enter to Shutdown... "
- wpeutil shutdown
+ param([string]$action)
+ switch ($action) {
+ 'Q' {PowerShell -ExecutionPolicy Bypass; break}
+ 'R' {wpeutil reboot; break}
+ 'S' {wpeutil shutdown; break}
+ default {throw}
}
exit 0
}
-function mount-servers {
- # Mount servers
- wk-write "Connecting to backup server(s)"
- foreach ($_server in $backup_servers) {
- if (test-connection $_server.ip -count 3 -quiet) {
- try {
- $_path = "\\{0}\{1}" -f $_server.ip, $_server.path
- $_drive = "{0}:" -f $_server.letter
- net use $_drive "$_path" /user:$backup_user $backup_pass | Out-Null
- wk-write ("`t{0} server: mounted" -f $_server.name)
-
- # Add friendly description
- $_regex = "^{0}$" -f $_server.letter
- (gdr | Where-Object {$_.Name -imatch $_regex}).Description = $_server.name
- } catch {
- wk-warn ("`t{0} server: failed" -f $_server.name)
- }
- } else {
- wk-warn ("`t{0} server: timed-out" -f $_server.name)
- }
- }
-}
-function unmount-servers {
- # Unmount servers
- wk-write "Disconnecting from backup server(s)"
- $mounted_servers = @(gdr | Where-Object {$_.DisplayRoot -imatch '\\\\'})
- foreach ($_server in $mounted_servers) {
- try {
- $_drive = "{0}:" -f $_server.Name
- net use $_drive /delete | Out-Null
- #wk-warn ("`t{0} server: unmounted" -f $_server.name)
- wk-warn "`tServer: unmounted"
- } catch {
- #wk-warn ("`t{0} server: failed" -f $_server.name)
- wk-warn "`tServer: failed"
- }
- }
-}
-function menu-imaging {
- wk-write "Drive Imaging"
- wk-write ""
-
- ## WARNING
- wk-warn "WARNING: This section is experimental"
- pause
- ## WARNING
-
- # Service Order
- $menu_service_order += "Please enter the service order`r`n"
- do {
- clear
- $service_order = read-host -prompt $menu_service_order
- } until ($service_order -imatch '^\d[\w\-]+$')
-
- # Select Disk
- $disk = select-disk
- if (!($disk)) {
- # No drives detected or user aborted
- wk-warn "Drive Imaging aborted."
- wk-write ""
- pause "Press Enter to return to main menu... "
- return $false
- } elseif ($disk -imatch '^M$') {
- # User selected to return to the menu
- return
- }
- wk-write ""
-
- # Mount server(s)
- mount-servers
-
- # Select Server
- $server = select-server
- if (!($server)) {
- # No servers detected
- wk-warn "Drive Imaging aborted."
- wk-write ""
- pause "Press Enter to return to main menu... "
- return $false
- } elseif ($server -imatch '^M$') {
- # User selected to return to the menu
- return
- }
- wk-write ""
- wk-write ("Saving partition backups to: {0}" -f $server.Description)
- wk-write ""
-
- # Backup partitions
- $partitions = Get-Partition -DiskNumber $disk.DiskNumber
- foreach ($_p in $partitions) {
- $_v = Get-Volume -Partition $_p
-
- $_name = "{0}" -f $_p.PartitionNumber
- if ($_v -and $_v.FileSystemLabel -ne "") {
- $_name += "_{0}" -f $_v.FileSystemLabel
- } else {
- $_name += "_{0}" -f $_p.Type
- }
-
- $_imagepath = "{0}{1}" -f $server.Root, $service_order
- $_imagefile = "{0}{1}\{2}.wim" -f $server.Root, $service_order, $_name
-
- if ($_p.AccessPaths -ne $null) {
- $_capturepath = $_p.AccessPaths | Where-Object {$_ -imatch '^\w:\\$'}
-
- # Take image
- wk-write (" Imaging partition {0} --> `"{1}`"" -f $_p.PartitionNumber, $_imagefile)
- if (!(Test-Path "$_imagepath")) {
- mkdir "$_imagepath" | out-null
- }
- New-WindowsImage -ImagePath "$_imagefile" -CapturePath "$_capturepath" -Name "$_name" -CompressionType "fast" | out-null
-
- # Verify image
- ## Code borrowed from: https://stackoverflow/a/10262275
- $pinfo = New-Object System.Diagnostics.ProcessStartInfo
- $pinfo.FileName = "$WKPath\7z.exe"
- $pinfo.RedirectStandardError = $true
- $pinfo.RedirectStandardOutput = $true
- $pinfo.UseShellExecute = $false
- $pinfo.Arguments = 't "{0}"' -f $_imagefile
- $p = New-Object System.Diagnostics.Process
- $p.StartInfo = $pinfo
- $p.Start() | Out-Null
- write-host " Verifying . . . " -NoNewline
- $p.WaitForExit()
- if ($p.ExitCode -eq 0) {
- write-host "Complete." -foreground "green"
- } else {
- write-host "Failed." -foreground "red"
- }
- }
- }
-
- # Unmount server(s)
- unmount-servers
- pause "Press Enter to return to main menu... "
-}
-function menu-setup {
- wk-write "Windows Setup"
- wk-write ""
-
- # Check if any destination drives were detected
- $disks = Get-Disk | Where-Object {$_.Size -ne 0 -and $_.BusType -inotmatch 'USB'}
- if ($disks.count -eq 0) {
- wk-error "No suitable destination drives were detected."
- pause "Press Enter to return to main menu... " $True
- return $false
- }
-
- # Build windows menu
- $windows_versions = @(
- @{Name="Windows 7 Home Premium"; ImageFile="Win7"; Index=1},
- @{Name="Windows 7 Professional"; ImageFile="Win7"; Index=2},
- @{Name="Windows 7 Ultimate"; ImageFile="Win7"; Index=3},
- @{Name="Windows 8.1"; ImageFile="Win8"; Index=1},
- @{Name="Windows 8.1 Pro"; ImageFile="Win8"; Index=2},
- @{Name="Windows 10 Home"; ImageFile="Win10"; Index=1},
- @{Name="Windows 10 Pro"; ImageFile="Win10"; Index=2}
- )
- $menu_setup_windows = "Which version of Windows are we installing?`r`n`r`n"
- $valid_answers = @("M", "m")
- for ($i=0; $i -lt $windows_versions.length; $i++) {
- $valid_answers += ($i + 1)
- $menu_setup_windows += "{0}: {1}`r`n" -f ($i + 1), $windows_versions[$i].Name
- }
- $menu_setup_windows += "`r`n"
- $menu_setup_windows += "M: Main Menu`r`n"
- $menu_setup_windows += "`r`n"
- $menu_setup_windows += "Please make a selection`r`n"
-
- # Select Windows version
- do {
- clear
- $answer = read-host -prompt $menu_setup_windows
- } until ($valid_answers -contains $answer)
-
- if ($answer -imatch '^M$') {
- # Exit if requested
- return
- } else {
- $answer -= 1
- $dest_windows_version = $windows_versions[$answer]
- }
-
- # Build disk menu
- $menu_setup_disk = "To which drive are we installing {0}?`r`n`r`n" -f $dest_windows_version.Name
- $valid_answers = @("M", "m")
- foreach ($_ in $disks) {
- $valid_answers += $_.DiskNumber
- $menu_setup_disk += "{0}: {1:N0} Gb`t({2}) {3}`r`n" -f $_.DiskNumber, ($_.Size / 1GB), $_.PartitionStyle, $_.FriendlyName
- }
- $menu_setup_disk += "`r`n"
- $menu_setup_disk += "M: Main Menu`r`n"
- $menu_setup_disk += "`r`n"
- $menu_setup_disk += "Please make a selection`r`n"
-
- # Select disk
- do {
- clear
- $answer = read-host -prompt $menu_setup_disk
- } until ($valid_answers -contains $answer)
-
- if ($answer -imatch '^M$') {
- # Exit if requested
- return
- } else {
- # Double check before deleting data
- $dest_disk = $disks | Where-Object {$_.DiskNumber -eq $answer}
- wk-warn "All data will be deleted from the following drive:"
- wk-warn ("`t{0:N0} Gb`t({1}) {2}`r`n" -f ($dest_disk.Size / 1GB), $dest_disk.PartitionStyle, $dest_disk.FriendlyName)
- if (ask ("Proceed and install {0}?" -f $dest_windows_version.Name)) {
- wk-warn "SAFTEY CHECK:"
- wk-write (" Installing:`t{0}" -f $dest_windows_version.Name)
- wk-error (" And ERASING:`tDisk {0} - {1:N0} Gb`t({2}) {3}`r`n" -f $dest_disk.DiskNumber, ($dest_disk.Size / 1GB), $dest_disk.PartitionStyle, $dest_disk.FriendlyName)
- if (ask "Is this correct?") {
- ## WARNING
- wk-warn "WARNING: This section is experimental"
- ## WARNING
- ## Here be dragons
-
- try {
- # Select UEFI or BIOS
- if ($dest_windows_version.Name -match '^Windows 7') {
- if (ask "Setup drive using MBR (legacy) layout?") {
- format-mbr $dest_disk
- } else {
- format-gpt $dest_disk
- }
- } elseif ($dest_windows_version.Name -match '^Windows (8|10)') {
- if (ask "Setup drive using GPT (UEFI) layout?") {
- format-gpt $dest_disk
- } else {
- format-mbr $dest_disk
- }
- }
-
- # Apply image
- apply-image $dest_windows_version.ImageFile $dest_windows_version.Index
-
- # Create boot files (copies files for both Legacy and UEFI)
- bcdboot W:\Windows /s S: /f ALL
- if ($dest_windows_version.Name -match '^Windows (8|10)') {
- W:\Windows\System32\reagentc /setreimage /path T:\Recovery\WindowsRE /target W:\Windows
- }
-
- # Reboot
- wk-write "Windows Setup complete."
- wk-write ""
- return 0
- } catch {
- wk-error "$Error"
- wk-error "Windows Setup aborted."
- wk-write ""
- pause "Press Enter to return to main menu... "
- return $false
- }
- } else {
- wk-error "Windows Setup aborted."
- wk-write ""
- pause "Press Enter to return to main menu... "
- }
- } else {
- wk-error "Windows Setup aborted."
- wk-write ""
- pause "Press Enter to return to main menu... "
- }
- }
-}
function menu-tools {
- wk-write "Misc Tools"
- wk-write ""
- wk-warn "Be careful."
- Start-Process "$WKPath\explorer++" -argumentlist @("$WKPath")
- wk-exit
+ # Avail tools
+ $tools = @(
+ @{Name="Blue Screen View"; Folder="BlueScreenView"; File="BlueScreenView64.exe"},
+ @{Name="Explorer++"; Folder="Explorer++"; File="Explorer++64.exe"},
+ @{Name="Fast Copy"; Folder="FastCopy"; File="FastCopy64.exe"; Args=@('/cmd=noexist_only', '/utf8', '/skip_empty_dir', '/linkdest', '/exclude="desktop.ini;Thumbs.db"')},
+ @{Name="HW Monitor"; Folder="HWMonitor"; File="HWMonitor64.exe"},
+ @{Name="NT Password Editor"; Folder="NT Password Editor"; File="ntpwedit64.exe"},
+ @{Name="Notepad2"; Folder="Notepad2"; File="Notepad2-Mod64.exe"},
+ @{Name="Prime95"; Folder="Prime95"; File="prime95.exe"},
+ @{Name="PhotoRec (GUI)"; Folder="TestDisk"; File="qphotorec_win.exe"},
+ @{Name="PhotoRec (CLI)"; Folder="TestDisk"; File="photorec_win.exe"},
+ @{Name="TestDisk (CLI)"; Folder="TestDisk"; File="testdisk_win.exe"}
+ )
+
+ # Build menu
+ $selection = $null
+ $actions = @(@{Name="Main Menu"; Letter="M"})
+
+ # Run Loop
+ $_done = $false
+ do {
+ $selection = (menu-select "Tools Menu" $tools $actions)
+
+ if ($selection -imatch '^M$') {
+ # User selected to return to the menu
+ return $false
+ } elseif ($selection -inotmatch '^\d+$') {
+ # This shouldn't happen?
+ throw
+ } else {
+ $selection -= 1
+ $path = "{0}\{1}" -f $WKPath, $tools[$selection].Folder
+ if ($tools[$selection].ContainsKey("Args")) {
+ Start-Process $tools[$selection].File -ArgumentList $tools[$selection].Args -WorkingDirectory $path
+ } else {
+ Start-Process $tools[$selection].File -WorkingDirectory $path
+ }
+ }
+ } until ($_done)
}
function menu-main {
- $answered = $false
- $menu_main = @"
-WK WinPE Tools
-
-1: Drive Imaging
-2: Windows Setup
-3: Misc Tools
-
-Q: Quit
-R: Reboot
-S: Shutdown
-
-Please make a selection
-"@
-
- do {
- clear
- $answer = read-host -prompt $menu_main
- } until ($answer -imatch '^[123QRS]$')
- clear
-
- if ($answer.GetType().Name -match "String") {
- $answer = $answer.ToUpper()
- }
- switch ($answer) {
- 1 {menu-imaging; break}
- 2 {menu-setup; break}
- 3 {menu-tools; break}
- default {wk-exit}
+ # Build menu
+ $selection = $null
+ $menus = @(
+ @{Name="Drive Imaging"; Menu="menu-imaging"}
+ @{Name="Windows Setup"; Menu="menu-setup"}
+ @{Name="Misc Tools"; Menu="menu-tools"}
+ )
+ $actions = @(
+ @{Name="Command Prompt"; Letter="C"}
+ @{Name="PowerShell"; Letter="P"}
+ @{Name="Reboot"; Letter="R"}
+ @{Name="Shutdown"; Letter="S"}
+ )
+
+ # Show Menu
+ $selection = (menu-select "Main Menu" $menus $actions -SecretExit $true)
+
+ if ($selection -imatch '^C$') {
+ Start-Process "$windir\System32\cmd.exe" -argumentlist @("-new_console:n") -WorkingDirectory "$WKPath"
+ return
+ } elseif ($selection -imatch '^P$') {
+ Start-Process "$windir\System32\WindowsPowerShell\v1.0\powershell.exe" -argumentlist @("-ExecutionPolicy", "Bypass", "-new_console:n") -WorkingDirectory "$WKPath"
+ return
+ } elseif ($selection -imatch '^[QRS]$') {
+ wk-exit $selection
+ return
+ } elseif ($selection -inotmatch '^\d+$') {
+ # This shouldn't happen?
+ throw
+ } else {
+ # Launch sub-menu
+ $selection -= 1
+ & $menus[$selection].Menu
}
}
diff --git a/WK/Scripts/imaging.ps1 b/WK/Scripts/imaging.ps1
new file mode 100644
index 0000000..a1033dc
--- /dev/null
+++ b/WK/Scripts/imaging.ps1
@@ -0,0 +1,463 @@
+# WK imaging functions
+
+## Init ##
+$wd = $(Split-Path $MyInvocation.MyCommand.Path)
+pushd "$wd"
+. .\init.ps1
+
+# Functions
+function apply-image {
+ # Apply a Windows image to W:\
+ Param([string]$image, [string]$name)
+ $path = ""
+ $split_image = $false
+ $split_image_pattern = ""
+
+ # Check for local source
+ $volumes = @(Get-Volume | Where-Object {$_.Size -ne 0 -and $_.DriveLetter -imatch '^[C-Z]$'})
+ foreach ($v in $volumes) {
+ $letter = $v.DriveLetter + ":"
+ if (Test-Path "$letter\sources\$image.wim") {
+ $path = "$letter\sources\$image.wim"
+ } elseif (Test-Path "$letter\sources\$image.esd") {
+ $path = "$letter\sources\$image.wim"
+ } elseif (Test-Path "$letter\sources\$image.swm") {
+ $path = "$letter\sources\$image.swm"
+ $split_image = $true
+ $split_image_pattern = "$letter\sources\$image*.swm"
+ }
+ }
+
+ # Check for remote source (if necessary)
+ if ($path -imatch '^$') {
+ # Temporarily set path to network source
+ $path = "\\$source_server\Windows\$image"
+ wk-warn "Searching for network source"
+ if (Test-Path "$path.wim") {
+ $path = "$path.wim"
+ } elseif (Test-Path "$path.esd") {
+ $path = "$path.swm"
+ } elseif (Test-Path "$path.swm") {
+ $path = "$path.swm"
+ $split_image = $true
+ $split_image_pattern = "$path*.swm"
+ } else {
+ # Revert to empty path if nothing found.
+ $path = ""
+ }
+ }
+
+ # Expand Image
+ if ($path -imatch 'Win\d+\.(esd|wim)$') {
+ wk-write " Applying image..."
+ Expand-WindowsImage -ImagePath "$path" -Name "$name" -ApplyPath 'W:\' | out-null
+ } elseif ($path -imatch 'Win\d+\.swm$') {
+ wk-write " Applying split-image..."
+ Expand-WindowsImage -ImagePath "$path" -Name "$name" -ApplyPath 'W:\' -SplitImageFilePattern "$split_image_pattern" | out-null
+ } else {
+ wk-error "Image not found."
+ throw
+ }
+}
+function format-gpt {
+ Param($dest_disk)
+ wk-write "Drive will use a GPT (UEFI) layout."
+
+ # Double-check we have the right drive
+ ## I don't trust the order will be the same for diskpart & PS Storage Cmdlets
+ $_sel_uid = $dest_disk.Guid
+ if ($dest_disk.PartitionStyle -imatch "MBR") {
+ # MBR disks don't have GUIDs and use the signature in hex instead
+ $_sel_uid = "{0:x}" -f $dest_disk.Signature
+ }
+ $diskpart_script = "select disk {0}`r`n" -f $dest_disk.DiskNumber
+ $diskpart_script += "uniqueid disk"
+ Out-File -encoding 'UTF8' -filepath "$wd\diskpart.script" -inputobject $diskpart_script
+ Start-Process "diskpart" -argumentlist @("/s", "$wd\diskpart.script") -wait -nonewwindow -PassThru -RedirectStandardOutput "$wd\drive_uid" | Out-Null
+ if (!(Get-Content "$wd\drive_uid" | Where-Object {$_ -imatch $_sel_uid})) {
+ # GUIDs do not match
+ wk-error "Diskpart failed to select the same disk for formatting, aborting setup."
+ wk-warn "This system requires manual formatting & setup"
+ wk-write ""
+ throw "Failed to format disk"
+ } else {
+ wk-write ("Selecting Disk {0} ({1})" -f $dest_disk.DiskNumber, $_sel_uid)
+ }
+
+ # Generate Diskpart script and execute
+ ## NOTE 1: PS Storage Cmdlets can't be used; See Keith Garner's response here:
+ ## https://social.technet.microsoft.com/Forums/en-US/9d78da31-557f-4408-89e0-a1603f7ebe0d
+ ##
+ ## NOTE 2: This overwrites existing diskpart.script file without confirmation.
+ $diskpart_script = "select disk {0}`r`n" -f $dest_disk.DiskNumber
+ $diskpart_script += "clean`r`n"
+ $diskpart_script += "convert gpt`r`n"
+
+ # 1. Windows RE tools partition (Windows 8+)
+ if ($dest_windows_version.Name -imatch '^Windows (8|10)') {
+ $diskpart_script += "create partition primary size=300`r`n"
+ $diskpart_script += "format quick fs=ntfs label='Windows RE tools'`r`n"
+ $diskpart_script += "assign letter='T'`r`n"
+ $diskpart_script += "set id='de94bba4-06d1-4d40-a16a-bfd50179d6ac'`r`n"
+ $diskpart_script += "gpt attributes=0x8000000000000001`r`n"
+ }
+
+ # 2. System partition
+ $diskpart_script += "create partition efi size=260`r`n"
+ ## NOTE: Allows for Advanced Format 4Kn drives
+ $diskpart_script += "format quick fs=fat32 label='System'`r`n"
+ $diskpart_script += "assign letter='S'`r`n"
+
+ # 3. Microsoft Reserved (MSR) partition
+ $diskpart_script += "create partition msr size=128`r`n"
+
+ # 4. Windows partition
+ $diskpart_script += "create partition primary`r`n"
+ $diskpart_script += "format quick fs=ntfs label='Windows'`r`n"
+ $diskpart_script += "assign letter='W'`r`n"
+
+ # Run script
+ Out-File -encoding 'UTF8' -filepath "$wd\diskpart.script" -inputobject $diskpart_script
+ Start-Process "diskpart" -argumentlist @("/s", "$wd\diskpart.script") -wait -nonewwindow | out-null
+}
+function format-mbr {
+ Param($dest_disk)
+ wk-write "Drive will use a MBR (legacy) layout."
+
+ if ($dest_disk.PartitionStyle -inotmatch '^RAW$') {
+ # Only clean if necessary
+ clear-Disk $dest_disk.DiskNumber -RemoveData -RemoveOEM -Confirm:$false | out-null
+ }
+ Initialize-Disk $dest_disk.DiskNumber -PartitionStyle 'MBR' | out-null
+ New-Partition -DiskNumber $dest_disk.DiskNumber -Size 100Mb -DriveLetter 'S' -IsActive:$true | out-null
+ New-Partition -DiskNumber $dest_disk.DiskNumber -UseMaximumSize -DriveLetter 'W' -IsActive:$false | out-null
+ Format-Volume -DriveLetter 'S' -FileSystem 'NTFS' -NewFileSystemLabel 'System Reserved' | out-null
+ Format-Volume -DriveLetter 'W' -FileSystem 'NTFS' -NewFileSystemLabel 'Windows' | out-null
+}
+function select-disk {
+ param([string]$title, [bool]$skip_usb=$false)
+ $_skipped_parts = 0
+
+ # Get Disk(s)
+ if ($skip_usb) {
+ $disks = @(Get-Disk | Where-Object {$_.Size -ne 0 -and $_.BusType -inotmatch 'USB'} | Sort-Object -Property "Number")
+ } else {
+ $disks = @(Get-Disk | Where-Object {$_.Size -ne 0} | Sort-Object -Property "Number")
+ }
+
+ # Check if any drives were detected
+ if ($disks.count -eq 0) {
+ wk-error "No suitable drives were detected."
+ return $false
+ }
+
+ # Get selection
+ $selection = $null
+ $main_set = @()
+ if ($disks.count -eq 1) {
+ # Only one disk is available
+ $selection = $disks[0]
+ } else {
+ # Multiple options. Build and use menu
+ foreach ($_ in $disks) {
+ $_entry = "{0}`t[{1}] ({2}) {3}" -f (human-size $_.Size 0), $_.PartitionStyle, $_.BusType, $_.FriendlyName
+ $main_set += @{Name=$_entry}
+ }
+ $actions = @(@{Name="Main Menu"; Letter="M"})
+ $selection = (menu-select $title $main_set $actions)
+ }
+
+ if ($selection -imatch '^\d+$') {
+ $selection -= 1
+ return $disks[$selection]
+ } else {
+ return $selection
+ }
+}
+function menu-imaging {
+ wk-write "Drive Imaging"
+ wk-write ""
+
+ ## WARNING
+ wk-warn "WARNING: This section is experimental"
+ pause
+ ## WARNING
+
+ # Service Order
+ $menu_service_order += "Please enter the service order`r`n"
+ do {
+ clear
+ $service_order = read-host -prompt $menu_service_order
+ } until ($service_order -imatch '^\d[\w\-]+$')
+
+ # Select Disk
+ $disk = (select-disk "For which drive are we creating backup image(s)?")
+
+ if (!($disk)) {
+ # No drives detected or user aborted
+ wk-warn "Drive Imaging aborted."
+ wk-write ""
+ pause "Press Enter to return to main menu... " -warning=$true
+ return $false
+ } elseif ($disk -imatch '^M$') {
+ # User selected to return to the menu
+ return $false
+ } elseif ($disk.DiskNumber -imatch '^\d+$') {
+ # Valid disk selected
+ clear
+ wk-write ("Disk:`t{0}`t[{1}] ({2}) {3}" -f (human-size $disk.Size 0), $disk.PartitionStyle, $disk.BusType, $disk.FriendlyName)
+ wk-write "Partition(s):"
+
+ # Print partition info
+ $partitions = Get-Partition -DiskNumber $disk.DiskNumber
+ $_skipped_parts = 0
+ foreach ($_p in $partitions) {
+ # Assign letter
+ Add-PartitionAccessPath -DiskNumber $disk.DiskNumber -PartitionNumber $_p.PartitionNumber -AssignDriveLetter 2>&1 | Out-Null
+
+ # Update partition info
+ $_p = Get-Partition -DiskNumber $disk.DiskNumber -PartitionNumber $_p.PartitionNumber
+ $_v = Get-Volume -Partition $_p
+
+ # Set size label
+ $_size = (human-size $_p.size 0)
+ $_used = ""
+ if ($_v) {
+ $_used = "({0} used)" -f (human-size ($_v.Size - $_v.SizeRemaining) 0)
+ }
+
+ # Print partition info
+ if ($_p.AccessPaths) {
+ # Has drive letter
+ $_path = $_p.AccessPaths | Where-Object {$_ -imatch '^\w:\\$'}
+ $_label = " {0}" -f $_p.Type
+ if ($_v -and $_v.FileSystemLabel -ne "") {
+ $_label = '"{0}"' -f $_v.FileSystemLabel
+ }
+ $_msg = " {0:N0}:`t{1} ({2,6}) {3} {4}" -f $_p.PartitionNumber, $_path, $_size, $_label, $_used
+ wk-write "$_msg"
+ } else {
+ # No drive letter
+ $_msg = " *{0:N0}:`t ({1}) {2}" -f $_p.PartitionNumber, $_size, $_p.Type
+ wk-error "$_msg"
+ $_skipped_parts += 1
+ }
+ }
+ if ($_skipped_parts -gt 0) {
+ wk-warn " *`tUnable to backup these partition(s)"
+ }
+ wk-write ""
+ if (!(ask " Backup these partition(s)?")) {
+ wk-warn "Drive Imaging aborted."
+ wk-write ""
+ pause "Press Enter to return to main menu... " -warning=$true
+ return $false
+ }
+ }
+ wk-write ""
+
+ # Mount server(s)
+ mount-servers
+
+ # Select Server
+ $server = (select-server)
+ if (!($server)) {
+ # No servers detected
+ wk-warn "Drive Imaging aborted."
+ wk-write ""
+ pause "Press Enter to return to main menu... " -warning=$true
+ return $false
+ } elseif ($server -imatch '^M$') {
+ # User selected to return to the menu
+ return
+ }
+ wk-write ""
+ wk-write ("Saving partition backups to: {0}" -f $server.Description)
+ wk-write ""
+
+ # Backup partitions
+ $partitions = Get-Partition -DiskNumber $disk.DiskNumber
+ foreach ($_p in $partitions) {
+ $_v = Get-Volume -Partition $_p
+
+ $_name = "{0}" -f $_p.PartitionNumber
+ if ($_v -and $_v.FileSystemLabel -ne "") {
+ $_name += "_{0}" -f $_v.FileSystemLabel
+ } else {
+ $_name += "_{0}" -f $_p.Type
+ }
+ # Sanitize the name
+ $_name = $_name -replace '\s', '_'
+
+ $_imagepath = "{0}{1}" -f $server.Root, $service_order
+ $_imagefile = "{0}{1}\{2}.wim" -f $server.Root, $service_order, $_name
+
+ if ($_p.AccessPaths -ne $null) {
+ # Avoid unwanted clobbering
+ if (Test-Path "$_imagefile") {
+ if (!(ask ("Overwrite backup image: {0}" -f $_imagefile))) {
+ wk-warn "Drive Imaging aborted."
+ wk-write ""
+ pause "Press Enter to return to main menu... " -warning=$true
+ return $false
+ }
+ }
+ $_capturedir = $_p.AccessPaths | Where-Object {$_ -imatch '^\w:\\$'}
+
+ # Take image
+ wk-write (" Imaging partition {0} --> `"{1}`"" -f $_p.PartitionNumber, $_imagefile)
+ if (!(Test-Path "$_imagepath")) {
+ mkdir "$_imagepath" | out-null
+ }
+ $_dism_args = @(
+ '/Capture-Image',
+ '/ImageFile:$_imagefile',
+ '/CaptureDir:$_capturedir',
+ '/Name:$_name',
+ '/Compress:fast',
+ '/Quiet')
+ Start-Process "$windir\System32\Dism.exe" -ArgumentList $_dism_args -NoNewWindow -Wait | out-null
+
+ ## The following command fails to capture OS partitions consitantly. Until this is fixed I will use DISM directly (as above).
+ #New-WindowsImage -ImagePath "$_imagefile" -CapturePath "$_capturedir" -Name "$_name" -CompressionType "fast" | out-null
+
+ # Verify image
+ ## Code borrowed from: https://stackoverflow/a/10262275
+ $pinfo = New-Object System.Diagnostics.ProcessStartInfo
+ $pinfo.FileName = "$WKPath\7-Zip\7z.exe"
+ $pinfo.RedirectStandardError = $true
+ $pinfo.RedirectStandardOutput = $true
+ $pinfo.UseShellExecute = $false
+ $pinfo.Arguments = 't "{0}"' -f $_imagefile
+ $p = New-Object System.Diagnostics.Process
+ $p.StartInfo = $pinfo
+ $p.Start() | Out-Null
+ write-host " Verifying . . . " -NoNewline
+ $p.WaitForExit()
+ if ($p.ExitCode -eq 0) {
+ write-host "Complete." -foreground "green"
+ } else {
+ write-host "Failed." -foreground "red"
+ }
+ }
+ }
+
+ # Unmount server(s)
+ unmount-servers
+ pause "Press Enter to return to main menu... "
+}
+function menu-setup {
+ # Select Disk
+ $dest_disk = (select-disk "To which drive are we installing Windows?" -skip_usb=$true)
+
+ if (!($dest_disk)) {
+ # No drives detected or user aborted
+ wk-warn "Windows Setup aborted."
+ wk-write ""
+ pause "Press Enter to return to main menu... " -warning=$true
+ return $false
+ } elseif ($dest_disk -imatch '^M$') {
+ # User selected to return to the menu
+ return $false
+ } elseif ($dest_disk.DiskNumber -inotmatch '^\d+$') {
+ # This shouldn't happen?
+ throw
+ } else {
+ wk-warn "All data will be deleted from the following drive:"
+ wk-warn ("`t{0}`t({1}) {2}`r`n" -f (human-size $dest_disk.Size 0), $dest_disk.PartitionStyle, $dest_disk.FriendlyName)
+ if (!(ask "Proceed and install Windows?")) {
+ wk-warn "Windows Setup aborted."
+ wk-write ""
+ pause "Press Enter to return to main menu... " -warning=$true
+ return $false
+ }
+ }
+
+ # Set available Windows versions
+ $windows_versions = @(
+ @{Name="Windows 7 Home Basic"; ImageFile="Win7"; ImageName="Windows 7 HOMEBASIC"}
+ @{Name="Windows 7 Home Premium"; ImageFile="Win7"; ImageName="Windows 7 HOMEPREMIUM"}
+ @{Name="Windows 7 Professional"; ImageFile="Win7"; ImageName="Windows 7 PROFESSIONAL"}
+ @{Name="Windows 7 Ultimate"; ImageFile="Win7"; ImageName="Windows 7 ULTIMATE"}
+
+ @{Name="Windows 8.1"; ImageFile="Win8"; ImageName="Windows 8.1"; CRLF=$true}
+ @{Name="Windows 8.1 Pro"; ImageFile="Win8"; ImageName="Windows 8.1 Pro"}
+
+ # The ISOs from the MediaCreationTool are apparently Technical Previews
+ @{Name="Windows 10 Home"; ImageFile="Win10"; ImageName="Windows 10 Technical Preview"; CRLF=$true}
+ @{Name="Windows 10 Pro"; ImageFile="Win10"; ImageName="Windows 10 Pro Technical Preview"}
+ )
+
+ # Build menu and get selection
+ $dest_windows_version = $null
+ $selection = $null
+ $actions = @(@{Name="Main Menu"; Letter="M"})
+ $selection = (menu-select "Which version of Windows are we installing?" $windows_versions $actions)
+
+ if ($selection -imatch '^M$') {
+ # User selected to return to the menu
+ return $false
+ } elseif ($selection -inotmatch '^\d+$') {
+ # This shouldn't happen?
+ throw
+ } else {
+ $selection -= 1
+ $dest_windows_version = $windows_versions[$selection]
+ }
+
+ # Double check before deleting data
+ wk-warn "SAFTEY CHECK:"
+ wk-write (" Installing:`t{0}" -f $dest_windows_version.Name)
+ wk-error (" And ERASING:`tDisk: {0}`t({1}) {2}`r`n" -f (human-size $dest_disk.Size 0), $dest_disk.PartitionStyle, $dest_disk.FriendlyName)
+ if (!(ask "Is this correct?")) {
+ wk-warn "Windows Setup aborted."
+ wk-write ""
+ pause "Press Enter to return to main menu... " -warning=$true
+ return $false
+ }
+
+ ## WARNING
+ wk-warn "WARNING: This section is experimental"
+ ## WARNING
+
+ ## Here be dragons
+ try {
+ # Select UEFI or BIOS partition layout
+ if ($UEFI) {
+ # System booted via UEFI so assume new layout should be GPT
+ if (ask "Setup drive using GPT (UEFI) layout?") {
+ format-gpt $dest_disk
+ } else {
+ format-mbr $dest_disk
+ }
+ } else{
+ if (ask "Setup drive using MBR (legacy) layout?") {
+ format-mbr $dest_disk
+ } else {
+ format-gpt $dest_disk
+ }
+ }
+
+ # Apply image
+ apply-image $dest_windows_version.ImageFile $dest_windows_version.ImageName
+
+ # Create boot files (copies files for both Legacy and UEFI)
+ wk-write " Copying boot files..."
+ bcdboot W:\Windows /s S: /f ALL 2>&1 | out-null
+ if ($dest_windows_version.Name -imatch '^Windows (8|10)') {
+ W:\Windows\System32\reagentc /setreimage /path T:\Recovery\WindowsRE /target W:\Windows 2>&1 | out-null
+ }
+
+ # Done
+ wk-write "Windows Setup complete."
+ wk-write ""
+ pause "Press Enter to return to main menu... "
+ } catch {
+ # Error(s)
+ wk-error "$Error"
+ wk-error "Windows Setup aborted."
+ wk-write ""
+ pause "Press Enter to return to main menu... " -warning=$true
+ return $false
+ }
+}
diff --git a/WK/Scripts/init.ps1 b/WK/Scripts/init.ps1
index 009b756..b198fdd 100644
--- a/WK/Scripts/init.ps1
+++ b/WK/Scripts/init.ps1
@@ -4,9 +4,26 @@
$host.UI.RawUI.BackgroundColor = "black"
$host.UI.RawUI.ForegroundColor = "cyan"
+#$appdata = (gci env:appdata).value
+#$localappdata = (gci env:localappdata).value
+#$username = (gci env:username).value
+#$userprofile = (gci env:userprofile).value
$systemdrive = (gci env:systemdrive).value
+$windir = (gci env:windir).value
+#$programfiles = (gci env:programfiles).value
+#$programfiles86 = $programfiles
+#if (test-path env:"programfiles(x86)") {
+# $programfiles86 = (gci env:"programfiles(x86)").value
+#}
$WKPath = "$systemdrive\WK"
$date = get-date -uformat "%Y-%m-%d"
+#$logpath = "$WKPath\Info\$date"
+
+# Check if booted via UEFI
+$UEFI = $false
+if ((Get-ItemProperty -path "HKLM:\System\CurrentControlSet\Control").PEFirmwareType -eq 2) {
+ $UEFI = $true
+}
function ask {
param([string]$text = "Kotaero", [string]$log = "WK.log")
@@ -56,6 +73,80 @@ function human-size {
}
return $size
}
+function menu-select {
+ ## $MainEntries should be an "AoH" object (with at least the key "Name" for each item)
+ ## NOTE: if the CRLF=$true; then a spacer is added before that entry.
+ ## Example:
+ ## $MainEntries = @(
+ ## @{Name="Windows 10 Home"; ImageFile="Win10"; ImageName="Windows 10 Home"}
+ ## @{Name="Windows 10 Pro"; ImageFile="Win10"; ImageName="Windows 10 Pro"}
+ ##)
+
+ ## $ActionEntries should be an "AoH" object (with at least the keys "Name" and "Letter" for each item)
+ ## NOTE: if the CRLF=$true; then a spacer is added before that entry.
+ ## Example:
+ ## $ActionEntries = @(
+ ## @{Name="Reboot"; Letter="R"}
+ ## @{Name="Shutdown"; Letter="S"}
+ ##)
+
+ param(
+ [string]$Title = "## Untitled Menu ##",
+ $MainEntries = @(),
+ $ActionEntries = @(),
+ [string]$Prompt = "Please make a selection",
+ [bool]$SecretExit = $false
+ )
+
+ # Bail early if no items given
+ if ($MainEntries.length -eq 0 -and $ActionEntries.length -eq 0) {
+ throw "MenuError: No items given."
+ }
+
+ # Build menu
+ $menu_splash = "{0}`r`n`r`n" -f $title
+ $valid_answers = @()
+ if ($SecretExit) {
+ $valid_answers += "Q"
+ }
+
+ # Add main items to splash
+ if ($MainEntries.length -gt 0) {
+ for ($i=0; $i -lt $MainEntries.length; $i++) {
+ if ($MainEntries[$i].CRLF) {
+ # Add spacer
+ $menu_splash += "`r`n"
+ }
+ $valid_answers += ($i + 1)
+ $menu_splash += "{0,2:N0}: {1}`r`n" -f ($i + 1), $MainEntries[$i].Name
+ }
+ $menu_splash += "`r`n"
+ }
+
+ # Add action items to splash
+ if ($ActionEntries.length -gt 0) {
+ foreach ($_item in $ActionEntries) {
+ if ($_item.CRLF) {
+ # Add spacer
+ $menu_splash += "`r`n"
+ }
+ $menu_splash += " {0}: {1}`r`n" -f $_item.Letter.ToUpper(), $_item.Name
+ $valid_answers += $_item.Letter.ToLower(), $_item.Letter.ToUpper()
+ }
+ $menu_splash += "`r`n"
+ }
+
+ # Add prompt to splash
+ $menu_splash += "{0}`r`n" -f $prompt
+
+ # Select Windows version
+ do {
+ clear
+ $answer = read-host -prompt $menu_splash
+ } until ($valid_answers -contains $answer)
+
+ return $answer.ToUpper()
+}
function pause {
param([string]$message = "Press Enter to continue... ", [bool]$warning = $False)
if ($warning) {
diff --git a/WK/Scripts/servers.ps1 b/WK/Scripts/servers.ps1
new file mode 100644
index 0000000..2d47e19
--- /dev/null
+++ b/WK/Scripts/servers.ps1
@@ -0,0 +1,85 @@
+# WK server functions
+
+## Init ##
+$wd = $(Split-Path $MyInvocation.MyCommand.Path)
+pushd "$wd"
+. .\init.ps1
+
+# Variables
+$source_server = "10.0.0.10"
+$backup_servers = @(
+ @{ "ip"="10.0.0.10";
+ "letter"="Z";
+ "name"="ServerOne";
+ "path"="Backups"},
+ @{ "ip"="10.0.0.11";
+ "name"="ServerTwo";
+ "letter"="Y";
+ "path"="Backups"}
+ )
+$backup_user = "backup"
+$backup_pass = "Abracadabra"
+
+# Functions
+function select-server {
+ # Check for available servers
+ $avail_servers = @(Get-PSDrive | Where-Object {$_.DisplayRoot -imatch '\\\\'})
+ if ($avail_servers.count -eq 0) {
+ wk-error "No suitable backup servers were detected."
+ return $false
+ }
+
+ # Build menu and get selection
+ $selection = $null
+ $main_set = @()
+ foreach ($server in $avail_servers) {
+ $_entry = "{0} ({1} free)" -f $server.Description, (human-size $server.Free)
+ $main_set += @{Name=$_entry}
+ }
+ $actions = @(@{Name="Main Menu"; Letter="M"})
+ $selection = (menu-select "Where are we saving the backup image(s)?" $main_set $actions)
+
+ if ($selection -imatch '^\d+$') {
+ $selection -= 1
+ return $avail_servers[$selection]
+ }
+ return $selection
+}
+function mount-servers {
+ # Mount servers
+ wk-write "Connecting to backup server(s)"
+ foreach ($_server in $backup_servers) {
+ if (test-connection $_server.ip -count 3 -quiet) {
+ try {
+ $_path = "\\{0}\{1}" -f $_server.ip, $_server.path
+ $_drive = "{0}:" -f $_server.letter
+ net use $_drive "$_path" /user:$backup_user $backup_pass | Out-Null
+ wk-write ("`t{0} server: mounted" -f $_server.name)
+
+ # Add friendly description
+ $_regex = "^{0}$" -f $_server.letter
+ (Get-PSDrive | Where-Object {$_.Name -imatch $_regex}).Description = $_server.name
+ } catch {
+ wk-warn ("`t{0} server: failed" -f $_server.name)
+ }
+ } else {
+ wk-warn ("`t{0} server: timed-out" -f $_server.name)
+ }
+ }
+}
+function unmount-servers {
+ # Unmount servers
+ wk-write "Disconnecting from backup server(s)"
+ $mounted_servers = @(Get-PSDrive | Where-Object {$_.DisplayRoot -imatch '\\\\'})
+ foreach ($_server in $mounted_servers) {
+ try {
+ $_drive = "{0}:" -f $_server.Name
+ net use $_drive /delete | Out-Null
+ #wk-warn ("`t{0} server: unmounted" -f $_server.name)
+ wk-warn "`tServer: unmounted"
+ } catch {
+ #wk-warn ("`t{0} server: failed" -f $_server.name)
+ wk-warn "`tServer: failed"
+ }
+ }
+}
diff --git a/make-cd.cmd b/make-cd.cmd
index 885ba63..2f7bf21 100644
--- a/make-cd.cmd
+++ b/make-cd.cmd
@@ -5,6 +5,8 @@ setlocal EnableDelayedExpansion
title WinPE 10 creation tool
color 1b
pushd %~dp0
+set "wd=%cd%"
+set "pe_iso=WinPE-2016-02d.iso"
:Flags
for %%f in (%*) do (
@@ -12,8 +14,9 @@ for %%f in (%*) do (
)
:CreateISO
-del winpe10-test.iso
-makewinpemedia.cmd /iso pe_files winpe10-test.iso
+del "!pe_iso!"
+makewinpemedia.cmd /iso "%wd%\pe_files" "!pe_iso!"
+goto Done
:Abort
echo.
diff --git a/make.cmd b/make.cmd
index 01dc542..5868691 100644
--- a/make.cmd
+++ b/make.cmd
@@ -7,7 +7,7 @@ color 1b
pushd %~dp0
set "wd=%cd%"
set "winpe_ocs=%programfiles(x86)%\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs"
-
+set "pe_iso=WinPE-2016-02d.iso"
:Flags
for %%f in (%*) do (
@@ -62,29 +62,38 @@ dism /add-package /image:"%wd%\mount" /packagepath:"%winpe_ocs%\en-us\WinPE-Enha
del "%wd%\WK\Scripts\WK.log"
mkdir "%wd%\mount\WK"
robocopy /e "%wd%\WK" "%wd%\mount\WK"
-mklink "%wd%\mount\System32\explorer.exe" "%wd%\mount\WK\Explorer++.exe"
+del "%wd%\mount\Windows\explorer.exe"
+mklink /h "%wd%\mount\Windows\explorer.exe" "%wd%\mount\WK\Explorer++\Explorer++64.exe"
-:MenuLauncher
+:System32Stuff
copy /y "%wd%\System32\menu.cmd" "%wd%\mount\Windows\System32\menu.cmd"
+copy /y "%wd%\System32\Winpeshl.ini" "%wd%\mount\Windows\System32\Winpeshl.ini"
-:ReplaceStartnet
-copy /y "%wd%\System32\startnet.cmd" "%wd%\mount\Windows\System32\startnet.cmd"
-
-:ReplaceNotepad
+:RegistryEdits
reg load HKLM\WinPE-SW mount\Windows\System32\config\SOFTWARE
-reg add "HKLM\WinPE-SW\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /v Debugger /t REG_SZ /d "X:\WK\Notepad2.exe /z" /f
+reg load HKLM\WinPE-SYS mount\Windows\System32\config\SYSTEM
+
+rem Add 7-Zip to path
+reg add "HKLM\WinPE-SYS\ControlSet001\Control\Session Manager\Environment" /v Path /t REG_EXPAND_SZ /d "%%SystemRoot%%\system32;%%SystemRoot%%;%%SystemRoot%%\System32\Wbem;%%SYSTEMROOT%%\System32\WindowsPowerShell\v1.0\;%%SystemDrive%%\WK\7-Zip" /f
+
+rem Replace Notepad
+reg add "HKLM\WinPE-SW\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /v Debugger /t REG_SZ /d "X:\WK\Notepad2\Notepad2-Mod64.exe /z" /f
+
+rem Unload registry hives
reg unload HKLM\WinPE-SW
+reg unload HKLM\WinPE-SYS
:Background
takeown /f "%wd%\mount\Windows\System32\winpe.jpg" /a
icacls "%wd%\mount\Windows\System32\winpe.jpg" /grant administrators:F
copy /y "%wd%\System32\winpe.jpg" "%wd%\mount\Windows\System32\winpe.jpg"
+copy /y "%wd%\System32\winpe.jpg" "%wd%\mount\WK\ConEmu\winpe.jpg"
:ManualStuff
-echo Now is the time to add stuff (optional).
-echo.
-echo Press any key to commit changes...
-pause>nul
+REM echo Now is the time to add stuff (optional).
+REM echo.
+REM echo Press any key to commit changes...
+REM pause>nul
:Set-ScratchSpace
rem Force RamDisk size to try and avoid capture-image errors
@@ -94,8 +103,8 @@ dism /image:"%wd%\mount" /set-scratchspace:512
dism /unmount-image /mountdir:"%wd%\mount" /commit
:CreateISO
-del winpe10-2016.iso
-makewinpemedia.cmd /iso "%wd%\pe_files" winpe10-2016.iso
+del "!pe_iso!"
+makewinpemedia.cmd /iso "%wd%\pe_files" "!pe_iso!"
goto Done
:Abort