# encoding: ascii
# api: powershell
# title: WindowsInstallPoint
# description: Initialize-WindowsInstallPoint
# version: 1.7.0.0
# type: function
# author: Rich Kusak
# license: CC0
# function: Initialize-WindowsInstallPoint
# x-poshcode-id: 2609
# x-archived: 2011-11-05T17:25:59
# x-published: 2011-04-07T16:57:00
#
# This function was developed to automate the process required to make a USB drive a device from which Windows can be installed. Many posts are available on the internet about the manual steps required by searching: “install windows 7 from usb”.
#
function Initialize-WindowsInstallPoint {
<#
.SYNOPSIS
Initializes a drive to become a Windows OS install point.
.DESCRIPTION
The Initialize-WindowsInstallPoint function uses the "diskpart" utility to wipe, partition, and format a local or removable drive.
The "bootsect" tool processes the bootmgr allowing the drive to become a bootable device from which Windows can be installed.
Once the drive is initialized, copy the entire contents of the Windows install media to the drive.
Windows Vista, 7, Server 2008, and Server 2008 R2 are supported.
.PARAMETER Drive
Specifies the drive to initialize as a Windows install point.
<DriveLetter:>
.PARAMETER BootsectPath
The full file path to the "bootsect.exe" tool. For Windows versions 6.x, it's located on the installation media in the "boot" folder.
.PARAMETER DiskpartPath
The full file path to the "diskpart.exe" tool. Default parameter value: $env:SystemRoot\System32\diskpart.exe
.EXAMPLE
Initialize-WindowsInstallPoint X: D:\boot\bootsect.exe
Initializes drive X: using the bootsect.exe tool located in the boot folder of drive D:
.INPUTS
System.String
.OUTPUTS
None
.NOTES
Name: Initialize-WindowsInstallPoint
Author: Rich Kusak (rkusak@cbcag.edu)
Created: 2009-10-24
Last Edit: 2011-04-07 18:48
Version: 1.7.0.0
#Requires Version 2.0
.LINK
bootsect.exe
.LINK
diskpart.exe
#>
[CmdletBinding(ConfirmImpact='High', SupportsShouldProcess=$true)]
param (
[Parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true,
HelpMessage='The drive qualifier of the device to become a Windows install point.'
)]
[ValidateScript({
switch ($_) {
$env:SystemDrive {throw 'This operation cannot be performed on the system drive.'}
{$_ -match '^[a-z]:$'} {$true}
default {throw "The argument '$_' is not a valid drive designation. Supply an argument that matches '<DriveLetter>:' and try the command again."}
}
})]
[string]$Drive,
[Parameter(Position=1, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,
HelpMessage='The full path to the bootsect.exe tool.'
)]
[Alias('FullName')]
[ValidateScript({
if (Test-Path $_ -Include '*bootsect.exe') {$true} else {
throw "The argument '$_' is not a real path to the bootsect.exe tool."
}
})]
[string]$BootsectPath,
[Parameter(ValueFromPipelineByPropertyName=$true,
HelpMessage='The full path to the diskpart.exe tool.'
)]
[ValidateScript({
if (Test-Path $_ -Include '*diskpart.exe') {$true} else {
throw "The arguments '$_' is not a real path to the diskpart.exe tool."
}
})]
[string]$DiskpartPath = "$env:SystemRoot\System32\diskpart.exe"
)
begin {
function Test-Elevation {
$admin = [System.Security.Principal.WindowsBuiltInRole]::Administrator
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object System.Security.Principal.WindowsPrincipal($identity)
$principal.IsInRole($admin)
}
# Test that PowerShell is running as an administrator
if (-not (Test-Elevation)) {
throw 'Administrator privileges are required. PowerShell is not running as an administrator.'
}
$dpExitCodes = @{
0 = 'No error occurred. The entire script ran without failure.'
1 = 'A fatal exception occurred. There may be a serious problem.'
2 = 'The arguments specified on a Diskpart command line were incorrect.'
3 = 'Diskpart was unable to open the specified script or output file.'
4 = 'One of the services Diskpart uses returned a failure.'
5 = 'A command syntax error occurred. The script failed because an object was improperly selected or was invalid for use with that command.'
}
# Temporary files for the diskpart script and redirect standard output
$dpScript = [System.IO.Path]::GetTempFileName()
$rso = [System.IO.Path]::GetTempFileName()
# Set the initial instance of the variable used to control the flow of $psCmdlet
$psc = $null
} # end begin
process {
# Capitalize drive letter for display
$Drive = $Drive.ToUpper()
# Detrimine if the drive meets the requirements to become a Windows install point
Write-Debug 'Getting WMI object Win32_LogicalDisk'
$logicalDisk = Get-WmiObject Win32_LogicalDisk -Filter "DeviceID = '$Drive' "
if ($logicalDisk.DriveType -notmatch '^(2|3)$') {
return Write-Error "Drive '$Drive' is not a local or removable drive." -TargetObject $Drive
} elseif ($logicalDisk.Size -lt 3.5GB) {
# Different Windows editions vary in size but a drive 3.5GB or greater should accomidate all editions
Write-Warning "Drive '$Drive' may not be large enough for Windows installation files."
}
if ($psc -eq $null) {$psc = $psCmdlet}
if ($psc.ShouldProcess($Drive, "Initialize Windows install point. Any existing data will be DESTROYED")) {
Write-Progress -Activity 'Initialize Windows Install Point' -Status "Drive $Drive" -CurrentOperation 'Preparing disk volume' -PercentComplete 0
# Diskpart script content
@"
Select Volume $($Drive[0])
Clean
Create Partition Primary
Select Partition 1
Active
Format Quick FS=NTFS Label=WindowsInstallPoint
Assign Letter $Drive
Exit
"@ | Out-File $dpScript -Encoding ASCII -Force
Write-Debug 'Starting process diskpart.exe'
# Using Start-Process allows the exit code to be processed from either the console or ISE.
$diskpart = Start-Process $DiskpartPath "/s $dpScript" -NoNewWindow -Wait -PassThru -RedirectStandardOutput $rso
1..45 | ForEach {
Write-Progress -Activity 'Initialize Windows Install Point' -Status "Drive $Drive" -CurrentOperation 'Preparing disk volume' -PercentComplete $_
Sleep -Milliseconds 20
}
# Removes empty lines from the original diskpart.exe output and sends it to the verbose stream
Get-Content $rso | Select-String '.' | ForEach {Write-Verbose $_}
# Diskpart error handling
if ($diskpart.ExitCode -eq 0) {
Write-Verbose "Drive '$Drive' has been successfully processed by the diskpart.exe tool."
} elseif ($diskpart.ExitCode -gt 0) {
return Write-Error $dpExitCodes.($diskpart.ExitCode)
} else {
return Write-Error 'An unknown error occured.'
}
Write-Debug 'Starting process bootsect.exe'
# Using Start-Process allows the exit code to be processed from either the console or ISE.
$bootsect = Start-Process $BootsectPath "/NT60 $Drive" -NoNewWindow -Wait -PassThru -RedirectStandardOutput $rso
46..90 | ForEach {
Write-Progress -Activity 'Initialize Windows Install Point' -Status "Drive $Drive" -CurrentOperation 'Setting boot code' -PercentComplete $_
Sleep -Milliseconds 20
}
# Removes empty lines from the original bootsect.exe output and sends it to the verbose stream
Get-Content $rso | Select-String '.' | ForEach {Write-Verbose $_}
# Bootsect error handling
if ($bootsect.ExitCode -eq 0) {
Write-Verbose "Drive '$Drive' has been successfully processed by the bootsect.exe tool."
Write-Progress -Activity 'Initialize Windows Install Point' -Status "Drive $Drive" -CurrentOperation 'Complete' -PercentComplete 100
Sleep -Seconds 1
} else {
return Write-Error "$bootsect"
}
} # end ShouldProcess
Write-Verbose "Drive '$Drive' has been successfully initialized as a Windows install point."
} # end process
end {
# Cleanup temporary files
Remove-Item $dpScript, $rso -Force -WhatIf:$false
}
} # end function Initialize-WindowsInstallPoint