Collect evidence before restarting
A slow server or unreachable application does not immediately justify a restart. These 15 recipes help separate service, OS, memory, CPU, storage, and network issues. They do not change configuration, although DNS and TCP tests generate traffic. Select checks relevant to the symptom rather than repeatedly running everything.
Record the server, affected service, symptom onset with time zone, affected users, and recent changes. Preserve logs before intervening. This is targeted initial troubleshooting, not a complete assessment or a promise to resolve every incident without restarting.
Environment and permissions
Examples target local execution in Windows PowerShell 5.1 on Windows Server 2019, 2022, and 2025 with the corresponding Windows modules. PowerShell 7 may work depending on module compatibility; missing Windows modules or compatibility loading may yield deserialized objects. Check command availability before interpreting empty output.
These commands require no additional agent or commercial module; OS and application licensing still applies. Use an authorized identity with necessary read access. Some logs, processes, volumes, or counters may require elevation, but do not grant Domain Admin for this workflow.
- ContosoApp and app.contoso.example are fictional: substitute an existing service and an approved target.
- Preserve command errors. Access denied, no matching events, or missing commands do not mean healthy.
- For remote work, use an already approved management path. Do not enable remoting, change TrustedHosts, or bypass TLS to troubleshoot. Examples below remain local.
| Collection | Permissions to verify |
|---|---|
| CIM and services | WMI namespace read and service ACLs; provider-specific restrictions may apply. |
| Events | Log ACLs; Event Log Readers when sufficient. Security is not collected here. |
| Counters | Performance Monitor Users when sufficient; some objects require elevation. |
| Processes, volumes, networking | Local read; visibility may be partial. Elevate only an authorized check that requires it. |
1. Identify the OS and last boot
First confirm the server and version. Long uptime is not a fault diagnosis; compare last boot with symptom onset and changes. Local CIM uses the WMI provider’s read permissions.
Get-CimInstance Win32_OperatingSystem -ErrorAction Stop |
Select-Object CSName,Caption,Version,BuildNumber,LastBootUpTime,
@{Name='UptimeDays';Expression={[math]::Round(((Get-Date)-$_.LastBootUpTime).TotalDays,2)}}2. Find stopped automatic services
This is a candidate list, not a fault list: delayed start, triggers, and unused roles may explain stopped services. Confirm expected state and correlate ExitCode with events. Do not bulk-start services.
Get-CimInstance Win32_Service -Filter "StartMode='Auto' AND State<>'Running'" -ErrorAction Stop |
Select-Object Name,DisplayName,State,StartMode,StartName,ExitCode3. Inspect one service’s dependencies
ServicesDependedOn lists prerequisites; DependentServices lists consumers. Access depends on service-control-manager and service ACLs. An unknown name or access denial is a collection error, not evidence of a stopped service.
# Replace with an existing service NAME, not its display label
$serviceName = 'ContosoApp' # fictional
$service = Get-Service -Name $serviceName -ErrorAction Stop
$service | Select-Object Name,Status
$service.ServicesDependedOn | Select-Object Name,Status
$service.DependentServices | Select-Object Name,Status4. Read system errors and warnings
Filtering occurs at the source and collection is bounded. These are the latest 100 matching events, not the entire history. Access depends on the log: use Event Log Readers or appropriate delegation where possible. Distinguish no matches from access denial.
Get-WinEvent -FilterHashtable @{
LogName='System'; Level=1,2,3; StartTime=(Get-Date).AddHours(-2)
} -MaxEvents 100 -ErrorAction Stop |
Select-Object TimeCreated,ProviderName,Id,LevelDisplayName,Message5. Correlate application events
Look for repeated events around the symptom rather than an isolated entry. Products may log to dedicated channels or files instead. Protect exported messages: they may contain identifiers and sensitive paths.
Get-WinEvent -FilterHashtable @{
LogName='Application'; Level=1,2,3; StartTime=(Get-Date).AddHours(-2)
} -MaxEvents 100 -ErrorAction Stop |
Select-Object TimeCreated,ProviderName,Id,Message6. Measure visible and free memory
These properties are in KiB, hence division by 1MB to obtain GiB. Low free memory does not prove a leak: cache, workload, and commitment matter. Compare multiple periods with the server’s baseline.
Get-CimInstance Win32_OperatingSystem -ErrorAction Stop |
Select-Object @{Name='TotalGiB';Expression={[math]::Round($_.TotalVisibleMemorySize/1MB,2)}},
@{Name='FreeGiB';Expression={[math]::Round($_.FreePhysicalMemory/1MB,2)}},
@{Name='FreePct';Expression={[math]::Round(100*$_.FreePhysicalMemory/$_.TotalVisibleMemorySize,1)}}7. Sample CPU load
Three light observations are better than a single instant. LoadPercentage is a per-processor reading with its own averaging window, not a fine-grained per-core trace. VMs may require hypervisor metrics to reveal host contention.
1..3 | ForEach-Object {
Get-CimInstance Win32_Processor -ErrorAction Stop |
Select-Object @{Name='ObservedAt';Expression={Get-Date}},DeviceID,LoadPercentage
if ($_ -lt 3) { Start-Sleep -Seconds 2 }
}8. Sort processes by cumulative CPU time
CPU is cumulative processor time in seconds, not current utilization. An old process may lead without causing the present spike. StartTime or other properties may be inaccessible for protected processes; do not stop them to test a hypothesis.
Get-Process -ErrorAction Stop |
Sort-Object CPU -Descending |
Select-Object -First 10 Id,ProcessName,CPU,StartTime9. Compare resident and private process memory
WorkingSet64 describes resident physical memory; PrivateMemorySize64 describes private allocation, not necessarily all in RAM. Shared pages prevent simply summing working sets. Establish sustained growth before diagnosing a leak.
Get-Process -ErrorAction Stop |
Sort-Object WorkingSet64 -Descending |
Select-Object -First 10 Id,ProcessName,
@{Name='WorkingSetMiB';Expression={[math]::Round($_.WorkingSet64/1MB,1)}},
@{Name='PrivateMiB';Expression={[math]::Round($_.PrivateMemorySize64/1MB,1)}}10. Check volume free space
Include letterless volumes, mount points, and data volumes. Healthy does not establish full array or filesystem health. Reserve capacity depends on growth, transactions, and maintenance rather than a universal percentage.
Get-Volume -ErrorAction Stop |
Select-Object DriveLetter,FileSystemLabel,FileSystem,HealthStatus,OperationalStatus,
@{Name='SizeGiB';Expression={[math]::Round($_.Size/1GB,1)}},
@{Name='FreeGiB';Expression={[math]::Round($_.SizeRemaining/1GB,1)}}11. Observe disk latency over a short window
Counter names are localized; an invalid English path does not indicate disk failure. Use actual paths returned by the server. Performance Monitor Users or authorized elevation may be required. Correlate latency, workload, and underlying storage; very little I/O provides little diagnostic evidence.
# English counter names: replace with LOCALIZED paths on a French OS.
# Discover local paths with: Get-Counter -ListSet *
$paths = @(
'\PhysicalDisk(*)\Avg. Disk sec/Read',
'\PhysicalDisk(*)\Avg. Disk sec/Write'
)
Get-Counter -Counter $paths -SampleInterval 2 -MaxSamples 3 -ErrorAction Stop |
ForEach-Object { $_.CounterSamples |
Select-Object Timestamp,Path,
@{Name='LatencyMs';Expression={[math]::Round($_.CookedValue*1000,2)}},Status }12. Read IP and DNS configuration
-All includes virtual and disconnected interfaces. Compare addresses, gateways, and DNS servers with expected design. Multiple interfaces and routes can alter the selected source; do not change gateways or DNS during collection.
Get-NetIPConfiguration -All -Detailed -ErrorAction Stop13. Check one DNS name
This A lookup does not cover AAAA or SRV answers. Repeat for the actual record type and compare approved internal resolvers with -Server when needed. A single success does not rule out intermittency; preserve evidence before clearing caches.
# Fictional application name; use an approved real target
Resolve-DnsName -Name 'app.contoso.example' -Type A -DnsOnly -ErrorAction Stop |
Select-Object Name,Type,IPAddress,TTL14. Test a dependency’s TCP port
This actively attempts a connection: diagnostic-only, but real traffic that may be logged. TcpTestSucceeded validates neither TLS, authentication, nor an application transaction. Compare affected-client and server paths; this does not test UDP.
# Active network probe: approved host and one expected port only
Test-NetConnection -ComputerName 'app.contoso.example' -Port 443 -InformationLevel Detailed15. Map listening ports to process IDs
OwningProcess supports correlation, but processes can exit and PIDs can be reused. Local listening does not prove remote reachability through a firewall. Visibility depends on permissions; retain local address and IPv4/IPv6 context.
Get-NetTCPConnection -State Listen -ErrorAction Stop |
Select-Object LocalAddress,LocalPort,OwningProcess |
Sort-Object LocalPort
# Read the identified PID without assigning to the reserved $PID variable:
# Get-Process -Id 1234 -ErrorAction StopInterpret findings together
Fictional example: ContosoApp is running, its port listens, and the client TCP test succeeds, yet users still receive errors. Next inspect an application transaction, product logs, and dependencies rather than blindly restarting. Conversely, disk latency during backups warrants comparison with array, hypervisor, and backup-window metrics.
Point-in-time readings do not replace a time series. Record sample times and duration, compare normal and affected periods, and confirm clock consistency. Avoid universal CPU, memory, or disk thresholds: values need workload and expected response-time context.
Keep an actionable record
Export only necessary fields to an access-restricted location with defined retention. Do not post raw logs to a public ticket. Account names, addresses, paths, and application messages can expose sensitive data.
| Field | Fictional example |
|---|---|
| Scope and impact | srv-app-demo; application slow for one team |
| Time and evidence | 14:05 UTC; repeated app errors; port reachable |
| Hypothesis | Database dependency or storage |
| Next check | Correlate application logs with dependency metrics |
| Owner and validation | Application owner; representative transaction after intervention |
When a correction is necessary
- State a testable hypothesis and confirm impact with the service owner.
- Separate writes from collection: configuration changes, service or server restarts, patches, and network changes require their own intervention.
- Prepare a relevant backup or configuration baseline, maintenance window, pilot, and appropriate rollback. A screenshot does not replace tested restoration.
- After intervention, repeat relevant measurements and a representative user transaction. Compare with baseline and watch for recurrence.
- Do not globally disable antivirus, firewalls, or access controls to save time. Detailed tracing, captures, and dumps also need scope, duration, and data handling.
- If instability or impact is critical, follow incident and availability procedures: evidence preservation should not unreasonably delay necessary recovery.
Go beyond initial troubleshooting
These commands help scope an incident and hand over useful evidence. Recurring problems call for dependency, monitoring, and architecture analysis. Infrastructure consulting can connect observations to business risk and prepare measured corrections.