# Windows SysAdmin PowerShell Command Reference > Practical commands for day-to-day Windows Server administration — remote connections, file transfers, system management, and more. --- ## Table of Contents 1. [Connecting to Remote Servers](#connecting-to-remote-servers) 2. [WinRM & PowerShell Remoting](#winrm--powershell-remoting) 3. [SSH from PowerShell](#ssh-from-powershell) 4. [Alternatives When WinRM Is Not Available](#alternatives-when-winrm-is-not-available) 5. [Remote File Copies & Transfers](#remote-file-copies--transfers) 6. [Remote System Administration](#remote-system-administration) 7. [Active Directory](#active-directory) 8. [DNS & DHCP Management](#dns--dhcp-management) 9. [Disk & Storage Management](#disk--storage-management) 10. [Event Logs](#event-logs) 11. [Windows Updates](#windows-updates) 12. [Certificates](#certificates) 13. [Scheduled Tasks (Remote)](#scheduled-tasks-remote) 14. [Registry (Remote)](#registry-remote) 15. [Firewall Management](#firewall-management) 16. [Performance & Health Checks](#performance--health-checks) 17. [Quick Diagnostic One-Liners](#quick-diagnostic-one-liners) --- ## Connecting to Remote Servers ### Enter-PSSession — Interactive Remote Shell ```powershell Enter-PSSession -ComputerName SERVER01 Enter-PSSession -ComputerName SERVER01 -Credential (Get-Credential) Enter-PSSession -ComputerName SERVER01 -Credential "DOMAIN\adminuser" ``` > Opens an **interactive PowerShell session** on the remote machine — similar to RDP but command-line only. Everything you type runs on the remote server. Type `Exit-PSSession` to disconnect. Requires WinRM to be enabled on the target. --- ### Invoke-Command — Run Commands Remotely (Non-Interactive) ```powershell # Run a single command on one server Invoke-Command -ComputerName SERVER01 -ScriptBlock { Get-Service } # Run against multiple servers at once Invoke-Command -ComputerName SERVER01, SERVER02, SERVER03 -ScriptBlock { Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 5 } # Pass a variable from your local machine into the remote block $serviceName = "Spooler" Invoke-Command -ComputerName SERVER01 -ScriptBlock { Get-Service -Name $using:serviceName } # Run a local script file on a remote server Invoke-Command -ComputerName SERVER01 -FilePath "C:\Scripts\CheckHealth.ps1" # Run with alternate credentials Invoke-Command -ComputerName SERVER01 -Credential "DOMAIN\admin" -ScriptBlock { hostname } ``` > Best for **running commands or scripts on one or many servers without an interactive session**. Great for bulk administration — patch checks, service restarts, config audits. The `$using:` prefix passes local variables into the remote script block. --- ### New-PSSession — Persistent Reusable Session ```powershell # Create a session (stays open) $session = New-PSSession -ComputerName SERVER01 -Credential "DOMAIN\admin" # Use the session multiple times Invoke-Command -Session $session -ScriptBlock { Get-Process } Invoke-Command -Session $session -ScriptBlock { Get-EventLog -LogName System -Newest 10 } # Copy files using the session (see also Copy-Item section) Copy-Item "C:\Scripts\deploy.ps1" -Destination "C:\Scripts\" -ToSession $session # Enter interactively when needed Enter-PSSession -Session $session # Clean up when done Remove-PSSession $session # Manage multiple open sessions Get-PSSession Get-PSSession | Remove-PSSession # Close all sessions ``` > Use persistent sessions when you need to **run multiple commands against the same server** without re-authenticating each time. Much more efficient than opening a new connection for every command. --- ## WinRM & PowerShell Remoting > **WinRM (Windows Remote Management)** is the underlying service that enables PowerShell remoting. It must be configured on the target server before `Enter-PSSession` or `Invoke-Command` will work. ### Enable WinRM on a Target Server ```powershell # Run this ON the target server (or via GPO) Enable-PSRemoting -Force # Verify WinRM is running Get-Service WinRM Test-WSMan -ComputerName SERVER01 ``` > `Enable-PSRemoting` starts the WinRM service, sets it to automatic startup, and configures the firewall rules. Run this once on any server you want to manage remotely. ### Configure Trusted Hosts (Workgroup / Non-Domain) ```powershell # If servers are NOT domain-joined, you must explicitly trust them Set-Item WSMan:\localhost\Client\TrustedHosts -Value "SERVER01" Set-Item WSMan:\localhost\Client\TrustedHosts -Value "SERVER01,SERVER02,10.0.0.5" Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" # Trust all (lab use only) # View current trusted hosts Get-Item WSMan:\localhost\Client\TrustedHosts ``` > Domain-joined machines trust each other automatically via Kerberos. In workgroup or cross-domain environments you must add the target to TrustedHosts. **Avoid `*` in production.** ### Test WinRM Connectivity ```powershell Test-WSMan SERVER01 # Quick WinRM connectivity test Test-WSMan SERVER01 -Credential (Get-Credential) # With credentials Test-NetConnection SERVER01 -Port 5985 # Test WinRM HTTP port Test-NetConnection SERVER01 -Port 5986 # Test WinRM HTTPS port ``` --- ## SSH from PowerShell > OpenSSH is built into Windows 10/Server 2019 and later. PowerShell 7+ also supports SSH-based remoting natively — useful for Linux servers or when WinRM is unavailable. ### Basic SSH Connection ```powershell ssh username@SERVER01 # Connect to server ssh admin@192.168.1.50 # Connect by IP ssh -p 2222 admin@SERVER01 # Custom port ssh admin@SERVER01 "hostname && uptime" # Run a command without interactive shell ``` ### SSH Key Management ```powershell # Generate an SSH key pair ssh-keygen -t ed25519 -C "admin@company.com" # Copy public key to remote server (Linux target) ssh-copy-id admin@linuxserver01 # For Windows targets, append to authorized_keys manually $key = Get-Content "$env:USERPROFILE\.ssh\id_ed25519.pub" Invoke-Command -HostName SERVER01 -UserName admin -ScriptBlock { Add-Content "C:\ProgramData\ssh\administrators_authorized_keys" $using:key } ``` ### SSH Config File (~/.ssh/config) ``` # Create C:\Users\YourName\.ssh\config to save connection shortcuts Host webserver HostName 10.0.0.20 User admin IdentityFile ~/.ssh/id_ed25519 Host jumpbox HostName bastion.company.com User sysadmin ForwardAgent yes ``` ```powershell ssh webserver # Now connects using saved config ``` > The SSH config file saves connection shortcuts so you don't need to retype IPs, usernames, and key paths every time. ### PowerShell 7 SSH Remoting (Invoke-Command over SSH) ```powershell # Works with both Windows and Linux targets Invoke-Command -HostName linux01 -UserName root -ScriptBlock { uname -a } Enter-PSSession -HostName SERVER01 -UserName admin # Create persistent SSH-based session $sshSession = New-PSSession -HostName SERVER01 -UserName admin Invoke-Command -Session $sshSession -ScriptBlock { Get-Process } ``` > PowerShell 7 SSH remoting is particularly useful for **managing Linux servers from Windows** using the same PowerShell commands you already know. --- ## Alternatives When WinRM Is Not Available > WinRM may be blocked by firewalls, disabled by policy, or not yet configured. These are your options. ### Option 1 — RDP (Remote Desktop) via PowerShell ```powershell # Launch RDP session to a server mstsc /v:SERVER01 mstsc /v:SERVER01 /admin # Connect to console session mstsc /v:SERVER01:3390 # Custom RDP port # Check if RDP port is open before connecting Test-NetConnection SERVER01 -Port 3389 # Enable RDP on a remote machine (requires admin share access) Invoke-Command -ComputerName SERVER01 -ScriptBlock { Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" ` -Name "fDenyTSConnections" -Value 0 Enable-NetFirewallRule -DisplayGroup "Remote Desktop" } ``` ### Option 2 — PsExec (Sysinternals) ```powershell # Download from Microsoft Sysinternals, then: .\PsExec.exe \\SERVER01 cmd .\PsExec.exe \\SERVER01 -u DOMAIN\admin -p password cmd .\PsExec.exe \\SERVER01 powershell -Command "Get-Service" .\PsExec.exe \\SERVER01 -s powershell # Run as SYSTEM account ``` > PsExec uses **admin shares (C$, IPC$) over SMB** — it does not need WinRM. Very useful for one-off command execution on servers where remoting isn't set up. Requires the admin$ share to be accessible and the Remote Registry service running. ### Option 3 — Admin Shares (UNC Path Access) ```powershell # Access a remote server's filesystem directly via admin shares Get-ChildItem \\SERVER01\C$\Logs Get-ChildItem \\SERVER01\C$\Windows\System32\winevt\Logs # Copy files to/from remote server Copy-Item \\SERVER01\C$\Logs\app.log C:\LocalLogs\ Copy-Item C:\Scripts\fix.ps1 \\SERVER01\C$\Scripts\ # Map a drive to a remote share New-PSDrive -Name S -PSProvider FileSystem -Root \\SERVER01\C$ -Credential "DOMAIN\admin" Get-ChildItem S:\Logs Remove-PSDrive S ``` > Admin shares (`\\server\C$`, `\\server\Admin$`) let you **browse and copy files without WinRM** as long as you have admin rights and SMB (port 445) is accessible. Works even on older servers. ### Option 4 — WMI / CIM (Windows Management Instrumentation) ```powershell # Query remote system info via WMI (older, uses DCOM — port 135 + dynamic ports) Get-WmiObject -Class Win32_OperatingSystem -ComputerName SERVER01 Get-WmiObject -Class Win32_Service -ComputerName SERVER01 | Where-Object {$_.State -eq "Running"} # CIM is the modern replacement for WMI (uses WinRM by default, falls back to DCOM) Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName SERVER01 Get-CimInstance -ClassName Win32_LogicalDisk -ComputerName SERVER01 Get-CimInstance -ClassName Win32_Service -ComputerName SERVER01 -Filter "State='Stopped'" # CIM over DCOM session (bypasses WinRM requirement) $dcomOption = New-CimSessionOption -Protocol Dcom $cimSession = New-CimSession -ComputerName SERVER01 -SessionOption $dcomOption -Credential "DOMAIN\admin" Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $cimSession Remove-CimSession $cimSession ``` > WMI/CIM is powerful for **querying system info, hardware details, and running processes** without WinRM. CIM over DCOM is the go-to fallback when WinRM is blocked — uses port 135 and dynamic high ports. ### Option 5 — Remote Registry ```powershell # Requires Remote Registry service running on target $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", "SERVER01") $key = $reg.OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion") $key.GetValue("ProductName") $reg.Close() ``` > Useful for **reading or writing registry keys on remote servers** without any remoting protocol. Requires the Remote Registry service to be started on the target. ### Option 6 — Scheduled Tasks (Push-and-Run Pattern) ```powershell # 1. Copy your script to the remote server via admin share Copy-Item C:\Scripts\RunMe.ps1 \\SERVER01\C$\Scripts\ # 2. Register a scheduled task to run it immediately $action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Scripts\RunMe.ps1" $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddSeconds(10) Register-ScheduledTask -TaskName "AdminRemoteRun" -Action $action -Trigger $trigger ` -RunLevel Highest -CimSession (New-CimSession SERVER01) ``` > The **push-and-run** pattern works when you can write files via admin shares but can't run commands interactively. Copy the script over SMB, schedule it to run, and collect output from a log file. ### Option 7 — PowerShell Web Access (PSWA) ```powershell # Install on a gateway server (run once, as Admin) Install-WindowsFeature WindowsPowerShellWebAccess -IncludeManagementTools Install-PswaWebApplication -UseTestCertificate Add-PswaAuthorizationRule -UserName "DOMAIN\admin" -ComputerName * -ConfigurationName * ``` > PSWA provides a **browser-based PowerShell console** over HTTPS — useful when you need remote admin access without VPN or direct RDP. Access via `https://gateway/pswa`. --- ## Remote File Copies & Transfers ### Copy-Item Over PSSession ```powershell # Copy FROM remote server TO local machine $s = New-PSSession -ComputerName SERVER01 Copy-Item -Path "C:\Logs\app.log" -Destination "C:\LocalLogs\" -FromSession $s # Copy FROM local machine TO remote server Copy-Item -Path "C:\Scripts\deploy.ps1" -Destination "C:\Scripts\" -ToSession $s # Copy an entire folder recursively Copy-Item -Path "C:\App\Config" -Destination "C:\App\" -ToSession $s -Recurse Remove-PSSession $s ``` > The most reliable way to copy files when WinRM is available. **Works through the existing PS session** — no file share needed. ### Robocopy — Robust File Copy for Servers ```powershell # Basic copy robocopy \\SERVER01\C$\Logs C:\LocalLogs # Mirror a folder (exact copy, deletes extras at destination) robocopy \\SERVER01\C$\WebApp C:\Backup\WebApp /MIR # Copy with logging robocopy \\SERVER01\C$\Data C:\Backup\Data /E /LOG:C:\Logs\robocopy.log # Copy only changed files, retry on failure robocopy \\SERVER01\C$\Data C:\Backup /E /Z /R:3 /W:5 # Useful flags # /E = include subdirectories (including empty) # /MIR = mirror (sync + delete) # /Z = restartable mode (resume interrupted transfers) # /R:3 = retry 3 times on failure # /W:5 = wait 5 seconds between retries # /LOG = write output to log file # /NP = no progress (cleaner log output) # /XF = exclude files, e.g. /XF *.tmp *.log # /XD = exclude directories ``` > Robocopy is the **standard tool for large or reliable file transfers** in Windows environments. Handles network interruptions, permissions, and large directory trees far better than Copy-Item. ### SCP — Secure Copy (SSH-based) ```powershell # Copy file TO a remote server scp C:\Scripts\deploy.sh admin@linuxserver01:/opt/scripts/ # Copy file FROM a remote server scp admin@linuxserver01:/var/log/app.log C:\Logs\ # Copy entire folder scp -r C:\Config admin@linuxserver01:/opt/config/ # Use a specific identity file scp -i ~/.ssh/id_ed25519 C:\file.txt admin@server01:/tmp/ ``` > SCP is ideal for **transferring files to/from Linux servers or any SSH-accessible host**. Available natively in Windows 10/Server 2019+. ### SFTP — Interactive File Transfer ```powershell sftp admin@linuxserver01 # Inside SFTP session: # ls - list remote files # lls - list local files # get remote.txt - download file # put local.txt - upload file # cd /var/log - change remote directory # lcd C:\Logs - change local directory # exit - quit ``` ### BitsTransfer — Background File Transfer ```powershell # Download a file in the background Start-BitsTransfer -Source "\\SERVER01\Share\largefile.zip" ` -Destination "C:\Downloads\largefile.zip" -Asynchronous # Check transfer status Get-BitsTransfer # Wait for all transfers to complete Get-BitsTransfer | Complete-BitsTransfer ``` > BITS (Background Intelligent Transfer Service) is designed for **large file transfers that can be paused and resumed**, throttled to not saturate the network. Originally designed for Windows Update, it's great for large deployment files. --- ## Remote System Administration ### Restart & Shutdown ```powershell Restart-Computer -ComputerName SERVER01 -Force Restart-Computer -ComputerName SERVER01 -Credential "DOMAIN\admin" -Force Restart-Computer -ComputerName SERVER01 -Wait -For PowerShell -Timeout 300 # Wait for reboot Restart-Computer -ComputerName SERVER01, SERVER02, SERVER03 -Force # Reboot multiple Stop-Computer -ComputerName SERVER01 -Force # Shutdown ``` ### Remote Service Management ```powershell # Check service status on remote server Get-Service -ComputerName SERVER01 -Name "Spooler" Get-Service -ComputerName SERVER01 | Where-Object {$_.Status -eq "Stopped"} # Start/stop/restart (Get-Service -ComputerName SERVER01 -Name "Spooler").Start() (Get-Service -ComputerName SERVER01 -Name "Spooler").Stop() # Via Invoke-Command for more control Invoke-Command -ComputerName SERVER01 -ScriptBlock { Restart-Service -Name "W3SVC" -Force Get-Service "W3SVC" | Select-Object Name, Status } ``` ### Remote Process Management ```powershell # List processes on remote server Get-Process -ComputerName SERVER01 Get-Process -ComputerName SERVER01 | Sort-Object CPU -Descending | Select-Object -First 10 # Kill a process remotely Stop-Process -Id 1234 -Force # (Must be inside Invoke-Command for remote) Invoke-Command -ComputerName SERVER01 -ScriptBlock { Stop-Process -Name "app.exe" -Force } ``` ### Check Who Is Logged On ```powershell # Query logged-in users (uses query.exe) Invoke-Command -ComputerName SERVER01 -ScriptBlock { query user } # Via WMI Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName SERVER01 | Select-Object UserName ``` ### Remote Hotfix / Patch Check ```powershell # Check installed patches on a remote server Get-HotFix -ComputerName SERVER01 Get-HotFix -ComputerName SERVER01 | Sort-Object InstalledOn -Descending | Select-Object -First 10 # Check multiple servers $servers = @("SERVER01", "SERVER02", "SERVER03") Invoke-Command -ComputerName $servers -ScriptBlock { Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 1 } | Select-Object PSComputerName, HotFixID, InstalledOn ``` --- ## Active Directory > Requires the `ActiveDirectory` module (part of RSAT — Remote Server Administration Tools). > Install: `Add-WindowsCapability -Name Rsat.ActiveDirectory* -Online` ```powershell Import-Module ActiveDirectory # Load the module # --- Users --- Get-ADUser -Identity jsmith # Find a user Get-ADUser -Filter {Name -like "John*"} # Search by name Get-ADUser -Identity jsmith -Properties * # All properties Get-ADUser -Filter {Enabled -eq $false} # Disabled accounts Get-ADUser -Filter * -SearchBase "OU=Staff,DC=corp,DC=com" # Users in specific OU # Unlock, enable, disable Unlock-ADAccount -Identity jsmith Enable-ADAccount -Identity jsmith Disable-ADAccount -Identity jsmith # Reset password Set-ADAccountPassword -Identity jsmith -Reset ` -NewPassword (ConvertTo-SecureString "NewP@ss123!" -AsPlainText -Force) Set-ADUser -Identity jsmith -ChangePasswordAtLogon $true # --- Groups --- Get-ADGroup -Identity "Domain Admins" Get-ADGroupMember -Identity "Domain Admins" Add-ADGroupMember -Identity "VPN_Users" -Members jsmith Remove-ADGroupMember -Identity "VPN_Users" -Members jsmith -Confirm:$false # --- Computers --- Get-ADComputer -Identity SERVER01 Get-ADComputer -Filter {OperatingSystem -like "*Server 2022*"} Get-ADComputer -Filter * | Where-Object {$_.LastLogonDate -lt (Get-Date).AddDays(-90)} # Stale # --- Password expiry --- Get-ADUser -Filter * -Properties PasswordLastSet, PasswordExpired | Where-Object { $_.PasswordExpired -eq $true } | Select-Object Name, PasswordLastSet ``` --- ## DNS & DHCP Management ```powershell # --- DNS (requires DnsServer module or RSAT) --- Get-DnsServerZone -ComputerName DNS01 # List DNS zones Get-DnsServerResourceRecord -ZoneName "corp.com" -ComputerName DNS01 # All records # Add an A record Add-DnsServerResourceRecordA -Name "webserver" -ZoneName "corp.com" ` -IPv4Address "10.0.0.50" -ComputerName DNS01 # Remove a record Remove-DnsServerResourceRecord -ZoneName "corp.com" -RRType "A" -Name "webserver" -Force # Flush DNS cache on remote server Invoke-Command -ComputerName SERVER01 -ScriptBlock { Clear-DnsClientCache } Invoke-Command -ComputerName DNS01 -ScriptBlock { Clear-DnsServerCache } # --- DHCP (requires DhcpServer module or RSAT) --- Get-DhcpServerv4Scope -ComputerName DHCP01 # List scopes Get-DhcpServerv4Lease -ScopeId 10.0.0.0 -ComputerName DHCP01 # Active leases Get-DhcpServerv4Reservation -ScopeId 10.0.0.0 -ComputerName DHCP01 # Reservations # Add a DHCP reservation Add-DhcpServerv4Reservation -ScopeId 10.0.0.0 -IPAddress 10.0.0.100 ` -ClientId "AA-BB-CC-DD-EE-FF" -Description "Printer" -ComputerName DHCP01 ``` --- ## Disk & Storage Management ```powershell # Local and remote disk info Get-PSDrive -PSProvider FileSystem # Drives and free space Get-Volume # Volume details Get-Disk # Physical disks Get-Partition # Partition layout # Remote disk check via CIM Get-CimInstance Win32_LogicalDisk -ComputerName SERVER01 | Select-Object DeviceID, @{N="SizeGB";E={[math]::Round($_.Size/1GB,1)}}, @{N="FreeGB";E={[math]::Round($_.FreeSpace/1GB,1)}}, @{N="Free%";E={[math]::Round($_.FreeSpace/$_.Size*100,1)}} # Check all servers for low disk space (under 15% free) $servers = @("SERVER01","SERVER02","SERVER03") Invoke-Command -ComputerName $servers -ScriptBlock { Get-PSDrive -PSProvider FileSystem | Where-Object { ($_.Free / ($_.Used + $_.Free)) -lt 0.15 } | Select-Object Name, Used, Free, @{N="Free%";E={[math]::Round($_.Free/($_.Used+$_.Free)*100,1)}} } | Select-Object PSComputerName, Name, Free, "Free%" ``` --- ## Event Logs ```powershell # Read event logs (local or remote) Get-EventLog -LogName System -Newest 50 Get-EventLog -LogName System -ComputerName SERVER01 -Newest 50 Get-EventLog -LogName Application -EntryType Error -Newest 20 Get-EventLog -LogName System -Source "Service Control Manager" -Newest 20 Get-EventLog -LogName System -After (Get-Date).AddHours(-24) # Last 24 hours # Modern method using Get-WinEvent (more powerful, supports all logs) Get-WinEvent -LogName System -MaxEvents 50 Get-WinEvent -LogName "Microsoft-Windows-TaskScheduler/Operational" -MaxEvents 20 # Filter by event ID Get-WinEvent -FilterHashtable @{ LogName = "System" Id = 7036 # Service started/stopped events StartTime = (Get-Date).AddDays(-1) } # Search for failed logins (Event ID 4625) Get-WinEvent -ComputerName SERVER01 -FilterHashtable @{ LogName = "Security" Id = 4625 StartTime = (Get-Date).AddHours(-24) } | Select-Object TimeCreated, Message # Export event log to CSV Get-EventLog -LogName System -Newest 100 | Select-Object TimeGenerated, EntryType, Source, Message | Export-Csv "C:\Logs\system-events.csv" -NoTypeInformation ``` --- ## Windows Updates > Requires `PSWindowsUpdate` module: `Install-Module PSWindowsUpdate -Force` ```powershell Import-Module PSWindowsUpdate # Check for available updates Get-WindowsUpdate # Install all updates Install-WindowsUpdate -AcceptAll -AutoReboot # Install on a remote server Invoke-WUJob -ComputerName SERVER01 -Script { Import-Module PSWindowsUpdate Install-WindowsUpdate -AcceptAll -AutoReboot | Out-File C:\Logs\WU.log } -Confirm:$false # Check update history Get-WUHistory | Select-Object -First 20 Get-WUHistory -ComputerName SERVER01 | Where-Object {$_.Result -ne "Succeeded"} # Check last update time without module Get-HotFix -ComputerName SERVER01 | Sort-Object InstalledOn -Descending | Select-Object -First 1 ``` --- ## Certificates ```powershell # List certificates in local stores Get-ChildItem Cert:\LocalMachine\My # Personal (server certs) Get-ChildItem Cert:\LocalMachine\Root # Trusted root CAs Get-ChildItem Cert:\CurrentUser\My # Current user certs # Find expiring certificates (within 60 days) Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.NotAfter -lt (Get-Date).AddDays(60) } | Select-Object Subject, Thumbprint, NotAfter # Check certs on a remote server Invoke-Command -ComputerName SERVER01 -ScriptBlock { Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.NotAfter -lt (Get-Date).AddDays(60) } | Select-Object Subject, NotAfter } # Export a certificate (no private key) Export-Certificate -Cert (Get-Item Cert:\LocalMachine\My\) ` -FilePath "C:\Certs\server.cer" # Import a certificate Import-Certificate -FilePath "C:\Certs\server.cer" -CertStoreLocation Cert:\LocalMachine\Root ``` --- ## Scheduled Tasks (Remote) ```powershell # List tasks on a remote server Get-ScheduledTask -CimSession (New-CimSession SERVER01) Get-ScheduledTask -CimSession (New-CimSession SERVER01) | Where-Object {$_.State -eq "Ready"} # Run a scheduled task immediately on a remote server Invoke-Command -ComputerName SERVER01 -ScriptBlock { Start-ScheduledTask -TaskName "DailyBackup" } # Check last run result of a task Get-ScheduledTaskInfo -TaskName "DailyBackup" -CimSession (New-CimSession SERVER01) | Select-Object LastRunTime, LastTaskResult, NextRunTime # 0 = success; anything else is an error code ``` --- ## Registry (Remote) ```powershell # Read a remote registry key via Invoke-Command Invoke-Command -ComputerName SERVER01 -ScriptBlock { Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | Select-Object ProductName, CurrentBuild, ReleaseId } # Write a registry value remotely Invoke-Command -ComputerName SERVER01 -ScriptBlock { Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" ` -Name "SMB1" -Value 0 -Type DWORD } # Check if SMBv1 is enabled across multiple servers $servers = @("SERVER01","SERVER02","SERVER03") Invoke-Command -ComputerName $servers -ScriptBlock { $val = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" ` -Name "SMB1" -ErrorAction SilentlyContinue [PSCustomObject]@{ Server = $env:COMPUTERNAME SMBv1 = if ($val.SMB1 -eq 0) { "Disabled" } else { "Enabled or Not Set" } } } ``` --- ## Firewall Management ```powershell # View rules Get-NetFirewallRule | Where-Object {$_.Enabled -eq "True" -and $_.Direction -eq "Inbound"} Get-NetFirewallRule -DisplayName "*Remote*" Get-NetFirewallRule | Where-Object {$_.Action -eq "Block"} # Add a rule New-NetFirewallRule -DisplayName "Allow HTTPS Inbound" ` -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow New-NetFirewallRule -DisplayName "Block Telnet" ` -Direction Inbound -Protocol TCP -LocalPort 23 -Action Block # Enable/disable a rule Enable-NetFirewallRule -DisplayName "Remote Desktop - User Mode (TCP-In)" Disable-NetFirewallRule -DisplayName "Remote Desktop - User Mode (TCP-In)" # Remote firewall management via Invoke-Command Invoke-Command -ComputerName SERVER01 -ScriptBlock { New-NetFirewallRule -DisplayName "Allow App Port 8080" ` -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow } ``` --- ## Performance & Health Checks ```powershell # CPU usage (top processes) Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name, Id, CPU # Memory usage Get-CimInstance Win32_OperatingSystem | Select-Object @{N="TotalGB";E={[math]::Round($_.TotalVisibleMemorySize/1MB,1)}}, @{N="FreeGB"; E={[math]::Round($_.FreePhysicalMemory/1MB,1)}} # Remote CPU and memory check across multiple servers $servers = @("SERVER01","SERVER02","SERVER03") Invoke-Command -ComputerName $servers -ScriptBlock { $os = Get-CimInstance Win32_OperatingSystem $cpu = (Get-CimInstance Win32_Processor | Measure-Object LoadPercentage -Average).Average [PSCustomObject]@{ Server = $env:COMPUTERNAME "CPU%" = $cpu "MemFreeGB" = [math]::Round($os.FreePhysicalMemory/1MB, 1) "Uptime" = (Get-Date) - $os.LastBootUpTime } } | Select-Object PSComputerName, "CPU%", MemFreeGB, Uptime # Check uptime (Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime # Remote uptime check Invoke-Command -ComputerName SERVER01 -ScriptBlock { $os = Get-CimInstance Win32_OperatingSystem $up = (Get-Date) - $os.LastBootUpTime "Uptime: {0} days, {1} hours, {2} min" -f $up.Days, $up.Hours, $up.Minutes } ``` --- ## Quick Diagnostic One-Liners ```powershell # Is a server reachable? Test-Connection SERVER01 -Count 2 -Quiet # What ports are listening on a server? Invoke-Command -ComputerName SERVER01 -ScriptBlock { Get-NetTCPConnection -State Listen | Sort-Object LocalPort } # Who has a file locked? Invoke-Command -ComputerName FILESERVER01 -ScriptBlock { Get-SmbOpenFile | Where-Object { $_.Path -like "*report.xlsx*" } } # What's sharing on a server? Get-SmbShare -CimSession (New-CimSession SERVER01) # Which servers in a list are online? $servers = @("SERVER01","SERVER02","SERVER03","SERVER04") $servers | ForEach-Object { [PSCustomObject]@{ Server = $_ Online = (Test-Connection $_ -Count 1 -Quiet) } } # Check if a service exists and is running on multiple servers $servers = @("SERVER01","SERVER02") Invoke-Command -ComputerName $servers -ScriptBlock { Get-Service -Name "W3SVC" -ErrorAction SilentlyContinue | Select-Object MachineName, Name, Status } # Get last reboot time across servers $servers = @("SERVER01","SERVER02","SERVER03") Invoke-Command -ComputerName $servers -ScriptBlock { (Get-CimInstance Win32_OperatingSystem).LastBootUpTime } | Select-Object PSComputerName, @{N="LastBoot";E={$_}} # Find large files on a remote server Invoke-Command -ComputerName SERVER01 -ScriptBlock { Get-ChildItem C:\Logs -Recurse -File | Sort-Object Length -Descending | Select-Object FullName, @{N="SizeMB";E={[math]::Round($_.Length/1MB,1)}} | Select-Object -First 20 } # Check pending reboots Invoke-Command -ComputerName SERVER01 -ScriptBlock { $rebootKeys = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending", "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" ) $pending = $rebootKeys | Where-Object { Test-Path $_ } if ($pending) { "REBOOT PENDING" } else { "No reboot required" } } ``` --- ## Connection Method Summary | Method | Requires | Port | Best For | |--------|----------|------|----------| | `Enter-PSSession` | WinRM enabled | 5985/5986 | Interactive remote shell | | `Invoke-Command` | WinRM enabled | 5985/5986 | Run scripts on one or many servers | | SSH (`ssh`) | OpenSSH service | 22 | Linux targets, no WinRM | | PsExec | Admin share, SMB | 445 | Quick commands, no WinRM setup | | Admin Shares (`\\server\C$`) | SMB, admin rights | 445 | File access without remoting | | CIM over DCOM | DCOM, RPC | 135 + dynamic | WMI queries without WinRM | | RDP (`mstsc`) | RDP enabled | 3389 | GUI access | | Remote Registry | Remote Registry svc | 445 | Registry edits without remoting | | PSWA | IIS + PSWA role | 443 | Browser-based shell, no VPN | --- *Last updated: 2026 | Tested on Windows Server 2016–2022 / PowerShell 5.1 and 7.x*