Search
StarWind is a hyperconverged (HCI) vendor with focus on Enterprise ROBO, SMB & Edge

How to create a bootable USB for Windows Server 2019 installation?

  • May 20, 2024
  • 26 min read
Storage and Virtualization Engineer. Volodymyr has broad experience in solution architecture and data protection, backed by a technical background in applied physics.
Storage and Virtualization Engineer. Volodymyr has broad experience in solution architecture and data protection, backed by a technical background in applied physics.

Introduction

Since Windows Server 2019 release, the Internet is booming with its reviews. Someday, I maybe write my own one too. Till then, why don’t we focus on something more important than just listing new Windows Server features? In today’s post, I share the scripts for creating and formatting bootable USB disks for Windows Server.

I came up with the idea of this short guide after reading the following article:

https://www.thomasmaurer.ch/2018/07/create-a-usb-drive-for-windows-server-2019-installation.

In fact, Thomas did a great job, but his scripts can be improved! With this being said, today, I present my universal scripts based on ones developed by Thomas and improved with my experience and comments to his post in mind.

DISCLAIMER: You can try out the scripts form this article, but you are the only person responsible for the consequences!

Look at your hardware first

OS installation may sometimes be a challenging task. Especially, when it comes to selecting and creating a bootable device. By the way, did you know that apart from USB, there are at least 3 other storage media that can be used as Windows installation disks? USB is more convenient for that purpose, IMHO, so let’s just stick to it. Of course, you can use Rufus to create a bootable USB. But, it is always good to know an alternative, right? And, it is better NOT to rely on some weird 3rd party software…

Picking a bootable disk partition type that is compatible with your hardware is not that straightforward too. Before creating such device, you need to think whether OS can be installed on your hardware afterward. For instance, a device with MBR partition type can be started only on a PC or server that supports BIOS. Devices with GPT partition type, in turn, require UEFI. So, take a closer look at your hardware first!

The Toolkit used

Here are things needed to create a bootable USB:

  • Windows 10 PC. You need it to run the scripts. Note that several PowerShell commands may be unavailable.
  • 8 GB USB drive. Windows Server consumes at least 4 GB, but it is always good to have some extra space. So, obviously, you cannot create a bootable USB of lower capacity.
  • Installation image (*.iso) for copying Windows Server 2019 distributive files.
  • A device to test the bootable USB. It can be a PC, server, or VM.

Creating a bootable USB for Windows Server 2019

Initially, find a Windows Server 2019 installation image. You can download it and read about the OS itself at the official website:

https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2019.

The script for creating a bootable USB described here is good for older Windows Server versions too. I tried it for Windows Server 2016, it worked good. Do you remember that I said at the beginning that I made universal scripts?

Here’s briefly how my script for creating a Windows Server bootable USB works:

  1. All drives connected to the system are scanned after starting the script. I have fine-tuned it such that only USBs are listed. One more time, you can use one only if its volume exceeds 8GB.
  2. Select any USB disk from that list.
  3. The disk you choose becomes a bootable USB. Note that all data are wiped out from that drive! By default, a GPT volume formatted as FAT32 is created. But, you can also create an MBR volume formatted as NTFS.
  4. Mounting Windows Server 2019 bootable image.
  5. Copying files and directories from the mounted image to the USB.
  6. While being written to a GTP disk, the image will be automatically split into 3GB chunks if the image keeping install.wim files is larger than 4GB. File splitting involves a temporary folder creation, so you need from 4 through 8GB of free space on the system disk therefor.
  7. Once script finishes, the disk where the image resides gets automatically unmounted.

To run the script, save its body locally as “Create-USB-Drive.ps.1”.

Next, start PowerShell as Administrator. Run the script with this cmdlet:

PS>.\Create-USB-Drive.ps1 -ISOImg "C:\Temp\Win_Srv_2019.iso" -DriveName "New Usb Drive Name" -BootType "Boot Type"

I guess it is good to discuss each piece of this command.

  • .\Create-USB-Drive.ps1 stands for the script name.
  • -ISOImg “C:\Temp\Win_Srv_2019.iso” represents the path to the *.iso image. Keep the script in that directory (it is just more convenient).
  • -DriveName “New Usb Drive Name” – the new USB drive name. By default, it is called Boot-Drive.
  • -BootType “Boot Type” allows setting the firmware interface for booting (UEFI or BIOS). UEFI is set by default.

Boot Type

Select the necessary disk afterward. Or, you can exit the script.

Necessary disk

Now, finally, the script comes. Once it finishes, you can start installing the OS.

<#
 .SYNOPSIS A script to create an installation USB for Windows 2019 Server.
 .PARAMETER ISOImg The path to the Windows Server 2019 image.
 .PARAMETER DriveName The flash drive name. "Boot-Drive" is set by default.
 .PARAMETER BootType The boot type for the flash drive (UEFI or BIOS). UEFI is set by default.
 .EXAMPLE PS> .\Create-USB-Drive.ps1 -ISOImg "C:\Temp\Win_Srv_2019.iso" -DriveName "New Usb Drive" -BootType "UEFI"
#>
 
# Specify parameters passed to the script as variables
param
(
[Parameter (Mandatory = $true)]
[String]$ISOImg,
[Parameter (Mandatory = $false)]
[String]$DriveName = "Boot-Drive",
[Parameter (Mandatory = $false)]
[ValidateSet("BIOS","UEFI")]
[String]$BootType = "UEFI"
)
 
#Confirmation of the privileges to run the script.
#requires –RunAsAdministrator

# Variable definition based on the boot type
if ($BootType -eq "UEFI")
 {
 $PartStyle="GPT"
 $FSType="FAT32"
 $IsPartActive=$false 
 }
 else
 {
 $PartStyle="MBR"
 $FSType="NTFS"
 $IsPartActive=$true
 }
 
# Clean the console to get started
Clear-Host
 
# Check whether a USB drive is connected to the system
if (!(Get-Disk | Where BusType -eq "USB" )) 
 {
 # Get the list of all drives
 Get-Disk | Format-Table -AutoSize Number,FriendlyName,BusType,@{Name="Size (GB)"; Expression={[int]($_.Size/1GB)}},PartitionStyle
 
 # Delete local variables
 Remove-Variable -Name * -Force -ErrorAction SilentlyContinue
 
 # Pause before closing the console
 Write-Error "Flash drive not found! Please connect an appropriate one and run the script again!" | Pause | Clear-Host
 exit
 } 
 else
 {
 # Get the list of USB drives
 Get-Disk | Where BusType -eq "USB" | Format-Table -AutoSize Number,FriendlyName,@{Name="Size (GB)"; Expression={[int]($_.Size/1GB)}},PartitionStyle
 
 # The cycle variable initialization
 $Choice1=0
 
 # Create the first input cycle
 while (($Choice1).Equals(0)) 
 {
 # Get the number of the required USB drive from the user
 $NumOfDisk = Read-Host 'Type the number of the required disk from the list as a number. To exit the script, enter "Exit"'
 
 # Validation of entered data
 if (($NumOfDisk).Equals("E") -or ($NumOfDisk).Equals("e") -or ($NumOfDisk).Equals("Exit") -or ($NumOfDisk).Equals("exit") ) 
 {
 # Delete local variables
 Remove-Variable -Name * -Force -ErrorAction SilentlyContinue
 
 # Pause before closing the console
 Write-Warning "You have successfully terminated the script!" | Pause | Clear-Host 
 Exit
 } 
 
 # Get from the user the value for the variable name of the required USB drive
 $USBDrive = Get-Disk | Where Number -eq "$NumOfDisk"
 
 # Check if disk variable has been input correctly
 if (($USBDrive).BusType -eq "USB" -and ($USBDrive).Number -notlike $null -and ($USBDrive).Number -gt "0" -and ([int]($USBDrive.Size/1GB)) -ge "7" )
 {
 # The cycle variable initialization
 $Choice2=0
 
 # Create the second input cycle
 while (($Choice2).Equals(0)) 
 {
 # Reading data from the console to a variable
 $Confirm = Read-Host "You have selected the disk ("($USBDrive).FriendlyName" ). All data on this disk will be deleted! Continue (Yes(Y) / No(N) / Exit(E))"
 
 # Validation of the entered data 
 if (($Confirm).Equals("Y") -or ($Confirm).Equals("y") -or ($Confirm).Equals("Yes") -or ($Confirm).Equals("yes") ) 
 {
 $Choice1=1
 break
 }
 # Validation of the entered data
 elseif (($Confirm).Equals("N") -or ($Confirm).Equals("n") -or ($Confirm).Equals("No") -or ($Confirm).Equals("no") ) 
 {
 Write-Warning "Please choose another drive number!"
 $Choice2=1
 continue
 } 
 # Validation of the entered data
 elseif (($Confirm).Equals("E") -or ($Confirm).Equals("e") -or ($Confirm).Equals("Exit") -or ($Confirm).Equals("exit") ) 
 { 
 # Delete local variables
 Remove-Variable -Name * -Force -ErrorAction SilentlyContinue
 
 # Pause before closing the console
 Write-Warning "You have successfully terminated the script!" | Pause | Clear-Host
 exit
 } 
 else 
 {
 Write-Warning "An invalid or unrecognizable input received! Please reenter the value." 
 
 } 
 } 
 } 
 else
 {
 Write-Warning "An invalid or unrecognized value was received, or the selected drive volume is less than 8GB! Please re-enter the value!" 
 }
 }
 
# Delete data from the flash drive. Assign a partition style
$USBDrive | Clear-Disk -RemoveData -Verbose:$true -Confirm:$false -PassThru | Set-Disk -PartitionStyle $PartStyle -WarningAction SilentlyContinue
 
# Create the partition. Formatting in a new file system 
$DrivePart = $USBDrive | New-Partition -Verbose:$true -UseMaximumSize -AssignDriveLetter -WarningAction SilentlyContinue | Format-Volume -Verbose:$true -Force:$true -FileSystem $FSType -NewFileSystemLabel $DriveName 
 
# Make a partition active 
$USBDrive | Get-Partition -Verbose:$true | Set-Partition -Confirm:$false -Verbose:$true -IsActive $IsPartActive
 
# Mount the installation image
$MntImg = Mount-DiskImage -ImagePath $ISOImg -StorageType ISO -PassThru
 
# Mount an image letter
$MntImgLetter = ($MntImg | Get-Volume).DriveLetter
 
# Assign a drive letter
$DriveLetter = ($DrivePart).DriveLetter
 
# Assign an installation disk letter
$InstFSize = Get-Childitem -Path $MntImgLetter":\sources\install.wim" | select length
 if ( ($BootType).Equals("BIOS") -and [int](($InstFSize).Length/1GB) -le "4") 
 {
 # Copy all files to the USB drive.
 Copy-Item -Verbose:$true -Force:$true -Recurse -Path ($MntImgLetter+":\*") -Destination ($DriveLetter+":\")
 } 
 else
 {
 # Copy all files to the USB drive except install.wim
 Copy-Item -Verbose:$true -Exclude "install.wim" -Recurse -Path ($MntImgLetter+":\*") -Destination ($DriveLetter+":\")
 
 # Initialize the temporary directory variable on the PC and Create a temporary directory on the PC
 ($TmpPcDir = $env:TEMP+"\DISMTMP\") | new-item -Path $TmpPcDir -Force:$true -Verbose:$true -itemtype directory | Out-Null
 
 # Split a Windows image file (install.wim) 
 Dism /Split-Image /ImageFile:$MntImgLetter":\sources\install.wim" /SWMFile:$TmpPcDir\install.swm /FileSize:3000 /English /Quiet
 
 # Transfer files to the flash drive
 Move-Item -Verbose:$true -Force:$true -Path ($TmpPcDir+"*") -Destination ($DriveLetter+":\sources\") 
 
 # Delete the temporary directory
 Remove-Item $TmpPcDir -Force:$true -Verbose:$true -Recurse
 }
 
# Unmount the installation image
Dismount-DiskImage -Verbose:$true -ImagePath $ISOImg
 
# Delete local variables
Remove-Variable -Name * -Force -ErrorAction SilentlyContinue
 
 }
 
# Pause before closing the console
Write-Warning "The script has been successfully completed! Your bootable flash drive is ready to use!" | Pause | Clear-Host

Reformatting the Drive after Windows installation

Now, let’s take a look at my script for reformatting a USB. Here’s briefly how it works:

  1. Once the script is started, all disks connected to the system are scanned, but only USBs are listed. This script works for USB disks of any size.
  2. WARNING: IF YOU DO RUN THIS SCRIPT, DATA ON THE SELECTED USB IS WIPED OUT! You can create either a GPT volume or MBR one. Also, you can format the file system as FAT32, exFAT, FAT, or NTFS. Just input the appropriate parameter.

Save locally the body of the script provided below as Return-USB-Drive.ps1 file. Next, open PowerShell console as administrator, and you can run the script with the command below:

 PS>.\Return-USB-Drive.ps1 -DriveName " New Usb Drive Name " -PartStyle " Partition style " -FSType "File system format"

Let’s discuss what each part of the command stands for.

  • -DriveName “New Usb Drive Name” the new name of the USB disk. By default, it is called My Flash Drive.
  • -PartStyle ” Partition style ” – the partition table format (GPT or MBR). It is set to GPT by default.
  • -FSType “File system format” allows changing the type of file system formatting.

My Flash Drive

Select the disk which you want to reformat. Otherwise, you can exit the script.

Select the disk which you want to reformat

Here’s the script that I was talking about. Once it finishes, the disk is ready.

<#
 .SYNOPSIS A script for cleaning and reformatting a flash drive after installing Windows
 .PARAMETER DriveName A new flash drive name. My Flash Drive is set by default!
 .PARAMETER PartStyle The partition style for the flash drive (MBR or GPT). GPT is set by default!
 .PARAMETER FSType The flash disk file system format type. NTFS is set by default!
 .EXAMPLE PS> .\Return-USB-Drive.ps1 -DriveName "New Usb Drive" -PartStyle "GPT" -FSType "FAT32" 
#>

# Parameters passed to the script as variables
param
(
[Parameter (Mandatory = $false)]
[String]$DriveName = "My Flash Drive",
[Parameter (Mandatory = $true)]
[ValidateSet("exFAT","FAT","FAT32","NTFS")]
[String]$FSType = "NTFS",
[Parameter (Mandatory = $true)]
[ValidateSet("MBR","GPT")]
[String]$PartStyle = "GPT"
)

#Cleaning the console to get started
Clear-Host

#Confirmation of the privileges to run the script.
#requires –RunAsAdministrator

# Check whether a USB drive is connected to the system
if (!(Get-Disk | Where BusType -eq "USB" )) 
 {
 # Get the list of all drives
 Get-Disk | Format-Table -AutoSize Number,FriendlyName,BusType,@{Name="Size (GB)"; Expression={[int]($_.Size/1GB)}},PartitionStyle
 
 # Delete local variables
 Remove-Variable -Name * -Force -ErrorAction SilentlyContinue

# Pause before closing the console
 Write-Error "Flash drive not found! Please connect an appropriate one and run the script again!" | Pause | Clear-Host
 exit
 } 
 else
 {

# Get a list of USB drives
Get-Disk | Where BusType -eq "USB" | Format-Table -AutoSize Number,FriendlyName,@{Name="Size (GB)"; Expression={[int]($_.Size/1GB)}},PartitionStyle

# The cycle variable initialization
$Choice1=0

# Creating the first input cycle
while (($Choice1).Equals(0)) 
 {
 # Get the number of the required USB drive from the user
 $NumOfDisk = Read-Host 'Type the number of the required disk from the list as a number. To exit the script, enter "Exit"'

# Validation of the entered data
 if (($NumOfDisk).Equals("E") -or ($NumOfDisk).Equals("e") -or ($NumOfDisk).Equals("Exit") -or ($NumOfDisk).Equals("exit") ) 
 {
 # Delete local variables
 Remove-Variable -Name * -Force -ErrorAction SilentlyContinue
 
 # Pause before closing the console
 Write-Warning "You have successfully terminated the script!" | Pause | Clear-Host 
 exit
 } 
 
 # Get from the user the value for the variable name of the required USB drive
 $USBDrive = Get-Disk | Where Number -eq "$NumOfDisk"
 
 # Checking whether the disk variable has been input correctly
 if (($USBDrive).BusType -eq "USB" -and ($USBDrive).Number -notlike $null -and ($USBDrive).Number -gt "0") 
 {
 # The cycle variable initialization
 $Choice2=0
 
 # Creating the second input cycle
 while (($Choice2).Equals(0)) 
 {
 # Reading the data from the console to a variable
 $Confirm = Read-Host "You have selected the disk ("($USBDrive).FriendlyName" ). All data on this disk will be deleted! Continue (Yes(Y) / No(N) / Exit(E))"
 
 # Validation of the entered data 
 if (($Confirm).Equals("Y") -or ($Confirm).Equals("y") -or ($Confirm).Equals("Yes") -or ($Confirm).Equals("yes") ) 
 {
 $Choice1=1
 break
 }
 # Validation of the entered data
 elseif (($Confirm).Equals("N") -or ($Confirm).Equals("n") -or ($Confirm).Equals("No") -or ($Confirm).Equals("no") ) 
 {
 Write-Warning "Please choose another drive Number!"
 $Choice2=1
 continue
 } 
 # Validation of the entered data
 elseif (($Confirm).Equals("E") -or ($Confirm).Equals("e") -or ($Confirm).Equals("Exit") -or ($Confirm).Equals("exit") ) 
 { 
 # Delete local variables
 Remove-Variable -Name * -Force -ErrorAction SilentlyContinue
 
 # Pause before closing the console
 Write-Warning "You have successfully terminated the script!" | Pause | Clear-Host
 exit 
 } 
 else 
 {
 Write-Warning "An invalid or unrecognizable answer received! Please re-enter the value!" 
 
 } 
 } 
 } 
 else
 {
 Write-Warning "An invalid or unrecognized value was entered! Please re-enter the value!" 
 }
 
 }

# Delete data from the flash drive. Assign a partition style
$USBDrive | Clear-Disk -RemoveData -Verbose:$true -Confirm:$false -PassThru | Set-Disk -PartitionStyle $PartStyle -WarningAction SilentlyContinue

# Create the partition and format it to a new file system 
$DrivePart = $USBDrive | New-Partition -Verbose:$true -UseMaximumSize -AssignDriveLetter -WarningAction SilentlyContinue | Format-Volume -Verbose:$true -Force:$true -FileSystem $FSType -NewFileSystemLabel $DriveName

# Delete local variables
Remove-Variable -Name * -Force -ErrorAction SilentlyContinue
 }
 
# Pause before closing the console
Write-Warning "The script has been successfully completed! Your flash drive is ready to use!" | Pause | Clear-Host

Conclusion

The scripts discussed in this post allow creating a bootable USB drive for installing Windows Server 2019 or Windows Server 2016. I did not reinvent the wheel here. I just improved the existing scripts considering both what people say and my experience. I hope that my way to create a Windows Server installation disk comes in handy!

Found Volodymyr’s article helpful? Looking for a reliable, high-performance, and cost-effective shared storage solution for your production cluster?
Dmytro Malynka
Dmytro Malynka StarWind Virtual SAN Product Manager
We’ve got you covered! StarWind Virtual SAN (VSAN) is specifically designed to provide highly-available shared storage for Hyper-V, vSphere, and KVM clusters. With StarWind VSAN, simplicity is key: utilize the local disks of your hypervisor hosts and create shared HA storage for your VMs. Interested in learning more? Book a short StarWind VSAN demo now and see it in action!