456 lines
16 KiB
PowerShell
456 lines
16 KiB
PowerShell
# WK-Checklist
|
|
|
|
## Init ##
|
|
$wd = $(Split-Path $MyInvocation.MyCommand.Path)
|
|
pushd "$wd"
|
|
. .\init.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 = "WORKGROUP\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 {$_.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 "diskpart" -argumentlist @("/s", "$wd\diskpart.script") -wait -nonewwindow -PassThru -RedirectStandardOutput "$wd\drive_uid" | out-null
|
|
if (!(Get-Content "$wd\drive_uid" | where {$_ -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 "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 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
|
|
}
|
|
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 1 -quiet 2>&1 | out-null) {
|
|
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 {$_.Name -imatch $_regex}).Description = $_server.name
|
|
} catch {
|
|
wk-warn ("`t{0} server: failed" -f $_server.name)
|
|
}
|
|
} else {
|
|
wk-warn ("`t{0} server: unreachable" -f $_server.name)
|
|
}
|
|
}
|
|
}
|
|
function unmount-servers {
|
|
# Unmount servers
|
|
wk-write "Disconnecting from backup server(s)"
|
|
$mounted_servers = @(gdr | where {$_.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)
|
|
} catch {
|
|
wk-warn ("`t{0} server: failed" -f $_server.name)
|
|
}
|
|
}
|
|
}
|
|
function menu-imaging {
|
|
wk-write "Drive Imaging"
|
|
wk-write ""
|
|
|
|
## WARNING
|
|
wk-warn "WARNING: This section is experimental"
|
|
pause
|
|
## WARNING
|
|
|
|
# Check if any source drives were detected
|
|
$disks = get-disk | where {$_.Size -ne 0}
|
|
# if ($disks.count -eq 0) {
|
|
# wk-error "No suitable source drives were detected."
|
|
# pause "Press Enter to return to main menu... " $True
|
|
# return 1
|
|
# }
|
|
|
|
# Mount server(s)
|
|
mount-servers
|
|
|
|
# Build server menu
|
|
$avail_servers = @(gdr | where {$_.DisplayRoot -imatch '\\\\'})
|
|
# if ($avail_servers.count -eq 0) {
|
|
# wk-error "No suitable backup servers were detected."
|
|
# pause "Press Enter to return to main menu... " $True
|
|
# return 1
|
|
# }
|
|
$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 {
|
|
##testing## clear
|
|
$answer = read-host -prompt $menu_imaging_server
|
|
} until ($valid_answers -contains $answer)
|
|
if ($answer -imatch '^M$') {
|
|
# Exit if requested
|
|
unmount-servers
|
|
return
|
|
} else {
|
|
$answer -= 1
|
|
$dest_backup_server = $avail_servers[$answer]
|
|
}
|
|
|
|
# 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:N0} Gb`t({2}) {3} ({4})`r`n" -f $_.DiskNumber, ($_.Size / 1GB), $_.BusType, $_.FriendlyName, $_.PartitionStyle
|
|
}
|
|
$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 {
|
|
##testing## clear
|
|
$answer = read-host -prompt $menu_imaging_source
|
|
} until ($valid_answers -contains $answer)
|
|
|
|
if ($answer -imatch '^M$') {
|
|
# Exit if requested
|
|
return
|
|
} else {
|
|
##TODO##
|
|
get-volume
|
|
}
|
|
|
|
# Service Order
|
|
$menu_service_order += "Please enter the service order`r`n"
|
|
do {
|
|
##testing## clear
|
|
$service_order = read-host -prompt $menu_service_order
|
|
} until ($service_order -imatch '^\d[\w\-]+$')
|
|
|
|
# Backup partitions
|
|
wk-write ("Saving partition backups to: {0}" -f $dest_backup_server.Description)
|
|
wk-write ("`t{0}{1}" -f $dest_backup_server.Root, $service_order)
|
|
|
|
# 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 {$_.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 1
|
|
}
|
|
|
|
# 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 {
|
|
##testing## 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 {
|
|
##testing## 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 {$_.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 1
|
|
}
|
|
} 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 "$WKPath\explorer++" -argumentlist @("$WKPath")
|
|
wk-exit
|
|
}
|
|
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 {
|
|
##testing## clear
|
|
$answer = read-host -prompt $menu_main
|
|
} until ($answer -imatch '^[123QRS]$')
|
|
##testing## 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}
|
|
}
|
|
}
|
|
|
|
# Main Loop
|
|
do {
|
|
menu-main
|
|
} while ($true)
|