Disable a service and set the recovery to take no action.
Here’s the Powershell script:
<#
.SYNOPSIS
Disables a Windows service and resets its recovery options across multiple computers.
.DESCRIPTION
This script connects to multiple computers specified in a text file, disables the specified service,
resets its recovery options to prevent automatic restart, and generates a detailed status report.
The script uses parallel processing for improved performance when dealing with multiple computers.
.PARAMETER ServiceName
The name of the Windows service to disable. This is the service name (not display name).
Example: "Spooler" for Print Spooler service.
.PARAMETER ComputerListFile
Path to a text file containing computer names, one per line.
The file should contain valid computer names or IP addresses accessible on the network.
.PARAMETER ThrottleLimit
Maximum number of computers to process simultaneously. Default is 10.
Higher values may improve speed but consume more resources.
Valid range: 1-50
.PARAMETER OutputFile
Path and filename for the CSV report. Default creates a timestamped file.
Example: "ServiceStatusReport_20240115_143022.csv"
.PARAMETER ConnectionTimeout
Timeout in seconds for testing computer connectivity. Default is 2 seconds.
Increase this value for computers on slow networks.
.EXAMPLE
.\service-disable-set-ecovery-off-clause-ver.ps1 -ServiceName SiemensTiaAdminV3 -ComputerListFile C:\temp\test-service.txt -ThrottleLimit 10 -OutputFile c:\temp\service-tiaadmin.txt -ConnectionTimeout 2
Disables the SiemensTiaAdminV3 service and sets the recovery option to Take no Action
.EXAMPLE
.\Disable-ServiceOnComputers.ps1 -ServiceName "Spooler" -ComputerListFile "C:\computers.txt"
Disables the Print Spooler service on all computers listed in computers.txt using default settings.
.EXAMPLE
.\Disable-ServiceOnComputers.ps1 -ServiceName "Spooler" -ComputerListFile "C:\computers.txt" -ThrottleLimit 20 -OutputFile "C:\Reports\SpoolerStatus.csv"
Disables the Print Spooler service with custom throttle limit and output file location.
.EXAMPLE
.\Disable-ServiceOnComputers.ps1 -ServiceName "WSearch" -ComputerListFile "C:\servers.txt" -ConnectionTimeout 5
Disables Windows Search service with extended connection timeout for remote servers.
.NOTES
Author: System Administrator
Version: 2.0
Requires: PowerShell 5.1 or higher, Administrative privileges on target computers
The script requires:
- Network connectivity to target computers
- Administrative rights on remote computers
- Windows Remote Management (WinRM) enabled on target computers
- Appropriate firewall rules for remote management
.LINK
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-service
.OUTPUTS
CSV file containing:
- ComputerName: Name of the target computer
- ServiceName: Name of the service
- Status: Current service status (Running/Stopped/Error/Unreachable)
- StartType: Service startup type (Automatic/Manual/Disabled)
- RecoveryOptions: Service recovery actions in format "(action/delay); (action/delay); (action/delay)"
#>
#Requires -Version 5.1
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0, HelpMessage = "Enter the service name (not display name)")]
[ValidateNotNullOrEmpty()]
[string]$ServiceName,
[Parameter(Mandatory = $true, Position = 1, HelpMessage = "Path to text file containing computer names")]
[ValidateScript({
if (Test-Path $_ -PathType Leaf) { $true }
else { throw "Computer list file '$_' not found." }
})]
[string]$ComputerListFile,
[Parameter(HelpMessage = "Maximum number of concurrent connections (1-50)")]
[ValidateRange(1, 50)]
[int]$ThrottleLimit = 10,
[Parameter(HelpMessage = "Path for the output CSV file")]
[ValidateNotNullOrEmpty()]
[string]$OutputFile = "ServiceStatusReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv",
[Parameter(HelpMessage = "Connection timeout in seconds")]
[ValidateRange(1, 30)]
[int]$ConnectionTimeout = 2
)
# ========================
# FUNCTIONS
# ========================
function Parse-RecoveryActions {
<#
.SYNOPSIS
Parses sc.exe qfailure output to extract recovery actions in readable format
#>
param (
[string[]]$ScOutput
)
try {
# Find the line containing failure actions
$actionsLine = $ScOutput | Where-Object { $_ -match "FAILURE_ACTIONS\s*:" }
if (-not $actionsLine) {
return "(none/0); (none/0); (none/0)"
}
# Extract the actions from subsequent lines
$startIndex = [array]::IndexOf($ScOutput, $actionsLine)
$actions = @()
# Parse up to 3 recovery actions
for ($i = 1; $i -le 3; $i++) {
$line = $ScOutput[$startIndex + $i]
if ($line -match "(\w+)\s*--\s*delay:\s*(\d+)\s*milliseconds") {
$action = $matches[1].ToLower()
$delay = [int]$matches[2]
$actions += "($action/$delay)"
}
elseif ($line -match "(\w+)") {
$action = $matches[1].ToLower()
$actions += "($action/0)"
}
}
# If we found actions, join them; otherwise return default
if ($actions.Count -gt 0) {
return $actions -join "; "
}
else {
return "(none/0); (none/0); (none/0)"
}
}
catch {
return "(error parsing)"
}
}
function Disable-ServiceWithRecovery {
<#
.SYNOPSIS
Disables a service and resets its recovery options
#>
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$ServiceName,
[Parameter()]
[string]$ComputerName = $env:COMPUTERNAME
)
try {
# Check if service exists first
$service = Get-Service -Name $ServiceName -ErrorAction Stop
# Disable the service
Set-Service -Name $ServiceName -StartupType Disabled -ErrorAction Stop
# Reset recovery options
$scResult = & sc.exe "\\$ComputerName" failure $ServiceName reset=0 actions=none/0/none/0/none/0 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Failed to set recovery options: $scResult"
}
return @{
Success = $true
Message = "Service disabled and recovery options reset"
}
}
catch {
return @{
Success = $false
Message = $_.Exception.Message
}
}
}
function Get-ServiceDetails {
<#
.SYNOPSIS
Retrieves detailed service information including recovery options
#>
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$ServiceName,
[Parameter()]
[string]$ComputerName = $env:COMPUTERNAME
)
try {
$service = Get-Service -Name $ServiceName -ErrorAction Stop
$wmiService = Get-CimInstance -ClassName Win32_Service -Filter "Name='$ServiceName'" -ErrorAction Stop
# Get recovery options
$recoveryOutput = & sc.exe "\\$ComputerName" qfailure $ServiceName 2>&1
$recoveryActions = if ($LASTEXITCODE -eq 0) {
Parse-RecoveryActions -ScOutput $recoveryOutput
} else {
"(unable to retrieve)"
}
return [PSCustomObject]@{
ComputerName = $ComputerName
ServiceName = $ServiceName
Status = $service.Status
StartType = $wmiService.StartMode
RecoveryOptions = $recoveryActions
ErrorMessage = $null
}
}
catch {
return [PSCustomObject]@{
ComputerName = $ComputerName
ServiceName = $ServiceName
Status = "Error"
StartType = "N/A"
RecoveryOptions = "N/A"
ErrorMessage = $_.Exception.Message
}
}
}
# ========================
# MAIN SCRIPT
# ========================
# Initialize variables
$startTime = Get-Date
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "Service Management Script" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Service: $ServiceName" -ForegroundColor Yellow
Write-Host "Output: $OutputFile" -ForegroundColor Yellow
Write-Host "Throttle Limit: $ThrottleLimit" -ForegroundColor Yellow
Write-Host "========================================`n" -ForegroundColor Cyan
# Load and validate computer list
try {
$computers = Get-Content -Path $ComputerListFile |
Where-Object { $_ -and $_.Trim() } |
ForEach-Object { $_.Trim() } |
Select-Object -Unique
if ($computers.Count -eq 0) {
throw "No valid computer names found in '$ComputerListFile'"
}
Write-Host "Found $($computers.Count) unique computer(s) to process" -ForegroundColor Green
}
catch {
Write-Error "Failed to load computer list: $_"
exit 1
}
# Process computers in parallel batches
$results = [System.Collections.Concurrent.ConcurrentBag[PSCustomObject]]::new()
$processedCount = 0
# Create runspace pool for better performance
$runspacePool = [runspacefactory]::CreateRunspacePool(1, $ThrottleLimit)
$runspacePool.Open()
$jobs = foreach ($computer in $computers) {
$powershell = [powershell]::Create().AddScript({
param($Computer, $ServiceName, $ConnectionTimeout)
# Test connectivity
if (-not (Test-Connection -ComputerName $Computer -Count 1 -Quiet -TimeoutSeconds $ConnectionTimeout)) {
return [PSCustomObject]@{
ComputerName = $Computer
ServiceName = $ServiceName
Status = "Unreachable"
StartType = "N/A"
RecoveryOptions = "N/A"
ErrorMessage = "Computer is not reachable"
}
}
# Execute remote command
try {
$result = Invoke-Command -ComputerName $Computer -ErrorAction Stop -ScriptBlock {
param($ServiceName)
# Define functions in remote session
function Parse-RecoveryActions {
param ([string[]]$ScOutput)
try {
$actionsLine = $ScOutput | Where-Object { $_ -match "FAILURE_ACTIONS\s*:" }
if (-not $actionsLine) {
return "(none/0); (none/0); (none/0)"
}
$startIndex = [array]::IndexOf($ScOutput, $actionsLine)
$actions = @()
for ($i = 1; $i -le 3; $i++) {
$line = $ScOutput[$startIndex + $i]
if ($line -match "(\w+)\s*--\s*delay:\s*(\d+)\s*milliseconds") {
$action = $matches[1].ToLower()
$delay = [int]$matches[2]
$actions += "($action/$delay)"
}
elseif ($line -match "(\w+)") {
$action = $matches[1].ToLower()
$actions += "($action/0)"
}
}
if ($actions.Count -gt 0) {
return $actions -join "; "
}
else {
return "(none/0); (none/0); (none/0)"
}
}
catch {
return "(error parsing)"
}
}
function Disable-ServiceWithRecovery {
param ([string]$ServiceName)
try {
Set-Service -Name $ServiceName -StartupType Disabled -ErrorAction Stop
& sc.exe failure $ServiceName reset=0 actions=none/0/none/0/none/0 2>&1 | Out-Null
return @{ Success = $true; Message = "Success" }
}
catch {
return @{ Success = $false; Message = $_.Exception.Message }
}
}
function Get-ServiceDetails {
param ([string]$ServiceName)
try {
$service = Get-Service -Name $ServiceName -ErrorAction Stop
$wmiService = Get-CimInstance Win32_Service -Filter "Name='$ServiceName'" -ErrorAction Stop
$recoveryOutput = & sc.exe qfailure $ServiceName 2>&1
$recoveryActions = if ($LASTEXITCODE -eq 0) {
Parse-RecoveryActions -ScOutput $recoveryOutput
} else {
"(unable to retrieve)"
}
return [PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
ServiceName = $ServiceName
Status = $service.Status
StartType = $wmiService.StartMode
RecoveryOptions = $recoveryActions
ErrorMessage = $null
}
}
catch {
return [PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
ServiceName = $ServiceName
Status = "Error"
StartType = "N/A"
RecoveryOptions = "N/A"
ErrorMessage = $_.Exception.Message
}
}
}
# Disable service and get status
$disableResult = Disable-ServiceWithRecovery -ServiceName $ServiceName
$statusResult = Get-ServiceDetails -ServiceName $ServiceName
if (-not $disableResult.Success) {
$statusResult.ErrorMessage = $disableResult.Message
}
return $statusResult
} -ArgumentList $ServiceName
return $result
}
catch {
return [PSCustomObject]@{
ComputerName = $Computer
ServiceName = $ServiceName
Status = "Error"
StartType = "N/A"
RecoveryOptions = "N/A"
ErrorMessage = $_.Exception.Message
}
}
}).AddArgument($computer).AddArgument($ServiceName).AddArgument($ConnectionTimeout)
$powershell.RunspacePool = $runspacePool
[PSCustomObject]@{
PowerShell = $powershell
Handle = $powershell.BeginInvoke()
}
}
# Wait for jobs and collect results
$totalComputers = $computers.Count
foreach ($job in $jobs) {
$result = $job.PowerShell.EndInvoke($job.Handle)
if ($result) {
$results.Add($result)
}
$job.PowerShell.Dispose()
$processedCount++
$percentComplete = [math]::Round(($processedCount / $totalComputers) * 100, 2)
Write-Progress -Activity "Processing Computers" -Status "$processedCount of $totalComputers complete" -PercentComplete $percentComplete
}
$runspacePool.Close()
$runspacePool.Dispose()
# Convert results to array and sort
$finalResults = $results.ToArray() | Sort-Object ComputerName
# Display summary
Write-Host "`nProcessing complete!" -ForegroundColor Green
$summary = $finalResults | Group-Object Status | Select-Object Name, Count
Write-Host "`nSummary:" -ForegroundColor Cyan
$summary | Format-Table -AutoSize
# Display detailed results
Write-Host "`nDetailed Results:" -ForegroundColor Cyan
$finalResults | Format-Table -AutoSize
# Export to CSV
try {
$finalResults | Export-Csv -Path $OutputFile -NoTypeInformation -Force
Write-Host "`n✅ Report saved to: $OutputFile" -ForegroundColor Green
# Calculate execution time
$executionTime = (Get-Date) - $startTime
Write-Host "Total execution time: $($executionTime.ToString('mm\:ss'))" -ForegroundColor Yellow
}
catch {
Write-Error "Failed to export results: $_"
}
Usage Examples
# Run against computers in C:\Temp\computers.txt
.\ServiceControl.ps1 -ServiceName "Spooler" -ComputerListFile "C:\Temp\computers.txt"
# Run with custom throttle and output file
.\ServiceControl.ps1 -ServiceName "wuauserv" -ComputerListFile "C:\Temp\computers.txt" -Throttle 20 -OutFile "C:\Reports\UpdateServiceReport.csv"
-
If a PC fails ping, it’s skipped — script continues smoothly.
-
The report still contains that PC with “Unreachable” in the Status column.
✅ Example output if PC2 is offline:
ComputerName ServiceName Status StartType RecoveryOptions
------------ ----------- ------ --------- ---------------
PC1 Spooler Stopped Disabled (none/0); (none/0); (none/0)
PC2 Spooler Unreachable N/A N/A
PC3 Spooler Stopped Disabled (none/0); (none/0); (none/0)
⚡ Parallelized Script (PowerShell 7+)
param(
[Parameter(Mandatory = $true)]
[string]$ServiceName,
[Parameter(Mandatory = $true)]
[string]$ComputerListFile,
[int]$Throttle = 10,
[string]$OutFile = "ServiceStatusReport.csv"
)
# ========================
# FUNCTIONS
# ========================
function Disable-ServiceAndSetRecovery {
param ([string]$ServiceName)
try {
Set-Service -Name $ServiceName -StartupType Disabled -ErrorAction Stop
sc.exe failure $ServiceName reset=0 actions=none/0/none/0/none/0 | Out-Null
return $true
}
catch {
return $false
}
}
function Get-ServiceRecoveryStatus {
param ([string]$ServiceName)
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
$recovery = sc.exe qfailure $ServiceName
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
ServiceName = $ServiceName
Status = $service.Status
StartType = (Get-CimInstance Win32_Service -Filter "Name='$ServiceName'").StartMode
RecoveryOptions = ($recovery | Select-String "FAILURE_ACTIONS" -Context 0,3).Context.PostContext -join "; "
}
}
# ========================
# MAIN SCRIPT
# ========================
if (-not (Test-Path $ComputerListFile)) {
Write-Error "❌ Computer list file '$ComputerListFile' not found."
exit
}
$Computers = Get-Content $ComputerListFile | Where-Object { $_.Trim() }
if (-not $Computers) {
Write-Error "❌ No computer names found in '$ComputerListFile'."
exit
}
$Total = $Computers.Count
$Processed = 0
$Sync = [System.Collections.Concurrent.ConcurrentBag[object]]::new()
$Computers | ForEach-Object -Parallel {
param($ServiceName, $Total, $Sync)
$Computer = $_
if (-not (Test-Connection -ComputerName $Computer -Count 1 -Quiet)) {
$Sync.Add([PSCustomObject]@{
ComputerName = $Computer
ServiceName = $ServiceName
Status = "Unreachable"
StartType = "N/A"
RecoveryOptions = "N/A"
})
return
}
try {
function Disable-ServiceAndSetRecovery {
param([string]$ServiceName)
try {
Set-Service -Name $ServiceName -StartupType Disabled -ErrorAction Stop
sc.exe failure $ServiceName reset=0 actions=none/0/none/0/none/0 | Out-Null
return $true
}
catch { return $false }
}
function Get-ServiceRecoveryStatus {
param([string]$ServiceName)
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
$recovery = sc.exe qfailure $ServiceName
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
ServiceName = $ServiceName
Status = $service.Status
StartType = (Get-CimInstance Win32_Service -Filter "Name='$ServiceName'").StartMode
RecoveryOptions = ($recovery | Select-String "FAILURE_ACTIONS" -Context 0,3).Context.PostContext -join "; "
}
}
if (Disable-ServiceAndSetRecovery -ServiceName $ServiceName) {
$result = Get-ServiceRecoveryStatus -ServiceName $ServiceName
} else {
$result = [PSCustomObject]@{
ComputerName = $Computer
ServiceName = $ServiceName
Status = "Failed"
StartType = "N/A"
RecoveryOptions = "N/A"
}
}
$Sync.Add($result)
}
catch {
$Sync.Add([PSCustomObject]@{
ComputerName = $Computer
ServiceName = $ServiceName
Status = "Error: $_"
StartType = "N/A"
RecoveryOptions = "N/A"
})
}
# Update progress (approximate since parallel)
[System.Threading.Interlocked]::Increment([ref]$using:Processed) | Out-Null
$Percent = [math]::Round(($using:Processed / $Total) * 100, 2)
Write-Progress -Activity "Processing Computers" -Status "$using:Processed of $Total complete" -PercentComplete $Percent
} -ArgumentList $ServiceName, $Total, $Sync -ThrottleLimit $Throttle
# ========================
# OUTPUT
# ========================
$Results = $Sync.ToArray() | Sort-Object ComputerName
$Results | Format-Table -AutoSize
$Results | Export-Csv $OutFile -NoTypeInformation
Write-Host "✅ Report saved to $OutFile"
🚀 Key Advantages
-
No manual job management — ForEach-Object -Parallel handles parallelism.
-
Throttle control built-in via -ThrottleLimit.
-
Thread-safe collection (ConcurrentBag) to aggregate results safely from parallel threads.
-
Cleaner progress tracking with Interlocked.Increment.
-
Better error reporting (explicit “Failed” vs “Unreachable” vs “Error: …”)