System administrators spend countless hours on repetitive tasks: creating user accounts, checking disk space, generating reports, monitoring services. PowerShell automation eliminates this manual work, saving 10-20 hours per week while reducing human error to near zero.
This guide provides 10 production-ready PowerShell scripts you can implement immediately. Each script includes detailed explanations, customization options, and real-world use cases from enterprise environments.
1. Bulk Active Directory User Creation
New-BulkADUsers.ps1
Use case: Onboard 10, 50, or 500 users in minutes instead of hours
Time saved: 2-5 hours per week
Creating users one by one through Active Directory Users and Computers is inefficient and error-prone. This script reads from a CSV file and creates fully configured user accounts with proper OU placement, group memberships, and password policies.
# Import user data from CSV
$Users = Import-Csv -Path "C:\NewUsers.csv"
foreach ($User in $Users) {
$UserParams = @{
Name = "$($User.FirstName) $($User.LastName)"
GivenName = $User.FirstName
Surname = $User.LastName
SamAccountName = $User.Username
UserPrincipalName = "$($User.Username)@yourdomain.com"
Path = "OU=Users,OU=Company,DC=yourdomain,DC=com"
AccountPassword = (ConvertTo-SecureString "TempPass123!" -AsPlainText -Force)
Enabled = $true
ChangePasswordAtLogon = $true
Department = $User.Department
Title = $User.JobTitle
}
New-ADUser @UserParams
# Add to groups based on department
Add-ADGroupMember -Identity "$($User.Department)-Users" -Members $User.Username
Write-Host "Created user: $($User.Username)" -ForegroundColor Green
}
Write-Host "Bulk user creation complete!" -ForegroundColor Cyan
CSV format (NewUsers.csv):
FirstName,LastName,Username,Department,JobTitle
John,Smith,jsmith,IT,System Administrator
Sarah,Johnson,sjohnson,HR,HR Manager
Mike,Williams,mwilliams,Sales,Account Executive
Customization options:
- Add email configuration with Exchange cmdlets
- Generate random secure passwords using SecureRandom
- Send welcome emails automatically
- Create home directories and map drives
2. Disk Space Monitoring & Alerts
Monitor-DiskSpace.ps1
Use case: Prevent server outages from full disks
Time saved: 1-2 hours per week
Proactive disk monitoring prevents critical failures. This script checks all servers, sends email alerts when thresholds are exceeded, and generates HTML reports for management.
$Servers = Get-Content "C:\Scripts\servers.txt"
$Threshold = 20 # Alert when less than 20% free
$EmailTo = "sysadmin@company.com"
$EmailFrom = "monitoring@company.com"
$SMTPServer = "smtp.company.com"
$Report = @()
foreach ($Server in $Servers) {
$Disks = Get-WmiObject Win32_LogicalDisk -ComputerName $Server -Filter "DriveType=3"
foreach ($Disk in $Disks) {
$PercentFree = [math]::Round(($Disk.FreeSpace / $Disk.Size) * 100, 2)
$DiskInfo = [PSCustomObject]@{
Server = $Server
Drive = $Disk.DeviceID
SizeGB = [math]::Round($Disk.Size / 1GB, 2)
FreeGB = [math]::Round($Disk.FreeSpace / 1GB, 2)
PercentFree = $PercentFree
Status = if ($PercentFree -lt $Threshold) { "CRITICAL" } else { "OK" }
}
$Report += $DiskInfo
if ($PercentFree -lt $Threshold) {
Write-Warning "$Server drive $($Disk.DeviceID) is at $PercentFree% free!"
}
}
}
# Generate HTML report
$HTMLReport = $Report | ConvertTo-Html -Title "Disk Space Report" -PreContent "Server Disk Space Report
"
# Send email if critical alerts exist
$CriticalDisks = $Report | Where-Object { $_.Status -eq "CRITICAL" }
if ($CriticalDisks) {
Send-MailMessage -To $EmailTo -From $EmailFrom -Subject "CRITICAL: Low Disk Space Alert" -Body ($HTMLReport | Out-String) -BodyAsHtml -SmtpServer $SMTPServer
}
# Save report
$Report | Export-Csv -Path "C:\Reports\DiskSpace_$(Get-Date -Format 'yyyy-MM-dd').csv" -NoTypeInformation
Schedule this script: Run daily at 6 AM using Task Scheduler
3. Automatic Service Restart
Restart-CriticalServices.ps1
Use case: Automatically recover failed services
Time saved: 3-5 hours per week
Services crash. Applications hang. This script monitors critical services and automatically restarts them, logging all actions for audit trails.
$Services = @(
"Spooler",
"MSSQLSERVER",
"W3SVC",
"WinRM"
)
$LogFile = "C:\Logs\ServiceRestart_$(Get-Date -Format 'yyyy-MM').log"
foreach ($ServiceName in $Services) {
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($Service.Status -ne "Running") {
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Host "$Timestamp - $ServiceName is $($Service.Status). Attempting restart..." -ForegroundColor Yellow
try {
Restart-Service -Name $ServiceName -Force
"$Timestamp - SUCCESS: Restarted $ServiceName" | Out-File $LogFile -Append
# Send alert
Send-MailMessage -To "admin@company.com" -From "monitoring@company.com" `
-Subject "Service Restarted: $ServiceName" `
-Body "$ServiceName was stopped and has been automatically restarted." `
-SmtpServer "smtp.company.com"
}
catch {
"$Timestamp - FAILED: Could not restart $ServiceName - $($_.Exception.Message)" | Out-File $LogFile -Append
Write-Error "Failed to restart $ServiceName"
}
}
}
Write-Host "Service monitoring complete." -ForegroundColor Green
4. Inactive User Account Cleanup
Disable-InactiveUsers.ps1
Use case: Maintain security compliance by disabling dormant accounts
Time saved: 2-3 hours per month
$InactiveDays = 90
$DisabledOU = "OU=Disabled,OU=Users,DC=company,DC=com"
$Date = (Get-Date).AddDays(-$InactiveDays)
$InactiveUsers = Get-ADUser -Filter {
LastLogonDate -lt $Date -and Enabled -eq $true
} -Properties LastLogonDate, EmailAddress
foreach ($User in $InactiveUsers) {
# Disable account
Disable-ADAccount -Identity $User
# Move to disabled OU
Move-ADObject -Identity $User.DistinguishedName -TargetPath $DisabledOU
# Log action
Write-Host "Disabled: $($User.SamAccountName) - Last logon: $($User.LastLogonDate)" -ForegroundColor Yellow
# Optional: Send notification to manager
if ($User.EmailAddress) {
# Send email notification
}
}
Write-Host "Disabled $($InactiveUsers.Count) inactive accounts." -ForegroundColor Cyan
5. Automated Daily Report Generation
Generate-DailyReport.ps1
Use case: Executive dashboards and compliance reporting
Time saved: 5-7 hours per week
# Gather system statistics
$Report = @{
Date = Get-Date -Format "yyyy-MM-dd"
TotalServers = (Get-ADComputer -Filter {OperatingSystem -like "*Server*"}).Count
TotalUsers = (Get-ADUser -Filter *).Count
DisabledUsers = (Get-ADUser -Filter {Enabled -eq $false}).Count
NewUsersToday = (Get-ADUser -Filter {whenCreated -ge (Get-Date).Date}).Count
PasswordExpiringIn7Days = (Get-ADUser -Filter {Enabled -eq $true} -Properties PasswordLastSet |
Where-Object {($_.PasswordLastSet -ne $null) -and
((New-TimeSpan -Start (Get-Date) -End ($_.PasswordLastSet.AddDays(90))).Days -le 7)}).Count
}
# Create HTML report
$HTMLBody = @"
IT Infrastructure Daily Report - $($Report.Date)
Metric Value
Total Servers $($Report.TotalServers)
Total Users $($Report.TotalUsers)
Disabled Users $($Report.DisabledUsers)
New Users Today $($Report.NewUsersToday)
Passwords Expiring (7 days) $($Report.PasswordExpiringIn7Days)
"@
# Send email
Send-MailMessage -To "management@company.com" -From "reports@company.com" `
-Subject "Daily IT Report - $($Report.Date)" -Body $HTMLBody -BodyAsHtml `
-SmtpServer "smtp.company.com"
6. Automated Backup Verification
Test-BackupIntegrity.ps1
Use case: Ensure backups are actually restorable
Time saved: 2-4 hours per week
$BackupPath = "\\backup-server\Backups"
$MaxAge = 1 # Alert if backup older than 1 day
$Backups = Get-ChildItem -Path $BackupPath -Filter "*.bak" -Recurse
foreach ($Backup in $Backups) {
$Age = (Get-Date) - $Backup.LastWriteTime
if ($Age.TotalDays -gt $MaxAge) {
Write-Warning "$($Backup.Name) is $([math]::Round($Age.TotalDays, 1)) days old!"
# Send alert
Send-MailMessage -To "backup-admin@company.com" -From "monitoring@company.com" `
-Subject "ALERT: Backup File Out of Date" `
-Body "Backup file $($Backup.Name) was last modified on $($Backup.LastWriteTime)" `
-SmtpServer "smtp.company.com"
}
# Verify file integrity
if ($Backup.Length -eq 0) {
Write-Error "$($Backup.Name) is 0 bytes - backup failed!"
}
}
Write-Host "Backup verification complete." -ForegroundColor Green
7. Network Connectivity Monitor
Test-NetworkConnectivity.ps1
Use case: Detect network issues before users complain
Time saved: 1-3 hours per week
$Targets = @(
"dc01.company.com",
"fileserver01.company.com",
"sql01.company.com",
"8.8.8.8"
)
foreach ($Target in $Targets) {
$PingResult = Test-Connection -ComputerName $Target -Count 4 -Quiet
if (-not $PingResult) {
Write-Warning "$Target is UNREACHABLE!"
# Send alert
Send-MailMessage -To "network-admin@company.com" -From "monitoring@company.com" `
-Subject "ALERT: Network Connectivity Issue - $Target" `
-Body "$Target failed ping test at $(Get-Date)" `
-SmtpServer "smtp.company.com"
} else {
Write-Host "$Target is reachable" -ForegroundColor Green
}
}
8. Password Expiration Notifications
Send-PasswordExpiryNotifications.ps1
Use case: Reduce help desk password reset tickets by 50%+
Time saved: 3-5 hours per week
$NotifyDays = 14 # Notify users 14 days before expiration
$MaxPasswordAge = 90
$Users = Get-ADUser -Filter {Enabled -eq $true -and PasswordNeverExpires -eq $false} `
-Properties PasswordLastSet, EmailAddress
foreach ($User in $Users) {
if ($User.PasswordLastSet) {
$ExpiryDate = $User.PasswordLastSet.AddDays($MaxPasswordAge)
$DaysToExpiry = ($ExpiryDate - (Get-Date)).Days
if ($DaysToExpiry -le $NotifyDays -and $DaysToExpiry -gt 0) {
$EmailBody = @"
Hello $($User.Name),
Your password will expire in $DaysToExpiry day(s) on $($ExpiryDate.ToShortDateString()).
Please change your password before it expires to avoid being locked out.
To change your password: Press Ctrl+Alt+Delete and select "Change Password"
IT Department
"@
if ($User.EmailAddress) {
Send-MailMessage -To $User.EmailAddress -From "it-helpdesk@company.com" `
-Subject "Password Expiring in $DaysToExpiry Days" `
-Body $EmailBody -SmtpServer "smtp.company.com"
Write-Host "Notified: $($User.SamAccountName) - $DaysToExpiry days remaining" -ForegroundColor Yellow
}
}
}
}
9. Clean Up Old Files Automatically
Remove-OldFiles.ps1
Use case: Reclaim disk space from temp files and old logs
Time saved: 1-2 hours per week
$Paths = @(
"C:\Windows\Temp",
"C:\Temp",
"C:\Logs"
)
$DaysOld = 30
$DeletedFiles = 0
$SpaceFreed = 0
foreach ($Path in $Paths) {
$Files = Get-ChildItem -Path $Path -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$DaysOld) }
foreach ($File in $Files) {
$SpaceFreed += $File.Length
Remove-Item $File.FullName -Force -ErrorAction SilentlyContinue
$DeletedFiles++
}
}
$SpaceFreedGB = [math]::Round($SpaceFreed / 1GB, 2)
Write-Host "Cleanup complete: Deleted $DeletedFiles files, freed $SpaceFreedGB GB" -ForegroundColor Green
# Log results
"$(Get-Date) - Deleted $DeletedFiles files, freed $SpaceFreedGB GB" | Out-File "C:\Scripts\Logs\cleanup.log" -Append
10. Group Membership Audit Report
Export-GroupMembership.ps1
Use case: Security audits and compliance reporting
Time saved: 2-4 hours per audit
$Groups = Get-ADGroup -Filter {GroupCategory -eq "Security"} | Sort-Object Name
$Report = @()
foreach ($Group in $Groups) {
$Members = Get-ADGroupMember -Identity $Group -Recursive
foreach ($Member in $Members) {
$Report += [PSCustomObject]@{
GroupName = $Group.Name
MemberName = $Member.Name
MemberType = $Member.objectClass
SamAccountName = $Member.SamAccountName
}
}
}
# Export to Excel-friendly format
$Report | Export-Csv -Path "C:\Reports\GroupMembership_$(Get-Date -Format 'yyyy-MM-dd').csv" -NoTypeInformation
Write-Host "Exported membership for $($Groups.Count) groups" -ForegroundColor Green
Master PowerShell Automation
Want to write your own PowerShell scripts from scratch? Our PowerShell for IT Automation course teaches scripting fundamentals to advanced automation techniques.
Explore PowerShell CourseImplementation Best Practices
1. Test in Non-Production First
- Always test scripts in lab or dev environments
- Use -WhatIf parameter to preview changes
- Validate CSV inputs before running bulk operations
2. Schedule with Task Scheduler
- Run monitoring scripts every 15-30 minutes
- Schedule cleanup scripts during off-hours
- Use service accounts with proper permissions
3. Implement Logging
- Log all script executions with timestamps
- Capture errors for troubleshooting
- Rotate logs to prevent disk space issues
4. Error Handling
- Use Try/Catch blocks for critical operations
- Send email alerts for failures
- Include -ErrorAction parameters appropriately
Measuring ROI
Before Automation:
- User creation: 5-10 minutes per user
- Disk space checks: 30 minutes daily
- Service monitoring: 2 hours weekly
- Report generation: 4 hours weekly
- Total: 15-20 hours per week
After Automation:
- User creation: 30 seconds per batch
- Automated monitoring: 0 manual hours
- Automated reports: 0 manual hours
- Total manual time: 1-2 hours per week (script maintenance)
Next Steps
1. Start Small
Implement one script this week. Master it. Then add another.
2. Build a Script Library
Store all scripts in version control (Git). Document parameters and use cases.
3. Share Knowledge
Train junior admins on using and modifying scripts. Create internal documentation.
4. Continuously Improve
Monitor script performance. Optimize slow operations. Add features based on team feedback.
Conclusion
PowerShell automation transforms IT administration from reactive firefighting to proactive management. These 10 scripts form the foundation of an efficient, automated infrastructure that scales with your organization.
Start implementing today. Your future self (and your team) will thank you for the hours saved and stress eliminated.
About the Author: Fedelis Andonganyung is a Microsoft Certified Azure Solutions Architect and founder of DigiTech Globals, specializing in PowerShell automation and IT infrastructure optimization.