diff --git a/LICENSE.txt b/LICENSE.txt index a401a44..3d52e4c 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2015 Alan Mason +Copyright (c) 2016 Alan Mason Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/System32/menu.cmd b/System32/menu.cmd index ca82607..2de9884 100644 --- a/System32/menu.cmd +++ b/System32/menu.cmd @@ -3,7 +3,7 @@ :Init setlocal EnableDelayedExpansion title Menu Launcher -color 0a +color 0b pushd %~dp0 :Flags @@ -30,5 +30,7 @@ echo. rem echo Press any key to exit... rem pause>nul popd -color -endlocal \ No newline at end of file +endlocal +cls +echo Careful now... +echo. \ No newline at end of file diff --git a/System32/startnet.cmd b/System32/startnet.cmd index c97c37b..45186f0 100644 --- a/System32/startnet.cmd +++ b/System32/startnet.cmd @@ -1,5 +1,6 @@ @echo off +color 0b echo Initializing... wpeinit wpeutil updatebootinfo diff --git a/WK/Scripts/WK.ps1 b/WK/Scripts/WK.ps1 index 9fc55b6..e063361 100644 --- a/WK/Scripts/WK.ps1 +++ b/WK/Scripts/WK.ps1 @@ -7,7 +7,7 @@ pushd "$wd" clear $host.UI.RawUI.WindowTitle = "WK PE Tool" $logpath = "$WKPath\Info\$date" -md "$logpath" 2>&1 | out-null +md "$logpath" 2>&1 | Out-Null $log = "$logpath\winpe.log" $source_server = "10.0.0.10" $backup_servers = @( @@ -20,23 +20,23 @@ $backup_servers = @( "letter"="Y"; "path"="Backups"} ) -$backup_user = "WORKGROUP\backup" +$backup_user = "backup" $backup_pass = "Abracadabra" # Functions function apply-image { - param([string]$image, [int]$index) + 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]$'} + $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") { + if (Test-Path "$letter\sources\$image.wim") { $path = "$letter\sources\$image.wim" - } elseif (test-path "$letter\sources\$image.swm") { + } elseif (Test-Path "$letter\sources\$image.swm") { $path = "$letter\sources\$image.swm" $split_image = $true $split_image_pattern = "$letter\sources\$image*.swm" @@ -46,9 +46,9 @@ function apply-image { # Check for remote source (if necessary) if ($path -match '^$') { net use z: "\\$source_server\Windows" /user:guest notarealpassword - if (test-path "Z:\$image.wim") { + if (Test-Path "Z:\$image.wim") { $path = "Z:\$image.wim" - } elseif (test-path "Z:\$image.swm") { + } elseif (Test-Path "Z:\$image.swm") { $path = "Z:\$image.swm" } } @@ -65,7 +65,7 @@ function apply-image { net use z: /delete } function format-gpt { - param($dest_disk) + Param($dest_disk) wk-write "Drive will use a GPT (UEFI) layout." # Double-check we have the right drive @@ -77,9 +77,9 @@ function format-gpt { } $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})) { + 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" @@ -122,16 +122,16 @@ function format-gpt { $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 + 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) + 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 + 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 @@ -139,6 +139,120 @@ function format-mbr { 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') { @@ -154,35 +268,37 @@ 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) { + 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 + 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 + (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: unreachable" -f $_server.name) + 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 {$_.DisplayRoot -imatch '\\\\'}) + $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) + 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 ("`t{0} server: failed" -f $_server.name) + wk-warn "`tServer: failed" } } } @@ -194,86 +310,92 @@ function menu-imaging { 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 -# } + + # 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 - # 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 + # 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 - } 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\-]+$') + wk-write "" + wk-write ("Saving partition backups to: {0}" -f $server.Description) + wk-write "" # 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) + $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 @@ -284,11 +406,11 @@ function menu-setup { wk-write "" # Check if any destination drives were detected - $disks = get-disk | where {$_.Size -ne 0 -and $_.BusType -inotmatch 'USB'} + $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 1 + return $false } # Build windows menu @@ -314,7 +436,7 @@ function menu-setup { # Select Windows version do { -##testing## clear + clear $answer = read-host -prompt $menu_setup_windows } until ($valid_answers -contains $answer) @@ -340,7 +462,7 @@ function menu-setup { # Select disk do { -##testing## clear + clear $answer = read-host -prompt $menu_setup_disk } until ($valid_answers -contains $answer) @@ -349,7 +471,7 @@ function menu-setup { return } else { # Double check before deleting data - $dest_disk = $disks | where {$_.DiskNumber -eq $answer} + $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)) { @@ -396,7 +518,7 @@ function menu-setup { wk-error "Windows Setup aborted." wk-write "" pause "Press Enter to return to main menu... " - return 1 + return $false } } else { wk-error "Windows Setup aborted." @@ -414,7 +536,7 @@ function menu-tools { wk-write "Misc Tools" wk-write "" wk-warn "Be careful." - start "$WKPath\explorer++" -argumentlist @("$WKPath") + Start-Process "$WKPath\explorer++" -argumentlist @("$WKPath") wk-exit } function menu-main { @@ -434,10 +556,10 @@ Please make a selection "@ do { -##testing## clear + clear $answer = read-host -prompt $menu_main } until ($answer -imatch '^[123QRS]$') -##testing## clear + clear if ($answer.GetType().Name -match "String") { $answer = $answer.ToUpper() diff --git a/WK/Scripts/init.ps1 b/WK/Scripts/init.ps1 index e006543..009b756 100644 --- a/WK/Scripts/init.ps1 +++ b/WK/Scripts/init.ps1 @@ -41,6 +41,21 @@ function wk-write { write-host ($text) out-file -filepath $log -inputobject $text -append } +function human-size { + param($bytes, [int]$decimals = 2) + if ($bytes -gt 1Tb) { + $size = "{0:N$decimals} Tb" -f ($bytes / 1Tb) + } elseif ($bytes -gt 1Gb) { + $size = "{0:N$decimals} Gb" -f ($bytes / 1Gb) + } elseif ($bytes -gt 1Mb) { + $size = "{0:N$decimals} Mb" -f ($bytes / 1Mb) + } elseif ($bytes -gt 1Kb) { + $size = "{0:N$decimals} Kb" -f ($bytes / 1Kb) + } else { + $size = "{0:N$decimals} b" -f $bytes + } + return $size +} function pause { param([string]$message = "Press Enter to continue... ", [bool]$warning = $False) if ($warning) { diff --git a/make.cmd b/make.cmd index 29afdf3..01dc542 100644 --- a/make.cmd +++ b/make.cmd @@ -50,6 +50,14 @@ dism /add-package /image:"%wd%\mount" /packagepath:"%winpe_ocs%\en-us\WinPE-Dism :: Install WinPE-WMI > WinPE-NetFX > WinPE-Scripting > WinPE-PowerShell before you install WinPE-SecureBootCmdlets. dism /add-package /image:"%wd%\mount" /packagepath:"%winpe_ocs%\WinPE-SecureBootCmdlets.cab" /logpath:"dism.log" +:: Install WinPE-WMI > WinPE-NetFX > WinPE-Scripting > WinPE-PowerShell before you install WinPE-StorageWMI. +dism /add-package /image:"%wd%\mount" /packagepath:"%winpe_ocs%\WinPE-StorageWMI.cab" /logpath:"dism.log" +dism /add-package /image:"%wd%\mount" /packagepath:"%winpe_ocs%\en-us\WinPE-StorageWMI_en-us.cab" /logpath:"dism.log" + +:: Install ?? before you install WinPE-EnhancedStorage. +dism /add-package /image:"%wd%\mount" /packagepath:"%winpe_ocs%\WinPE-EnhancedStorage.cab" /logpath:"dism.log" +dism /add-package /image:"%wd%\mount" /packagepath:"%winpe_ocs%\en-us\WinPE-EnhancedStorage_en-us.cab" /logpath:"dism.log" + :Robocopy del "%wd%\WK\Scripts\WK.log" mkdir "%wd%\mount\WK" @@ -78,12 +86,16 @@ echo. echo Press any key to commit changes... pause>nul +:Set-ScratchSpace +rem Force RamDisk size to try and avoid capture-image errors +dism /image:"%wd%\mount" /set-scratchspace:512 + :Unmount dism /unmount-image /mountdir:"%wd%\mount" /commit :CreateISO -del winpe10-test.iso -makewinpemedia.cmd /iso "%wd%\pe_files" winpe10-test.iso +del winpe10-2016.iso +makewinpemedia.cmd /iso "%wd%\pe_files" winpe10-2016.iso goto Done :Abort