PowerShell for Windows Server Admins: 10 Essential Scripts

PowerShell is the command-line backbone of modern Windows Server administration. Whether you are managing one server or a hundred, scripting repetitive tasks saves hours and eliminates human error. These ten scripts cover the tasks every Windows admin faces weekly — learn them, adapt them, and build your own library from here.

1. Get a System Inventory Report

Quickly document a server's hardware and OS details — useful for asset management and troubleshooting:

$info = Get-ComputerInfo
[PSCustomObject]@{
    Hostname    = $env:COMPUTERNAME
    OS          = $info.OsName
    Version     = $info.OsVersion
    RAM_GB      = [math]::Round($info.CsTotalPhysicalMemory / 1GB, 2)
    CPU         = $info.CsProcessors.Name
    LastBoot    = $info.OsLastBootUpTime
} | Format-List

2. Bulk Create Active Directory Users from CSV

Prepare a CSV with columns: FirstName, LastName, Department, OU. Then run:

Import-Csv "C:Temp
ew-users.csv" | ForEach-Object {
    $username = ($_.FirstName[0] + $_.LastName).ToLower()
    $upn = "$username@corp.local"
    New-ADUser `
        -Name "$($_.FirstName) $($_.LastName)" `
        -GivenName $_.FirstName `
        -Surname $_.LastName `
        -SamAccountName $username `
        -UserPrincipalName $upn `
        -Path "OU=$($_.Department),OU=Users,DC=corp,DC=local" `
        -AccountPassword (ConvertTo-SecureString "Welcome1!" -AsPlainText -Force) `
        -ChangePasswordAtLogon $true `
        -Enabled $true
    Write-Host "Created: $upn"
}

3. Find All Inactive User Accounts

Stale accounts are a security risk. Find users who have not logged in for 90 days:

$cutoff = (Get-Date).AddDays(-90)
Get-ADUser -Filter {LastLogonDate -lt $cutoff -and Enabled -eq $true} `
    -Properties LastLogonDate, Department |
    Select-Object Name, SamAccountName, Department, LastLogonDate |
    Sort-Object LastLogonDate |
    Export-Csv "C:Tempinactive-users.csv" -NoTypeInformation

4. Monitor Disk Space Across Multiple Servers

$servers = "srv-dc01", "srv-web01", "srv-files01"
$servers | ForEach-Object {
    Get-WmiObject Win32_LogicalDisk -ComputerName $_ -Filter "DriveType=3" |
    Select-Object `
        @{N="Server";E={$_.__SERVER}},
        DeviceID,
        @{N="Size_GB";E={[math]::Round($_.Size/1GB,1)}},
        @{N="Free_GB";E={[math]::Round($_.FreeSpace/1GB,1)}},
        @{N="Used_%";E={[math]::Round((($_.Size - $_.FreeSpace)/$_.Size)*100,1)}}
} | Format-Table -AutoSize

5. Restart a Service and Verify It Comes Back Up

$serviceName = "Spooler"
$server = "srv-print01"
Restart-Service -Name $serviceName -ComputerName $server
Start-Sleep -Seconds 5
$status = (Get-Service -Name $serviceName -ComputerName $server).Status
if ($status -eq "Running") {
    Write-Host "$serviceName on $server is Running." -ForegroundColor Green
} else {
    Write-Warning "$serviceName on $server is $status — investigate immediately."
}

6. Export All Members of a Security Group

$groupName = "Finance-ReadOnly"
Get-ADGroupMember -Identity $groupName -Recursive |
    Get-ADUser -Properties DisplayName, EmailAddress, Department |
    Select-Object DisplayName, SamAccountName, EmailAddress, Department |
    Export-Csv "C:Temp$groupName-members.csv" -NoTypeInformation

7. Check Windows Update Status

$session = New-Object -ComObject Microsoft.Update.Session
$searcher = $session.CreateUpdateSearcher()
$results = $searcher.Search("IsInstalled=0 and Type='Software'")
Write-Host "Pending updates: $($results.Updates.Count)"
$results.Updates | Select-Object Title, MsrcSeverity | Format-Table -AutoSize

8. Find and Kill Processes Consuming High CPU

Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name, Id, CPU, WorkingSet |
    ForEach-Object {
        $_ | Add-Member -NotePropertyName RAM_MB -NotePropertyValue ([math]::Round($_.WorkingSet/1MB,1)) -PassThru
    } | Select-Object Name, Id, CPU, RAM_MB | Format-Table -AutoSize

9. Create a Local Admin Account on Remote Servers

$servers = "srv-web01","srv-app01"
$password = Read-Host "Enter password" -AsSecureString
foreach ($srv in $servers) {
    Invoke-Command -ComputerName $srv -ScriptBlock {
        param($pw)
        New-LocalUser -Name "svc-admin" -Password $pw -FullName "Local Admin" -Description "Break-glass account" -PasswordNeverExpires $true
        Add-LocalGroupMember -Group "Administrators" -Member "svc-admin"
    } -ArgumentList $password
    Write-Host "Account created on $srv"
}

10. Generate an HTML Server Health Report

$report = @"

Server Health: $env:COMPUTERNAME

Generated: $(Get-Date)

Top 5 Processes by CPU

$(Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 Name, CPU | ConvertTo-Html -Fragment)

Disk Space

$(Get-PSDrive -PSProvider FileSystem | Select-Object Name, Used, Free | ConvertTo-Html -Fragment) "@ $report | Out-File "C:Temphealth-report.html" Invoke-Item "C:Temphealth-report.html"

Save your scripts in a structured folder like C:Scripts and version-control them with Git. A personal script library is one of the most valuable assets a Windows admin can build over their career.