Close
Close
Close
Close
In this tutorial, you’ll learn how to use a PowerShell script to replace multiple camera device drivers in Milestone XProtect.
Drivers are essential for connecting your XProtect recording server to cameras, enabling important features like video streams, advanced compression options (like Zipstream), and new analytics capabilities. While Milestone periodically updates these drivers (e.g., Axis, Bosch, Hanwha), many systems still run on outdated versions.
Manually updating each camera can be time-consuming, but with this script, you can automate the process, saving hours of manual work.
# Import the MilestonePStools module
Import-Module MilestonePStools -ErrorAction Stop
# Check if the module is loaded and available
if (-not (Get-Module -Name MilestonePStools -ListAvailable)) {
Write-Error "MilestonePStools module is not available. Please install the module."
exit
}
# Create the Logs directory if it doesn't exist
if (-not (Test-Path -Path "C:\Logs")) {
New-Item -Path "C:\Logs" -ItemType Directory
}
# Create the temporary directory for CSV storage
if (-not (Test-Path -Path "C:\Temp")) {
New-Item -Path "C:\Temp" -ItemType Directory
}
# Function to add content to log file with permission handling
function Add-LogContent {
param (
[string]$filePath,
[string]$content
)
try {
Add-Content -Path $filePath -Value $content -Force
} catch {
Write-Error "Failed to write to log file $filePath. Error: $_"
}
}
# Connect to the Management Server (forcing disconnection of any previous session)
Connect-ManagementServer -ShowDialog -AcceptEula -Force
# Get all recording servers
$allRecordingServers = Get-VmsRecordingServer
# Function to get hardware details in batches and save to CSV
function Get-HardwareDetails {
param (
$allHardware,
$allRecordingServers,
$csvPath
)
foreach ($hardware in $allHardware) {
$driver = $null
try {
$driver = Get-VmsHardwareDriver -Hardware $hardware -ErrorAction Stop
} catch {
$errorMsg = "$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss') : HardwareDriver '$($hardware.HardwareDriverPath)' for hardware '$($hardware.Name)' not found on the parent recording server."
Write-Error $errorMsg
Add-LogContent -filePath "C:\Logs\DriverErrors.txt" -content $errorMsg
continue
}
# Extract IP address from the Address property
$ipAddress = $null
if ($hardware.Address -match 'http://(.*?)/') {
$ipAddress = $matches[1]
}
# Extract RecordingServerId from ParentItemPath
$recordingServerId = $null
if ($hardware.ParentItemPath -match 'RecordingServer\[([^\]]+)\]') {
$recordingServerId = $matches[1]
}
# Initialize the recording server name as empty
$recorderName = ""
# Check if RecordingServerId is not null and get the recording server details
if ($recordingServerId -ne $null) {
$recorder = $allRecordingServers | Where-Object { $_.Id -eq $recordingServerId }
if ($recorder) {
$recorderName = $recorder.Name
}
}
$hardwareDetails = [PSCustomObject]@{
HardwareName = $hardware.Name
IPAddress = $ipAddress
CurrentDriver = $driver.Name
RecordingServerName = $recorderName
Enabled = if ($hardware.Enabled) { "Enabled" } else { "Disabled" }
}
$hardwareDetails | Export-Csv -Path $csvPath -Append -NoTypeInformation
}
}
# Get all hardware details in batches and save to CSV
$batchSize = 100
$allHardware = Get-VmsHardware -All
$csvPath = "C:\Temp\HardwareDetails.csv"
# Clear the CSV file if it exists
if (Test-Path -Path $csvPath) {
Remove-Item -Path $csvPath
}
for ($i = 0; $i -lt $allHardware.Count; $i += $batchSize) {
$batch = $allHardware[$i..[math]::Min($i + $batchSize - 1, $allHardware.Count - 1)]
Get-HardwareDetails -allHardware $batch -allRecordingServers $allRecordingServers -csvPath $csvPath
}
# Import the CSV data back into a PowerShell object for processing
$hardwareDetails = Import-Csv -Path $csvPath
# Sort hardware details by RecordingServerName
$hardwareDetails = $hardwareDetails | Sort-Object RecordingServerName
# Display grid with cameras using old drivers
$camerasToReplace = $hardwareDetails | Out-GridView -Title "Select Cameras to Replace Drivers" -OutputMode Multiple
# Log selected cameras for replacement
$logFile = "C:\Logs\DriverReplacementLog.txt"
$camerasToReplace | ForEach-Object {
$logEntry = "$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss') : Selected camera for replacement: HardwareName=$($_.HardwareName), IPAddress=$($_.IPAddress), CurrentDriver=$($_.CurrentDriver), Enabled=$($_.Enabled), RecordingServerName=$($_.RecordingServerName)"
Add-LogContent -filePath $logFile -content $logEntry
Write-Host $logEntry
}
# Get all hardware drivers
$allDrivers = Get-VmsRecordingServer | Select-Object -First 1 | Get-VmsHardwareDriver
# Sort drivers by Name
$allDrivers = $allDrivers | Sort-Object Name
# Step 3: Select New Driver
$newDriver = $allDrivers | Out-GridView -Title "Select New Driver" -OutputMode Single
# Log selected new driver
$logEntry = "$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss') : Selected new driver: Name=$($newDriver.Name)"
Add-LogContent -filePath $logFile -content $logEntry
Write-Host $logEntry
# Final verification: Display the selected cameras and new driver
$confirmation = $camerasToReplace | Select-Object @{Name="HardwareName";Expression={$_."HardwareName"}}, @{Name="IPAddress";Expression={$_."IPAddress"}}, @{Name="CurrentDriver";Expression={$_."CurrentDriver"}}, @{Name="NewDriver";Expression={$newDriver.Name}}, @{Name="RecordingServerName";Expression={$_."RecordingServerName"}}, @{Name="Enabled";Expression={$_."Enabled"}} | Out-GridView -Title "Confirm Driver Replacement - Click OK to Proceed" -OutputMode Multiple
# Log confirmed cameras for replacement
$confirmation | ForEach-Object {
$logEntry = "$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss') : Confirmed camera for replacement: HardwareName=$($_.HardwareName), IPAddress=$($_.IPAddress), CurrentDriver=$($_.CurrentDriver), NewDriver=$($_.NewDriver), Enabled=$($_.Enabled), RecordingServerName=$($_.RecordingServerName)"
Add-LogContent -filePath $logFile -content $logEntry
Write-Host $logEntry
}
if ($confirmation.Count -eq 0) {
Write-Host "Operation cancelled."
exit
}
# Step 4: Replace Drivers and Log Changes
foreach ($camera in $confirmation) {
try {
$hardware = $allHardware | Where-Object { $_.Name -eq $camera.HardwareName }
if ($hardware) {
$startTime = Get-Date
$hardware | Set-VmsHardwareDriver -Driver $newDriver -Confirm:$false
$endTime = Get-Date
$duration = $endTime - $startTime
$logEntry = "$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss') : Replaced driver for camera $($camera.HardwareName) at IP $($camera.IPAddress) connected to $($camera.RecordingServerName) with new driver $($newDriver.Name). Duration: $duration"
Add-LogContent -filePath $logFile -content $logEntry
Write-Host $logEntry
} else {
$logEntry = "$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss') : No hardware found for camera $($camera.HardwareName) at IP $($camera.IPAddress)"
Add-LogContent -filePath $logFile -content $logEntry
Write-Host $logEntry
}
} catch {
$logEntry = "$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss') : Failed to replace driver for camera $($camera.HardwareName) at IP $($camera.IPAddress). Error: $_"
Add-LogContent -filePath $logFile -content $logEntry
Write-Host $logEntry
}
}
Write-Host "Driver replacement completed. Check the log file at $logFile for details."
# Cleanup: Remove the temporary CSV file
Remove-Item -Path $csvPath -Force
Subscribe to get a monthly dose of security & surveillance industry news and insights, Milestone VMS time-saving tricks, tips for hacking your way out of boring work sent directly to your inbox!