1. [Module Loading Explained](#module-loading-explained)
2. [PowerShell Session Management](#powershell-session-management)
3. [Essential Aliases & Shortcuts](#essential-aliases--shortcuts)
4. [Error Handling Tips](#error-handling-tips)
5. [Output Formatting Tricks](#output-formatting-tricks)
6. [Performance & Memory Tips](#performance--memory-tips)
7. [Remote Management](#remote-management)
8. [Scripting Best Practices](#scripting-best-practices)
9. [Common Gotchas](#common-gotchas)
10. [Productivity Hacks](#productivity-hacks)
---
✅ NEED TO IMPORT:
Enter-PSSession
)❌ DON'T NEED TO IMPORT:
# Check if IIS modules are loaded
gmo Web*,IIS*
# Expected output if loaded:
# ModuleType Name ExportedCommands
# ---------- ---- ----------------
# Manifest IISAdministration {Get-IISAppPool, Start-IISCommitDelay, Stop-IISCommitDelay, Get-IISConfigCollection...}
# Manifest WebAdministration {Add-WebConfiguration, Add-WebConfigurationLock, Add-WebConfigurationProperty, Backup-WebConfiguration...}
# If empty, modules aren't loaded - run import
ipmo WebAdministration,IISAdministration
#### Option 1: PowerShell Profile (Recommended)
# Check if profile exists
Test-Path $PROFILE
# Output: True/False
# If False, create profile
if(!(Test-Path $PROFILE)) {
New-Item $PROFILE -ItemType File -Force
Write-Host "Profile created at: $PROFILE" -ForegroundColor Green
}
# Add auto-import to profile
Add-Content $PROFILE @"
# Auto-load IIS modules
Import-Module WebAdministration,IISAdministration -ErrorAction SilentlyContinue
if(Get-Module WebAdministration) {
Write-Host "IIS modules loaded" -ForegroundColor Green
}
"@
# Reload current session
. $PROFILE
#### Option 2: Custom Batch Launcher
Create iis-ps.bat
:
@echo off
title IIS PowerShell
powershell -NoExit -Command "Import-Module WebAdministration,IISAdministration; Clear-Host; Write-Host 'IIS PowerShell Ready' -ForegroundColor Cyan; Write-Host 'Modules loaded: WebAdministration, IISAdministration' -ForegroundColor Green"
#### Option 3: Quick Load Function
# Add to profile or run manually
function iis {
ipmo WebAdministration,IISAdministration -ea 0
Write-Host "IIS modules loaded" -ForegroundColor Green
Get-IISSite | select Name,State | ft -Auto
}
# Usage: just type 'iis' and hit enter
#### Option 4: Module Auto-Import (Windows 10/Server 2016+)
# Enable auto-import (one-time setup)
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
# This allows PowerShell to auto-load modules when you use their commands
---
Regular PowerShell Console:
PowerShell ISE:
VS Code with PowerShell Extension:
Windows Terminal + PowerShell:
# Keep session alive during long operations
$host.UI.RawUI.WindowTitle = "IIS Management - $(Get-Date)"
# Prevent accidental closure
function exit {
$response = Read-Host "Are you sure you want to exit? (y/N)"
if($response -eq 'y') { Exit }
}
# Save command history for later
Get-History | Export-Csv "C:\temp\ps-history-$(Get-Date -f 'yyyyMMdd').csv"
---
# File/Directory operations
ls = Get-ChildItem (gci)
dir = Get-ChildItem (gci)
cd = Set-Location (sl)
pwd = Get-Location (gl)
cat = Get-Content (gc)
cp = Copy-Item (copy)
mv = Move-Item (move)
rm = Remove-Item (del)
# Object manipulation
select = Select-Object
where = Where-Object (?)
foreach = ForEach-Object (%)
sort = Sort-Object
group = Group-Object
measure = Measure-Object
# Common commands
gsv = Get-Service
gps = Get-Process
gwmi = Get-WmiObject
gcim = Get-CimInstance
# Formatting
ft = Format-Table
fl = Format-List
fw = Format-Wide
# Add these to your profile
Set-Alias gwc Get-WebConfiguration
Set-Alias swc Set-WebConfiguration
Set-Alias sites Get-IISSite
Set-Alias pools Get-IISAppPool
Set-Alias bindings Get-IISSiteBinding
# Usage examples:
# sites | select Name,State
# pools | where State -eq Stopped
# gwc "system.webserver/defaultDocument" "IIS:\Sites\MySite"
# Add to profile for quick directory changes
function logs { cd "C:\inetpub\logs\LogFiles" }
function wwwroot { cd "C:\inetpub\wwwroot" }
function iisconfig { cd "$env:windir\System32\inetsrv\config" }
function temp { cd $env:TEMP }
# Usage: just type 'logs' to jump to log directory
---
# Use -ErrorAction parameter (or -ea shorthand)
Get-IISSite "NonExistentSite" -ea SilentlyContinue # Returns nothing if not found
Get-IISSite "NonExistentSite" -ea Stop # Throws terminating error
Get-IISSite "NonExistentSite" -ea Continue # Shows error but continues
# Try-catch for better error handling
try {
$site = Get-IISSite "MySite"
Write-Host "Site found: $($site.Name)" -ForegroundColor Green
} catch {
Write-Host "Site not found: $($_.Exception.Message)" -ForegroundColor Red
}
# Check if site exists before operations
function Test-IISSiteExists {
param([string]$SiteName)
try {
Get-IISSite $SiteName -ea Stop | Out-Null
return $true
} catch {
return $false
}
}
# Safe site operations
if(Test-IISSiteExists "MySite") {
Stop-IISSite "MySite"
Write-Host "Site stopped" -ForegroundColor Green
} else {
Write-Host "Site 'MySite' not found" -ForegroundColor Yellow
}
# Handle multiple sites safely
$sites = @("Site1", "Site2", "Site3")
foreach($site in $sites) {
try {
Get-IISSite $site | Stop-IISSite
Write-Host "✓ Stopped: $site" -ForegroundColor Green
} catch {
Write-Host "✗ Failed: $site - $($_.Exception.Message)" -ForegroundColor Red
}
}
---
# Auto-size columns
Get-IISSite | ft -AutoSize
# Select specific columns
Get-IISSite | select Name,State,ID | ft
# Custom column names and expressions
Get-IISSite | select Name,State,@{n="App Pool";e={$_.Applications[0].ApplicationPoolName}} | ft
# Wrap long content
Get-IISSite | ft -Wrap
# Group output
Get-IISSite | sort State | ft -GroupBy State
# Full details in list format
Get-IISSite "MySite" | fl
# Specific properties
Get-IISSite "MySite" | fl Name,State,ID,Applications
# Custom formatting
Get-IISAppPool | fl Name,State,@{n="Identity";e={$_.ProcessModel.IdentityType}},@{n=".NET Version";e={$_.ManagedRuntimeVersion}}
# Interactive table (GUI)
Get-IISSite | Out-GridView
# Select multiple items and pass to pipeline
Get-IISSite | Out-GridView -PassThru | Stop-IISSite
# Filter and select app pools
Get-IISAppPool | Out-GridView -Title "Select App Pools to Restart" -PassThru | Restart-WebAppPool
# Export to CSV
Get-IISSite | Export-Csv "C:\temp\iis-sites.csv" -NoTypeInformation
# Export to HTML report
Get-IISSite | ConvertTo-Html -Property Name,State,ID | Out-File "C:\temp\iis-report.html"
# Export to JSON
Get-IISSite | ConvertTo-Json | Out-File "C:\temp\iis-sites.json"
# Copy to clipboard
Get-IISSite | ft | clip
---
# BAD: Slow for many sites
Get-IISSite | ForEach-Object { Get-WebConfiguration "system.webserver/defaultDocument" "IIS:\Sites\$($_.Name)" }
# GOOD: Faster approach
$sites = Get-IISSite
foreach($site in $sites) {
try {
gwc "system.webserver/defaultDocument" "IIS:\Sites\$($site.Name)" -ea Stop
} catch {
Write-Warning "Cannot get config for $($site.Name)"
}
}
# BETTER: Parallel processing for many sites (PowerShell 7+)
$sites | ForEach-Object -Parallel {
Import-Module WebAdministration
gwc "system.webserver/defaultDocument" "IIS:\Sites\$($_.Name)" -ea SilentlyContinue
} -ThrottleLimit 10
# Clear variables when done with large datasets
$largeSiteData = Get-IISSite | foreach { /* complex processing */ }
# Use $largeSiteData...
Remove-Variable largeSiteData
# Use -OutVariable to avoid storing large objects
Get-Content "C:\inetpub\logs\LogFiles\W3SVC1\huge.log" | Where-Object {$_ -like "*error*"} -OutVariable errors
# $errors now contains just the filtered results
# Garbage collection for long-running scripts
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
# Show progress for long operations
$sites = Get-IISSite
$total = $sites.Count
$current = 0
foreach($site in $sites) {
$current++
Write-Progress -Activity "Processing Sites" -Status "Site: $($site.Name)" -PercentComplete (($current / $total) * 100)
# Your processing here
Start-Sleep 1 # Simulate work
}
Write-Progress -Activity "Processing Sites" -Completed
---
# Enable WinRM on target server (run on server)
Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "ServerIP" -Force
# Connect from management machine
$cred = Get-Credential
$session = New-PSSession -ComputerName "ServerIP" -Credential $cred
# Import IIS modules in remote session
Invoke-Command -Session $session -ScriptBlock {
Import-Module WebAdministration,IISAdministration
}
# Run IIS commands remotely
Invoke-Command -Session $session -ScriptBlock { Get-IISSite }
# Interactive remote session
Enter-PSSession $session
# Now you're "inside" the remote server
# Run IIS commands normally
# Type 'exit' to return to local machine
# Clean up
Remove-PSSession $session
# Function to get remote IIS info
function Get-RemoteIISInfo {
param(
[string]$ComputerName,
[pscredential]$Credential
)
$scriptBlock = {
Import-Module WebAdministration,IISAdministration -ea 0
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
IISVersion = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\InetStp").MajorVersion
Sites = (Get-IISSite | measure).Count
AppPools = (Get-IISAppPool | measure).Count
RunningPools = (Get-IISAppPool | where State -eq Started | measure).Count
Services = (Get-Service W3SVC,WAS | select Name,Status)
}
}
if($Credential) {
Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock $scriptBlock
} else {
Invoke-Command -ComputerName $ComputerName -ScriptBlock $scriptBlock
}
}
# Usage:
# Get-RemoteIISInfo -ComputerName "Server01"
# Get-RemoteIISInfo -ComputerName "Server01" -Credential (Get-Credential)
---
function Restart-IISSiteComplete {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[ValidateNotNullOrEmpty()]
[string]$SiteName,
[int]$WaitSeconds = 3,
[switch]$Force
)
# Validate site exists
try {
$site = Get-IISSite $SiteName -ErrorAction Stop
} catch {
throw "Site '$SiteName' not found"
}
# Rest of function...
}
function Stop-IISSiteWithLogging {
[CmdletBinding()]
param([string]$SiteName)
$logFile = "C:\temp\iis-operations.log"
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
try {
Write-Verbose "Stopping site: $SiteName"
Stop-IISSite $SiteName
$logEntry = "$timestamp - SUCCESS - Stopped site: $SiteName"
Add-Content $logFile $logEntry
Write-Host "✓ $SiteName stopped" -ForegroundColor Green
} catch {
$logEntry = "$timestamp - ERROR - Failed to stop site: $SiteName - $($_.Exception.Message)"
Add-Content $logFile $logEntry
Write-Error "Failed to stop $SiteName`: $($_.Exception.Message)"
}
}
# Usage with verbose output:
# Stop-IISSiteWithLogging "MySite" -Verbose
# Store IIS configuration in hash tables
$iisConfig = @{
Sites = @(
@{
Name = "Website1"
Path = "C:\inetpub\wwwroot\site1"
Port = 80
AppPool = "Site1Pool"
},
@{
Name = "Website2"
Path = "C:\inetpub\wwwroot\site2"
Port = 8080
AppPool = "Site2Pool"
}
)
AppPools = @(
@{
Name = "Site1Pool"
RuntimeVersion = "v4.0"
Identity = "ApplicationPoolIdentity"
},
@{
Name = "Site2Pool"
RuntimeVersion = "v4.0"
Identity = "ApplicationPoolIdentity"
}
)
}
# Function to apply configuration
function Deploy-IISConfiguration {
param($Config)
# Create app pools
foreach($pool in $Config.AppPools) {
if(!(Get-IISAppPool $pool.Name -ea 0)) {
New-WebAppPool $pool.Name
Set-ItemProperty "IIS:\AppPools\$($pool.Name)" managedRuntimeVersion $pool.RuntimeVersion
Write-Host "Created app pool: $($pool.Name)" -ForegroundColor Green
}
}
# Create sites
foreach($site in $Config.Sites) {
if(!(Get-IISSite $site.Name -ea 0)) {
New-IISSite -Name $site.Name -BindingInformation "*:$($site.Port):" -PhysicalPath $site.Path
Set-ItemProperty "IIS:\Sites\$($site.Name)" applicationPool $site.AppPool
Write-Host "Created site: $($site.Name)" -ForegroundColor Green
}
}
}
# Deploy the configuration
Deploy-IISConfiguration $iisConfig
---
# Always check if running as administrator
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Error "This script requires Administrator privileges. Please run PowerShell as Administrator."
exit 1
}
# Check IIS features are installed
$iisFeatures = Get-WindowsFeature | where {$_.Name -like "*IIS*" -and $_.InstallState -eq "Installed"}
if($iisFeatures.Count -eq 0) {
Write-Error "IIS is not installed on this server"
exit 1
}
# Always use full paths, not relative paths
$sitePath = "C:\inetpub\wwwroot\mysite" # Good
$sitePath = ".\mysite" # Bad - depends on current directory
# Check paths exist before using
if(!(Test-Path $sitePath)) {
Write-Error "Site path does not exist: $sitePath"
return
}
# Handle UNC paths correctly
$uncPath = "\\server\share\website"
if($uncPath -like "\\*") {
# Special handling for UNC paths
Test-Path $uncPath -Credential $cred
}
# BAD: This doesn't work as expected
Get-IISSite | Stop-IISSite # Stop-IISSite doesn't accept pipeline input
# GOOD: Use ForEach-Object
Get-IISSite | foreach { Stop-IISSite $_.Name }
# BETTER: Filter first, then process
Get-IISSite | where State -eq "Started" | foreach { Stop-IISSite $_.Name }
# Case-sensitive vs case-insensitive
$siteName = "MyWebsite"
# BAD: Case sensitive (might fail)
if($siteName -eq "mywebsite") { }
# GOOD: Case insensitive
if($siteName -ieq "mywebsite") { }
if($siteName.ToLower() -eq "mywebsite") { }
# Pattern matching
if($siteName -like "*web*") { } # Case insensitive wildcard
if($siteName -match "^My.*") { } # Regex match
---
# One-liner to check all sites
Get-IISSite | select Name,State,@{n="AppPool";e={(Get-IISAppPool $_.Applications[0].ApplicationPoolName).State}},@{n="Path";e={$_.Applications[0].VirtualDirectories[0].PhysicalPath}},@{n="PathExists";e={Test-Path $_.Applications[0].VirtualDirectories[0].PhysicalPath}} | ft -Auto
# Start all stopped sites
Get-IISSite | where State -eq Stopped | foreach { Start-IISSite $_.Name; Write-Host "Started: $($_.Name)" -ForegroundColor Green }
# Restart all app pools containing "test"
Get-IISAppPool | where Name -like "*test*" | foreach { Restart-WebAppPool $_.Name; Write-Host "Restarted: $($_.Name)" -ForegroundColor Yellow }
# Stop sites on specific ports
Get-IISSiteBinding | where BindingInformation -like "*:8080:*" | foreach { Stop-IISSite $_.SiteName; Write-Host "Stopped site on port 8080: $($_.SiteName)" }
# Save commonly used commands as functions in your profile
function iis-status {
Write-Host "=== IIS Quick Status ===" -ForegroundColor Cyan
Write-Host "Services:" -ForegroundColor Yellow
gsv W3SVC,WAS | ft Name,Status -Auto
Write-Host "Sites:" -ForegroundColor Yellow
Get-IISSite | group State | ft Name,Count -Auto
Write-Host "App Pools:" -ForegroundColor Yellow
Get-IISAppPool | group State | ft Name,Count -Auto
}
function iis-errors {
Write-Host "Recent IIS Errors:" -ForegroundColor Red
gel Application -EntryType Error -Newest 10 | where Source -like "*IIS*" | select TimeGenerated,Source,Message | ft -Wrap
}
function iis-recycle-all {
Get-IISAppPool | foreach {
Restart-WebAppPool $_.Name
Write-Host "Recycled: $($_.Name)" -ForegroundColor Green
}
}
# Usage: just type function name
# iis-status
# iis-errors
# iis-recycle-all
# Search command history
h | where CommandLine -like "*Get-IIS*"
# Repeat last command
r
# Edit and re-run command (opens in notepad)
# h | select -Last 1 | foreach { $_.CommandLine | Out-File temp.ps1; notepad temp.ps1 }
# Create shortcuts for complex commands
$sites = Get-IISSite # Store in variable for reuse
$pools = Get-IISAppPool
# Now use $sites and $pools instead of running Get-IIS* repeatedly
$sites | where State -eq Stopped
$pools | where State -eq Started
# Copy site names to clipboard for use elsewhere
Get-IISSite | select -ExpandProperty Name | clip
# Copy app pool names
Get-IISAppPool | select -ExpandProperty Name | clip
# Copy site info as formatted text
Get-IISSite | ft Name,State,ID | Out-String | clip