839 lines
30 KiB
Plaintext
839 lines
30 KiB
Plaintext
# 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\<Thumbprint>) `
|
||
-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*
|