PowerShell
Stop Hunting for Web Servers: How to Auto-Discover Every IIS Instance in Your Domain | Lazy Admin Blog

Have you ever been asked for a list of every active web server in your environment, only to realize your documentation is six months out of date? You could check your DNS records manually, or you could let PowerShell do the detective work for you.
This script scans your Active Directory for Windows Servers, checks if the World Wide Web Publishing Service (W3SVC) is actually running, and then pulls a deep-profile of the hardware, OS, and network configuration for every active hit.
The Setup
- Create the workspace: Create a folder at
C:\Temp\ServersRunningIIS. - Prepare the list: The script will automatically generate a list of all Windows Servers from AD, but ensure you have the Active Directory PowerShell module installed.
- Run with Privileges: Since the script uses WMI to query remote system info (RAM, OS Version, etc.), run your PowerShell ISE or Console as a Domain Admin.
The PowerShell Script
# Script: IIS Server Discovery & Profiler# Location: lazyadminblog.com# Purpose: Identify active IIS nodes and collect hardware/OS specsImport-Module ActiveDirectory# 1. Harvest all Windows Servers from ADWrite-Host "Gathering server list from Active Directory..." -ForegroundColor Cyan$servers = Get-ADComputer -Filter {operatingsystem -Like "Windows server*"} | Select-Object -ExpandProperty Name$servers | Out-File "C:\Temp\ServersRunningIIS\serverlist.txt"# 2. Load the list for processing$serversall = Get-Content "C:\Temp\ServersRunningIIS\serverlist.txt" Start-Transcript -Path "C:\Temp\ServersRunningIIS\log_output.txt" -Appendforeach($vm in $serversall) { try { # Check if IIS Service (W3SVC) exists and is running $iis = Get-WmiObject Win32_Service -ComputerName $vm -Filter "name='W3SVC'" -ErrorAction SilentlyContinue if($iis.State -eq "Running") { Write-Host "FOUND: IIS is active on $vm" -BackgroundColor DarkBlue -ForegroundColor DarkYellow # Collect Network Info $ipinfo = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $vm | Where-Object {$_.IPEnabled -eq $true -and $_.IPAddress -like "1*"} | Select-Object -First 1 # Collect Hardware Info $hwinfo = Get-WmiObject Win32_Computersystem -ComputerName $vm # Collect OS Info $osinfo = Get-WmiObject Win32_OperatingSystem -ComputerName $vm # Flattening data for CSV-style output $allinfo = "$($hwinfo.Name);$($hwinfo.Domain);$($ipinfo.IPAddress);$($ipinfo.IPSubnet);$($ipinfo.DefaultIPGateway);$($hwinfo.TotalPhysicalMemory);$($hwinfo.Manufacturer);$($hwinfo.Model);$($osinfo.Caption);$($osinfo.OSArchitecture);$($osinfo.ServicePackMajorVersion);$($osinfo.SystemDrive);$($osinfo.Version)" # Save results to our 'Running' list $allinfo | Out-File "C:\Temp\ServersRunningIIS\RunningWebServers.txt" -Append } } catch { Write-Host "Could not connect to $vm" -ForegroundColor Red }}Stop-TranscriptWrite-Host "Audit Complete! Check C:\Temp\ServersRunningIIS\RunningWebServers.txt" -ForegroundColor Green
What’s inside the report?
The output file (RunningWebServers.txt) uses a semicolon (;) delimiter, making it easy to import into Excel. It captures:
- Network: IP Address, Subnet, and Gateway.
- Hardware: Manufacturer, Model, RAM, and Domain membership.
- Software: OS Version, Architecture (x64/x86), and System Drive.
Lazy Admin Tip
If you want to open the results immediately in Excel, just rename the output file from .txt to .csv and use the “Text to Columns” feature in Excel with the semicolon as the separator!
Automation: Bulk Create and Delete VM Snapshots Across Linked vCenters | Lazy Admin Blog

In a large environment, taking snapshots before a major patch or application update is a standard safety net. But if you have servers spread across multiple vCenters in Linked Mode (e.g., Datacenter1 and Datacenter2), clicking through the vSphere Client is a waste of time.
Today, I’m sharing a “Lazy Admin” script that allows you to bulk create, check, and remove snapshots using a simple CSV list.
Prerequisites
- VMware PowerCLI: Ensure the PowerCLI module is installed on the machine running the script.
- CSV Setup: Create a file named
snapshot_servers.csvinC:\Temp\VMSnapshots\.
The CSV should look like this: | Host | Location | | :— | :— | | Server01 | Datacenter1 | | Server02 | Datacenter2 |
Part 1: Creating Snapshots
- Open PowerShell ISE with vCenter Administrator credentials.
- Load the functions by running the full script (provided below).
- Run the following command:
Create-VMSnapshots -SS_CSV "C:\Temp\VMSnapshots\snapshot_servers.csv" -SS_Name "Pre-Patching" -SS_Description "Requested by App Team"
The script will iterate through your CSV and create snapshots sequentially. You can monitor the progress in the vSphere Tasks console.
Part 2: Deleting Snapshots
Once your changes are verified, don’t let those snapshots linger and bloat your datastores! To remove them:
- Use the same
snapshot_servers.csvlist. - Run the following command:
Remove-VMSnapshots -SS_CSV "C:\Temp\VMSnapshots\snapshot_servers.csv"
Note: This is a sequential script; it will wait for one snapshot removal to finish before moving to the next to avoid pinning your storage I/O.
The Script: VMSnapshots.ps1
Save this code to C:\Temp\VMSnapshots\VMSnapshots.ps1.
function Create-VMSnapshots { param ( [string]$SS_CSV = $(Read-Host "Enter path to CSV"), [string]$SS_Name = $(Read-Host "Enter name for snapshots"), [string]$SS_Description = $(Read-Host "Enter description for snapshots") ) # Import VMware PowerCLI Module If ( !(Get-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) ) { import-module VMware.VimAutomation.Core } $Servers = Import-CSV $SS_CSV $WLM_vCenter = Connect-VIServer vCenter1 -WarningAction SilentlyContinue $EDN_vCenter = Connect-VIServer vCenter2 -WarningAction SilentlyContinue ForEach($Server in $Servers){ If($Server.Location -eq 'Datacenter1'){ New-Snapshot -VM $Server.Host -Name $SS_Name -Description $SS_Description -Quiesce -Server $WLM_vCenter -WarningAction SilentlyContinue } ElseIf($Server.Location -eq 'Datacenter2'){ New-Snapshot -VM $Server.Host -Name $SS_Name -Description $SS_Description -Quiesce -Server $EDN_vCenter -WarningAction SilentlyContinue } } }function Check-VMSnapshots { param ( [string]$SS_CSV = $(Read-Host "Enter path to CSV"), [string]$SS_Name = $(Read-Host "Enter snapshot name") ) # Import VMware PowerCLI Module If ( !(Get-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) ) { import-module VMware.VimAutomation.Core } $Servers = Import-CSV $SS_CSV $WLM_vCenter = Connect-VIServer vCenter1 -WarningAction SilentlyContinue $EDN_vCenter = Connect-VIServer vCenter2 -WarningAction SilentlyContinue ForEach($Server in $Servers){ If($Server.Location -eq 'Datacenter1'){ Get-Snapshot -VM $Server.Host -Name $SS_Name -Server $WLM_vCenter | Select VM, Name, @{ n="SpaceUsedGB"; e={[math]::round( $_.SizeGB )}} -WarningAction SilentlyContinue } ElseIf($Server.Location -eq 'Datacenter2'){ Get-Snapshot -VM $Server.Host -Name $SS_Name -Server $EDN_vCenter | Select VM, Name, @{ n="SpaceUsedGB"; e={[math]::round( $_.SizeGB )}} -WarningAction SilentlyContinue } } } function Remove-VMSnapshots { param ( [string]$SS_CSV = $(Read-Host "Enter path to CSV") ) # Import VMware PowerCLI Module If ( !(Get-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) ) { import-module VMware.VimAutomation.Core } $Servers = Import-CSV $SS_CSV $WLM_vCenter = Connect-VIServer vCenter1 -WarningAction SilentlyContinue $EDN_vCenter = Connect-VIServer vCenter2 -WarningAction SilentlyContinue ForEach($Server in $Servers){ If($Server.Location -eq 'Datacenter1'){ Get-Snapshot $Server.Host -Server $WLM_vCenter | Remove-Snapshot -Confirm:$false -WarningAction SilentlyContinue } ElseIf($Server.Location -eq 'Datacenter2'){ Get-Snapshot $Server.Host -Server $EDN_vCenter | Remove-Snapshot -Confirm:$false -WarningAction SilentlyContinue } } }
The Ultimate Robocopy Command for Large-Scale Migrations | Lazy Admin Blog

If you need to move huge files while keeping a close eye on progress, this is the syntax you want. It includes logging, multi-threading for speed, and the ability to resume if the network drops.
The “Power User” Command
DOS
robocopy "D:\Source_Data" "E:\Destination_Data" /s /e /z /mt:32 /tee /log+:"C:\Logs\MigrationLog.txt"
Switch Breakdown: Why We Use Them
| Switch | What it does |
| /s /e | Copies all subdirectories, including empty ones. |
| /z | Restart Mode: If the connection drops mid-file, Robocopy can resume from where it left off instead of starting the file over. Essential for 100GB+ files! |
| /mt:32 | Multi-Threading: Uses 32 threads to copy multiple files simultaneously. (Default is 8). Adjust based on your CPU/Disk speed. |
| /tee | Writes the status to the console window and the log file at the same time. |
| /log+: | Creates a log file. Using the + appends to an existing log rather than overwriting it—perfect for multi-day migrations. |
How to Monitor Progress in Real-Time
Because we used the /tee and /log+ switches, you have two ways to monitor the status:
- The Console: You’ll see a rolling percentage for each file directly in your Command Prompt.
- Tail the Log: Since the log is being updated live, you can “tail” it from another window (or even remotely) to see the progress without touching the active copy session.
Lazy Admin Tip (PowerShell):
Open a PowerShell window and run this command to watch your Robocopy log update in real-time as files move:
Get-Content "C:\Logs\MigrationLog.txt" -Wait
Important Notes for Huge Files
- Disk Quotas: Robocopy doesn’t check destination space before starting. Use
dirordf(if using Linux targets) to ensure you have enough room. - Permissions: If you need to copy NTFS permissions (ACLs), add the /copyall switch.
- Bandwidth: Running
/mt:128(the max) can saturate a 1Gbps link. If you’re copying over a live production network, stick to/mt:8or/mt:16.
#WindowsServer #Robocopy #DataMigration #SysAdmin #ITInfrastructure #StorageAdmin #TechTips #LazyAdmin #CloudMigration
