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

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

  • May 20, 2024
  • 21 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.

Since the release of Windows Server 2022, discussions and reviews have been abundant. One day, I might write my own review, but for now, let’s focus on something more practical—creating a bootable USB for installation. In today’s guide, I’ll share updated scripts for creating and formatting bootable USB drives for Windows Server.

Introduction

This guide was inspired by an article by Thomas Maurer on creating bootable USB drives for Windows Server 2019. While his approach was solid, I’ve improved upon his scripts with updated methods and additional enhancements.

DISCLAIMER: You may use the scripts in this guide, but you are responsible for any consequences that result from running them.

Look at your hardware first

OS installation can sometimes be challenging, especially when selecting and creating a bootable device. Although there are multiple ways to create installation media, using a USB drive remains the most convenient. Tools like Rufus are available, but knowing an alternative method—one that doesn’t rely on third-party software—is always beneficial.

Before proceeding, ensure that your hardware is compatible with the chosen boot method. Devices with MBR partition type can only boot on systems that support BIOS, whereas GPT partition types require UEFI.

The toolkit used

To create a bootable USB, you will need:

  • A Windows 11 or Windows Server 2022 system to run the scripts.
  • An 8GB or larger USB drive (Windows Server 2022 requires at least 4GB, but extra space is recommended).
  • A Windows Server 2022 installation ISO.
  • A target system for testing (PC, server, or VM).

Creating a bootable USB for Windows Server 2022

First, obtain a Windows Server 2022 installation ISO. You can download it from the official Microsoft website.

Steps Overview:

  1. The script scans connected drives and lists only USB devices with at least 8GB of storage.
  2. You select the USB drive to use.
  3. The selected drive is formatted and converted into a bootable USB (all existing data will be erased).
  4. The Windows Server 2022 image is mounted.
  5. Files are copied from the mounted image to the USB drive.
  6. If the install.wim file exceeds 4GB, it is split into smaller chunks.
  7. Once complete, the image is automatically unmounted.

Running the Script

Save the following script as Create-USB-Drive.ps1 and execute it in PowerShell as an administrator:

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

Script Parameters:

  • ISOImg: Path to the Windows Server 2022 ISO file.
  • DriveName: Name assigned to the USB drive (default: Boot-Drive).
  • BootType: Specifies whether the USB is bootable via UEFI (default) or BIOS.

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_2022.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

After installing Windows Server, you may want to reformat the USB drive for regular use. The following script accomplishes this by allowing you to:

  • Select the USB drive.
  • Format it to GPT or MBR.
  • Choose a file system format (FAT32, exFAT, or NTFS).

Save the script as Return-USB-Drive.ps1 and run it using:

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

Script Parameters:

  • DriveName: Name for the USB drive (default: My Flash Drive).
  • PartStyle: Partition format (GPT or MBR; default: GPT).
  • FSType: File system format (NTFS by default).

My Flash Drive

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

JetFlash

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

These scripts provide an efficient way to create a bootable USB for installing Windows Server 2022. Instead of relying on third-party tools, this PowerShell-based approach ensures a streamlined process. I hope this guide proves useful!

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!