Skip to content

Instantly share code, notes, and snippets.

@9000cats
Created October 16, 2024 16:11
Show Gist options
  • Save 9000cats/b1ade1c68293184a9b9179060231de12 to your computer and use it in GitHub Desktop.
Save 9000cats/b1ade1c68293184a9b9179060231de12 to your computer and use it in GitHub Desktop.
Stops all Veeam services on Windows in reverse dependency order while displaying real-time status updates.
<#
.SYNOPSIS
This PowerShell script retrieves all active Veeam services along with their dependencies and handles their status updates.
It then processes each service in reverse dependency order to stop them gracefully, ensuring dependencies are handled correctly.
The script also provides real-time status updates for all services.
.DESCRIPTION
The script includes the following functions:
- `Get-VeeamServicesWithDependencies`: Retrieves all Veeam services that are not stopped or disabled, along with their dependencies.
- `Get-ServiceDependencies`: Returns the active dependent services for a given service.
- `Update-StatusDisplay`: Displays the current status of the services with real-time updates.
- `Stop-ServiceAndDependencies`: Stops a service and its dependencies in a safe order, displaying the process status.
.NOTES
Version: 1.0
Authors: Warren Held, ChatGPT o1-preview, Claude 3.5 Opus
Date: 10/16/2024
#>
# Function to get all Veeam services and their dependencies
function Get-VeeamServicesWithDependencies {
$veeamServices = Get-WmiObject Win32_Service | Where-Object {
$_.Name -like "*Veeam*" -and $_.State -ne "Stopped" -and $_.StartMode -ne "Disabled"
}
$result = @()
foreach ($service in $veeamServices) {
$dependencies = Get-ServiceDependencies -ServiceName $service.Name
$result += [PSCustomObject]@{
Name = $service.Name
DisplayName = $service.DisplayName
Dependencies = $dependencies
Status = $service.State
}
}
return $result
}
# Function to get service dependencies
function Get-ServiceDependencies {
param ([string]$ServiceName)
$service = Get-Service -Name $ServiceName
return $service.DependentServices | Where-Object { $_.Status -ne "Stopped" -and $_.StartType -ne "Disabled" } | Select-Object -ExpandProperty Name
}
# Function to update the status display
function Update-StatusDisplay {
param (
[array]$Services,
[string]$CurrentlyStoppingService = ""
)
Clear-Host
Write-Host "Veeam Services Status:"
Write-Host "----------------------"
# Calculate the maximum length of the DisplayName, with an upper limit
$maxNameLength = ($Services | ForEach-Object { $_.DisplayName.Length } | Measure-Object -Maximum).Maximum
$maxNameLength = [Math]::Min($maxNameLength, 60) # Set an upper limit to keep the output tidy
# Create a format string for consistent alignment
$formatString = "{0,-$($maxNameLength + 2)}{1}"
foreach ($service in $Services) {
$currentService = Get-Service -Name $service.Name -ErrorAction SilentlyContinue
if ($currentService) {
# Determine the status
$status = if ($service.Name -eq $CurrentlyStoppingService) { "Stopping" } else { $currentService.Status.ToString() }
# Choose the color based on the status
$statusColor = switch ($status) {
"Running" { "Yellow" }
"Stopped" { "Green" }
"Stopping" { "Cyan" }
default { "White" }
}
# Truncate or pad the DisplayName to fit the column width
$displayName = $service.DisplayName
if ($displayName.Length -gt $maxNameLength) {
$displayName = $displayName.Substring(0, $maxNameLength - 3) + "..."
} else {
$displayName = $displayName.PadRight($maxNameLength)
}
# Prepare the status text
$paddedStatus = "[{0}]" -f $status.PadRight(8).Substring(0, 8)
# Write the output with proper alignment
Write-Host ($formatString -f $displayName, $paddedStatus) -ForegroundColor $statusColor
}
}
}
# Function to stop a service and its dependencies
function Stop-ServiceAndDependencies {
param (
[string]$ServiceName,
[array]$AllServices
)
$service = $AllServices | Where-Object { $_.Name -eq $ServiceName }
if ($service -and (Get-Service -Name $ServiceName).Status -ne "Stopped") {
foreach ($dep in $service.Dependencies) {
Stop-ServiceAndDependencies -ServiceName $dep -AllServices $AllServices
}
try {
Update-StatusDisplay -Services $AllServices -CurrentlyStoppingService $ServiceName
Start-Sleep -Seconds 1 # Give time to see the "Stopping" status
Stop-Service -Name $ServiceName -Force -ErrorAction Stop
$stopTime = [DateTime]::Now.AddSeconds(30)
while ((Get-Service -Name $ServiceName).Status -ne 'Stopped' -and [DateTime]::Now -lt $stopTime) {
Start-Sleep -Milliseconds 500
Update-StatusDisplay -Services $AllServices -CurrentlyStoppingService $ServiceName
}
if ((Get-Service -Name $ServiceName).Status -ne 'Stopped') {
Write-Host "Service stop operation timed out after 30 seconds: $($service.DisplayName)" -ForegroundColor Red
}
} catch {
Write-Host "Failed to stop service: $($service.DisplayName). Error: $_" -ForegroundColor Red
}
Update-StatusDisplay -Services $AllServices
Start-Sleep -Seconds 1
}
}
# Main script (excerpt)
$veeamServices = Get-VeeamServicesWithDependencies
if ($veeamServices.Count -eq 0) {
Write-Host "No running Veeam services found that are not disabled." -ForegroundColor Yellow
exit
}
# Initial status display
Update-StatusDisplay -Services $veeamServices
# Stop services in reverse dependency order
$stopOrder = $veeamServices | Sort-Object { $_.Dependencies.Count } -Descending
foreach ($service in $stopOrder) {
Stop-ServiceAndDependencies -ServiceName $service.Name -AllServices $veeamServices
}
# Final status update
Update-StatusDisplay -Services $veeamServices
Write-Host "All eligible Veeam services have been processed." -ForegroundColor Green
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment