
PowerShell Tips for Veeam – Silent Install of Veeam Enterprise Manager

With the recent release of Veeam Backup & Replication 12.3.2.3617, which addresses a 9.9 CVE, it was time to complete some upgrades. However, to upgrade Veeam Backup & Replication, Veeam Enterprise Manager must be upgraded first. You can find more about this patch release here – https://www.veeam.com/kb4696
To upgrade a large number of Veeam servers, I was seeking a way to automate the initial step, which involves upgrading the Enterprise Manager. I was able to produce a script that will do the following things, and could also be used to upgrade Veeam Backup & Replication as well –
- Download the upgrade ISO file for the 12.3.2.3617 release to a directory you specify in the variables
- Verify the downloaded ISO file to ensure it is complete and accurate
- Mount the ISO file once verified
- Prompt to run the silent installer EXE file using the Enterprise Manager XML file
- Dismount the ISO once completed
Below is the script created to accomplish these tasks. As I mentioned, you can also use it with VBR, utilizing the related XML file. Please note that for my script, I used the E drive and a folder called Software that was located on my servers. You can change this variable to any drive or folder you require.
Veeam Enterprise Manager Silent Install Script –
# Veeam Backup & Replication 12.3.2 Upgrade Script
# This script downloads, mounts, and runs the Veeam 12.3.2 upgrade
# Author: Chris Childerhose
# Variables
$downloadUrl = "https://download2.veeam.com/VBR/v12/VeeamBackup&Replication_12.3.2.3617_20250610_update.iso"
$softwareFolder = "E:\Software"
$isoFileName = "VeeamBackup&Replication_12.3.2.3617_20250610_update.iso"
$isoPath = Join-Path $softwareFolder $isoFileName
$answerFilePath = "E:\Software\EmAnswerFile_upgrade.xml"
Write-Host "Starting Veeam 12.3.2 Upgrade Process..." -ForegroundColor Green
try {
# Step 1: Create Software folder if it doesn't exist
Write-Host "Checking if Software folder exists..." -ForegroundColor Yellow
if (-not (Test-Path $softwareFolder)) {
Write-Host "Creating Software folder at $softwareFolder" -ForegroundColor Yellow
New-Item -ItemType Directory -Path $softwareFolder -Force | Out-Null
Write-Host "Software folder created successfully." -ForegroundColor Green
} else {
Write-Host "Software folder already exists." -ForegroundColor Green
}
# Step 2: Download the Veeam ISO
Write-Host "Starting download of Veeam 12.3.2 ISO..." -ForegroundColor Yellow
Write-Host "Download URL: $downloadUrl" -ForegroundColor Cyan
Write-Host "Destination: $isoPath" -ForegroundColor Cyan
# Check if file already exists
if (Test-Path $isoPath) {
$overwrite = Read-Host "ISO file already exists. Do you want to re-download? (Y/N)"
if ($overwrite -ne 'Y' -and $overwrite -ne 'y') {
Write-Host "Using existing ISO file." -ForegroundColor Green
} else {
Remove-Item $isoPath -Force
Write-Host "Downloading ISO file... This may take several minutes." -ForegroundColor Yellow
# Download with progress monitoring
$downloadJob = Start-Job -ScriptBlock {
param($url, $path)
Invoke-WebRequest -Uri $url -OutFile $path -UseBasicParsing
} -ArgumentList $downloadUrl, $isoPath
# Monitor download progress
$downloadStartTime = Get-Date
while ($downloadJob.State -eq "Running") {
$elapsed = (Get-Date) - $downloadStartTime
Write-Host "Download in progress... Elapsed time: $($elapsed.ToString('mm\:ss'))" -ForegroundColor Cyan
# Check if partial file exists and show size
if (Test-Path $isoPath) {
$currentSize = (Get-Item $isoPath).Length / 1MB
Write-Host "Current file size: $([math]::Round($currentSize, 2)) MB" -ForegroundColor Cyan
}
Start-Sleep -Seconds 10
}
# Wait for job completion and get results
$downloadResult = Receive-Job -Job $downloadJob -Wait
Remove-Job -Job $downloadJob
Write-Host "Download completed successfully." -ForegroundColor Green
}
} else {
Write-Host "Downloading ISO file... This may take several minutes." -ForegroundColor Yellow
# Download with progress monitoring
$downloadJob = Start-Job -ScriptBlock {
param($url, $path)
Invoke-WebRequest -Uri $url -OutFile $path -UseBasicParsing
} -ArgumentList $downloadUrl, $isoPath
# Monitor download progress
$downloadStartTime = Get-Date
while ($downloadJob.State -eq "Running") {
$elapsed = (Get-Date) - $downloadStartTime
Write-Host "Download in progress... Elapsed time: $($elapsed.ToString('mm\:ss'))" -ForegroundColor Cyan
# Check if partial file exists and show size
if (Test-Path $isoPath) {
$currentSize = (Get-Item $isoPath).Length / 1MB
Write-Host "Current file size: $([math]::Round($currentSize, 2)) MB" -ForegroundColor Cyan
}
Start-Sleep -Seconds 10
}
# Wait for job completion and get results
$downloadResult = Receive-Job -Job $downloadJob -Wait
Remove-Job -Job $downloadJob
Write-Host "Download completed successfully." -ForegroundColor Green
}
# Verify the download completed successfully
Write-Host "Verifying download completion..." -ForegroundColor Yellow
$maxWaitTime = 30 # seconds
$waitTime = 0
while (-not (Test-Path $isoPath) -and $waitTime -lt $maxWaitTime) {
Write-Host "Waiting for file to be fully written to disk..." -ForegroundColor Cyan
Start-Sleep -Seconds 2
$waitTime += 2
}
if (-not (Test-Path $isoPath)) {
throw "Downloaded ISO file not found at $isoPath after waiting"
}
# Additional wait to ensure file is completely written
Write-Host "Ensuring file is completely written..." -ForegroundColor Yellow
Start-Sleep -Seconds 5
$fileSize = (Get-Item $isoPath).Length / 1MB
Write-Host "ISO file size: $([math]::Round($fileSize, 2)) MB" -ForegroundColor Cyan
# Verify file is not corrupted by checking if it's a reasonable size for a Veeam ISO
if ($fileSize -lt 100) {
throw "Downloaded file appears to be too small ($([math]::Round($fileSize, 2)) MB). Download may have failed."
}
Write-Host "Download verification completed successfully." -ForegroundColor Green
# Step 3: Mount the ISO
Write-Host "Mounting ISO file..." -ForegroundColor Yellow
$mountResult = Mount-DiskImage -ImagePath $isoPath -PassThru
$driveLetter = ($mountResult | Get-Volume).DriveLetter
if (-not $driveLetter) {
throw "Failed to get drive letter for mounted ISO"
}
Write-Host "ISO mounted successfully on drive $driveLetter`:" -ForegroundColor Green
# Verify the setup file exists
$setupPath = "$driveLetter`:\Setup\Silent\Veeam.Silent.Install.exe"
if (-not (Test-Path $setupPath)) {
throw "Setup file not found at $setupPath"
}
# Check if answer file exists
if (-not (Test-Path $answerFilePath)) {
Write-Warning "Answer file not found at $answerFilePath"
Write-Host "Please ensure the answer file exists before running the installation." -ForegroundColor Red
$continue = Read-Host "Do you want to continue anyway? (Y/N)"
if ($continue -ne 'Y' -and $continue -ne 'y') {
throw "Installation cancelled - answer file not found"
}
}
# Step 4: Run the silent installation
Write-Host "Starting Veeam silent installation..." -ForegroundColor Yellow
Write-Host "Setup Path: $setupPath" -ForegroundColor Cyan
Write-Host "Answer File: $answerFilePath" -ForegroundColor Cyan
$installCommand = "`"$setupPath`" /AnswerFile `"$answerFilePath`" /SkipNetworkLogonErrors"
Write-Host "Command: $installCommand" -ForegroundColor Cyan
# Confirm before running
$confirm = Read-Host "Do you want to proceed with the installation? (Y/N)"
if ($confirm -eq 'Y' -or $confirm -eq 'y') {
Write-Host "Running installation command..." -ForegroundColor Yellow
# Start the process and wait for completion
$process = Start-Process -FilePath $setupPath -ArgumentList "/AnswerFile `"$answerFilePath`" /SkipNetworkLogonErrors" -Wait -PassThru -NoNewWindow
if ($process.ExitCode -eq 0) {
Write-Host "Installation completed successfully!" -ForegroundColor Green
} else {
Write-Host "Installation completed with exit code: $($process.ExitCode)" -ForegroundColor Yellow
Write-Host "Please check the Veeam installation logs for details." -ForegroundColor Yellow
}
} else {
Write-Host "Installation cancelled by user." -ForegroundColor Yellow
}
} catch {
Write-Host "Error occurred: $($_.Exception.Message)" -ForegroundColor Red
Write-Host $_.ScriptStackTrace -ForegroundColor Red
} finally {
# Cleanup: Dismount the ISO if it was mounted
try {
if ($driveLetter) {
Write-Host "Dismounting ISO..." -ForegroundColor Yellow
Dismount-DiskImage -ImagePath $isoPath
Write-Host "ISO dismounted successfully." -ForegroundColor Green
}
} catch {
Write-Warning "Failed to dismount ISO: $($_.Exception.Message)"
}
}
Write-Host "Script execution completed." -ForegroundColor Green
I hope this will be helpful for those who manage multiple sites and Veeam environments, allowing them to automate the upgrade to the latest release of the product.
Until the next PowerShell tip, happy blogging.