This SCCM health check PowerShell script provides a complete audit of your Microsoft Endpoint Configuration Manager environment..
In the previous article i have explained how to solve the issue of SCCM server stuck at Checking Prerequisites during update of the branch or version where administrator needs to check date of release and delete records from the database and recheck updates again. Here you will be learning, how to perform your server health check using PowerShell with an executive and impressive html report.
This script is clean and usable in production environment.
Copy the below script
# ==========================================================
# MSERVERPRO – SCCM HEALTH CHECK REPORT v3.0
# Author: Habib
# Practice: Infrastructure & EUC Practice
# Company: MServerPro
# ==========================================================
# ===================== CONFIGURATION ======================
param(
[switch]$OpenReport
)
$CompanyInfo = @{
Name = "MServerPro"
Practice = "Infrastructure & EUC Practice"
Author = "Habib"
Version = "3.1"
SupportEmail = "habibnp01@gmail.com"
}
# Performance Thresholds
$Thresholds = @{
CPU_Green = 60
CPU_Yellow = 80
MEM_Green = 70
MEM_Yellow = 85
DISK_Green = 20
DISK_Yellow = 10
MaxUptimeDays = 90
}
# ===================== USER INTERFACE FUNCTIONS ======================
function Show-ScriptHeader {
Clear-Host
Write-Host "`n" + "="*60 -ForegroundColor Cyan
Write-Host " SCCM HEALTH CHECK REPORT GENERATOR v$($CompanyInfo.Version)" -ForegroundColor Cyan
Write-Host " Company: $($CompanyInfo.Name)" -ForegroundColor Yellow
Write-Host " Practice: $($CompanyInfo.Practice)" -ForegroundColor Yellow
Write-Host " Author: $($CompanyInfo.Author)" -ForegroundColor Yellow
Write-Host "="*60 -ForegroundColor Cyan
Write-Host "`n"
}
function Get-UserInput {
param(
[string]$Title,
[string]$Message,
[string]$DefaultValue = "",
[switch]$Password
)
Show-ScriptHeader
Write-Host " $Title" -ForegroundColor Green
Write-Host " " + ("-" * ($Title.Length + 2)) -ForegroundColor Green
if ($Password) {
$secureInput = Read-Host " $Message" -AsSecureString
$inputValue = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureInput))
} else {
if ($DefaultValue) {
$inputValue = Read-Host " $Message" -DefaultValue $DefaultValue
} else {
$inputValue = Read-Host " $Message"
}
}
return $inputValue
}
function Show-Progress {
param(
[string]$Message,
[string]$Status = "Processing..."
)
Write-Host "`n [$Status]" -ForegroundColor Cyan -NoNewline
Write-Host " $Message" -ForegroundColor White
}
function Show-Success {
param([string]$Message)
Write-Host " [✓] " -ForegroundColor Green -NoNewline
Write-Host $Message -ForegroundColor White
}
function Show-Warning {
param([string]$Message)
Write-Host " [!] " -ForegroundColor Yellow -NoNewline
Write-Host $Message -ForegroundColor White
}
function Show-Error {
param([string]$Message)
Write-Host " [✗] " -ForegroundColor Red -NoNewline
Write-Host $Message -ForegroundColor White
}
function Get-SCCMCredentials {
Show-ScriptHeader
Write-Host " CREDENTIALS REQUIRED" -ForegroundColor Green
Write-Host " --------------------" -ForegroundColor Green
Write-Host "`n To perform SCCM health checks, you need administrative credentials." -ForegroundColor Yellow
Write-Host " Please enter credentials with SCCM administrator privileges." -ForegroundColor Yellow
Write-Host ""
$username = Get-UserInput -Title "CREDENTIALS" -Message "Enter username (DOMAIN\Username):"
$password = Get-UserInput -Title "CREDENTIALS" -Message "Enter password:" -Password
if ([string]::IsNullOrEmpty($username) -or [string]::IsNullOrEmpty($password)) {
Show-Error "Credentials are required to continue."
return $null
}
try {
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($username, $securePassword)
Show-Success "Credentials captured successfully"
return $credential
}
catch {
Show-Error "Failed to create credentials object: $($_.Exception.Message)"
return $null
}
}
function Get-SCCMServerInfo {
Show-ScriptHeader
Write-Host " SCCM SERVER INFORMATION" -ForegroundColor Green
Write-Host " -----------------------" -ForegroundColor Green
Write-Host "`n Please provide information about your SCCM environment." -ForegroundColor Yellow
Write-Host ""
# Get SCCM Server Name
$sccmServer = Get-UserInput -Title "SCCM SERVER" -Message "Enter SCCM Primary Server Name:" -DefaultValue $env:COMPUTERNAME
# Get SMS Provider
$smsProvider = Get-UserInput -Title "SMS PROVIDER" -Message "Enter SMS Provider Server Name:" -DefaultValue $sccmServer
# Get Site Code (try to detect automatically)
Show-Progress "Attempting to detect SCCM site code..."
$siteCode = $null
try {
# Try to read from registry
$regPath = "HKLM:\SOFTWARE\Microsoft\SMS\Identification"
if (Test-Path $regPath) {
$siteCode = (Get-ItemProperty -Path $regPath -Name "Site Code" -ErrorAction SilentlyContinue)."Site Code"
if ($siteCode) {
Show-Success "Detected site code from registry: $siteCode"
}
}
} catch {}
if (-not $siteCode) {
$siteCode = Get-UserInput -Title "SITE CODE" -Message "Enter SCCM Site Code (3 characters):"
if ($siteCode.Length -ne 3) {
Show-Warning "Site code should typically be 3 characters (e.g., ABC, XYZ)"
}
}
# Get Output Path
$defaultOutput = "C:\Reports\SCCMHealth_$(Get-Date -Format 'yyyyMMdd')"
$outputPath = Get-UserInput -Title "REPORT LOCATION" -Message "Enter output directory for reports:" -DefaultValue $defaultOutput
return @{
SCCMServer = $sccmServer
SMSProvider = $smsProvider
SiteCode = $siteCode
OutputPath = $outputPath
}
}
# ===================== INITIALIZATION ======================
function Initialize-Script {
param($OutputPath)
Show-Progress "Initializing SCCM Health Check..."
# Create output directory
if (!(Test-Path $OutputPath)) {
try {
New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
Show-Success "Created output directory: $OutputPath"
}
catch {
Show-Error "Failed to create output directory: $($_.Exception.Message)"
return $false
}
} else {
Show-Success "Output directory already exists: $OutputPath"
}
# Import required modules
Show-Progress "Checking for required PowerShell modules..."
# Check for CimCmdlets module
if (-not (Get-Module -Name CimCmdlets -ListAvailable)) {
Show-Error "CimCmdlets module is not available. This module is required for SCCM health checks."
Write-Host "`n Please install the module using:" -ForegroundColor Yellow
Write-Host " Install-WindowsFeature -Name RSAT" -ForegroundColor White
return $false
} else {
Show-Success "CimCmdlets module is available"
}
try {
Import-Module -Name CimCmdlets -ErrorAction Stop -Force
Show-Success "Imported required modules"
return $true
}
catch {
Show-Error "Failed to import required modules: $($_.Exception.Message)"
return $false
}
}
# ===================== HTML FUNCTIONS ======================
function Get-HTMLHeader {
param($HostName, $SCCMServer, $SiteCode)
$ReportDate = Get-Date -Format "dddd, MMMM dd, yyyy"
$ReportTime = Get-Date -Format "HH:mm:ss"
return @"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SCCM Health Check Report - $HostName</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.report-header {
background: linear-gradient(135deg, #0b5394 0%, #083e6b 100%);
color: white;
padding: 30px;
border-radius: 10px;
margin-bottom: 30px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.company-info {
margin-bottom: 15px;
}
.company-name {
font-size: 1.8rem;
font-weight: 700;
color: white;
}
.company-practice {
font-size: 1rem;
opacity: 0.9;
font-style: italic;
}
.report-title {
font-size: 2.2rem;
font-weight: 600;
margin: 10px 0;
color: white;
}
.report-meta {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 25px;
background: rgba(255, 255, 255, 0.1);
padding: 20px;
border-radius: 8px;
}
.meta-item {
display: flex;
flex-direction: column;
}
.meta-label {
font-size: 0.85rem;
opacity: 0.9;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 5px;
}
.meta-value {
font-size: 1.1rem;
font-weight: 600;
}
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 25px;
overflow: hidden;
border: 1px solid #dee2e6;
}
.card-header {
background: linear-gradient(to right, #e6f2ff, white);
padding: 15px 20px;
border-bottom: 2px solid #dee2e6;
}
.card-header h2 {
margin: 0;
color: #0b5394;
font-size: 1.3rem;
font-weight: 600;
}
.card-body {
padding: 20px;
}
.summary-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 20px 0;
}
.summary-card {
text-align: center;
padding: 25px;
border-radius: 8px;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-top: 4px solid;
transition: transform 0.3s ease;
}
.summary-card:hover {
transform: translateY(-5px);
}
.summary-card.total { border-top-color: #0b5394; }
.summary-card.healthy { border-top-color: #28a745; }
.summary-card.warning { border-top-color: #ffc107; }
.summary-card.critical { border-top-color: #dc3545; }
.summary-card .count {
font-size: 2.5rem;
font-weight: 700;
margin: 10px 0;
}
.summary-card.total .count { color: #0b5394; }
.summary-card.healthy .count { color: #28a745; }
.summary-card.warning .count { color: #ffc107; }
.summary-card.critical .count { color: #dc3545; }
.summary-card .label {
font-size: 0.9rem;
color: #6c757d;
text-transform: uppercase;
letter-spacing: 1px;
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 0.95rem;
}
.data-table th {
background-color: #e9ecef;
color: #495057;
font-weight: 600;
padding: 12px 15px;
text-align: left;
border-bottom: 2px solid #dee2e6;
}
.data-table td {
padding: 10px 15px;
border-bottom: 1px solid #dee2e6;
vertical-align: middle;
}
.data-table tr:hover {
background-color: #f8f9fa;
}
.status {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.status-ok {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-warning {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.status-critical {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.progress-container {
width: 100%;
background-color: #e9ecef;
border-radius: 10px;
overflow: hidden;
height: 20px;
}
.progress-bar {
height: 100%;
border-radius: 10px;
transition: width 0.3s ease;
}
.progress-cpu {
background: linear-gradient(90deg, #28a745, #17a2b8);
}
.progress-memory {
background: linear-gradient(90deg, #28a745, #ffc107);
}
.progress-disk {
background: linear-gradient(90deg, #28a745, #dc3545);
}
.recommendation {
background: #f8f9fa;
border-left: 4px solid #0b5394;
padding: 15px;
margin: 10px 0;
border-radius: 4px;
}
.recommendation h4 {
color: #0b5394;
margin-bottom: 8px;
}
.report-footer {
background: #343a40;
color: white;
padding: 25px;
margin-top: 40px;
border-radius: 8px;
text-align: center;
}
.company-footer {
margin-bottom: 15px;
font-size: 1.1rem;
font-weight: 600;
color: #ffc107;
}
@media print {
body { background: white; }
.card { box-shadow: none; border: 1px solid #ddd; }
.report-footer { background: white; color: black; border-top: 2px solid #ddd; }
}
</style>
</head>
<body>
<div class="container">
<div class="report-header">
<div class="company-info">
<div class="company-name">$($CompanyInfo.Name)</div>
<div class="company-practice">$($CompanyInfo.Practice)</div>
</div>
<div class="report-title">SCCM Health Check Report</div>
<div class="author-info">Prepared by: $($CompanyInfo.Author) | Version: $($CompanyInfo.Version)</div>
<div class="report-meta">
<div class="meta-item">
<div class="meta-label">SCCM Server</div>
<div class="meta-value">$SCCMServer</div>
</div>
<div class="meta-item">
<div class="meta-label">Site Code</div>
<div class="meta-value">$SiteCode</div>
</div>
<div class="meta-item">
<div class="meta-label">Report Date</div>
<div class="meta-value">$ReportDate</div>
</div>
<div class="meta-item">
<div class="meta-label">Report Time</div>
<div class="meta-value">$ReportTime</div>
</div>
<div class="meta-item">
<div class="meta-label">Generated From</div>
<div class="meta-value">$HostName</div>
</div>
</div>
</div>
"@
}
function Get-HTMLFooter {
return @"
<div class="report-footer">
<div class="container">
<div class="company-footer">$($CompanyInfo.Name)</div>
<small>Report generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | SCCM Health Check v$($CompanyInfo.Version)</small><br>
<small>Prepared by: $($CompanyInfo.Author) | $($CompanyInfo.Practice)</small><br>
<small>Contact: $($CompanyInfo.SupportEmail)</small><br>
<small>This report contains confidential information for authorized personnel only.</small>
</div>
</div>
<script>
// Interactive features
document.addEventListener('DOMContentLoaded', function() {
// Highlight critical rows
document.querySelectorAll('.status-critical').forEach(element => {
const row = element.closest('tr');
if (row) {
row.style.backgroundColor = 'rgba(220, 53, 69, 0.05)';
}
});
// Toggle recommendations on mobile
if (window.innerWidth <= 768) {
document.querySelectorAll('.card-header').forEach(header => {
header.style.cursor = 'pointer';
header.addEventListener('click', function() {
const cardBody = this.nextElementSibling;
cardBody.style.display = cardBody.style.display === 'none' ? 'block' : 'none';
});
});
}
});
</script>
</body>
</html>
"@
}
# ===================== HEALTH CHECK FUNCTIONS ======================
function Get-ServerPerformance {
param($ComputerName, $Credential)
Show-Progress "Checking server performance metrics..." -Status "PERFORMANCE"
try {
# CPU Usage
Show-Progress "Checking CPU utilization..." -Status "CPU"
$CPU = [math]::Round((Get-Counter '\Processor(_Total)\% Processor Time' -ComputerName $ComputerName -ErrorAction Stop).CounterSamples.CookedValue, 2)
# Memory Usage
Show-Progress "Checking memory utilization..." -Status "MEMORY"
$OS = Get-CimInstance Win32_OperatingSystem -ComputerName $ComputerName -ErrorAction Stop
$TotalMemoryGB = [math]::Round($OS.TotalVisibleMemorySize / 1GB, 2)
$FreeMemoryGB = [math]::Round($OS.FreePhysicalMemory / 1GB, 2)
$UsedMemoryGB = $TotalMemoryGB - $FreeMemoryGB
# Calculate memory usage percentage
if ($TotalMemoryGB -gt 0) {
$MemUsage = [math]::Round(($UsedMemoryGB / $TotalMemoryGB) * 100, 2)
} else {
$MemUsage = 0
Show-Warning "Could not calculate memory percentage accurately"
}
# Disk Usage
Show-Progress "Checking disk usage..." -Status "DISK"
$Disks = @()
$DiskDrives = Get-CimInstance Win32_LogicalDisk -ComputerName $ComputerName -Filter "DriveType=3" -ErrorAction Stop
foreach ($Disk in $DiskDrives) {
$FreeGB = [math]::Round($Disk.FreeSpace / 1GB, 2)
$TotalGB = [math]::Round($Disk.Size / 1GB, 2)
if ($TotalGB -gt 0) {
$FreePct = [math]::Round(($FreeGB / $TotalGB) * 100, 2)
} else {
$FreePct = 0
}
$Disks += [PSCustomObject]@{
Drive = $Disk.DeviceID
Label = $Disk.VolumeName
TotalGB = $TotalGB
FreeGB = $FreeGB
FreePct = $FreePct
DiskStatus = if ($FreePct -ge $Thresholds.DISK_Green) { "Healthy" }
elseif ($FreePct -ge $Thresholds.DISK_Yellow) { "Warning" }
else { "Critical" }
}
}
# Uptime
Show-Progress "Checking system uptime..." -Status "UPTIME"
$LastBoot = $OS.LastBootUpTime
$UptimeDays = (New-TimeSpan -Start $LastBoot -End (Get-Date)).Days
$UptimeHours = (New-TimeSpan -Start $LastBoot -End (Get-Date)).Hours
# Determine status
$CPUStatus = if ($CPU -le $Thresholds.CPU_Green) { "Healthy" }
elseif ($CPU -le $Thresholds.CPU_Yellow) { "Warning" }
else { "Critical" }
$MemStatus = if ($MemUsage -le $Thresholds.MEM_Green) { "Healthy" }
elseif ($MemUsage -le $Thresholds.MEM_Yellow) { "Warning" }
else { "Critical" }
Show-Success "CPU: $CPU% ($CPUStatus)"
Show-Success "Memory: $MemUsage% ($MemStatus)"
Show-Success "Uptime: $UptimeDays days, $UptimeHours hours"
return [PSCustomObject]@{
CPU = $CPU
CPUStatus = $CPUStatus
Memory = $MemUsage
MemStatus = $MemStatus
TotalMemoryGB = $TotalMemoryGB
UsedMemoryGB = $UsedMemoryGB
FreeMemoryGB = $FreeMemoryGB
Disks = $Disks
UptimeDays = $UptimeDays
UptimeHours = $UptimeHours
LastBoot = $LastBoot
}
}
catch {
Show-Error "Failed to get server performance: $($_.Exception.Message)"
return $null
}
}
function Get-SCCMComponents {
param($CimSession, $SiteCode)
Show-Progress "Checking SCCM components..." -Status "COMPONENTS"
try {
$Components = Get-CimInstance -CimSession $CimSession `
-Namespace "root\sms\site_$SiteCode" `
-ClassName SMS_ComponentSummarizer `
-ErrorAction Stop |
Group-Object ComponentName |
ForEach-Object {
$maxStatus = ($_.Group | Measure-Object Status -Maximum).Maximum
[PSCustomObject]@{
Component = $_.Name
Status = $maxStatus
StatusText = switch ($maxStatus) {
0 { "Healthy"; break }
1 { "Warning"; break }
2 { "Critical"; break }
default { "Unknown" }
}
StatusClass = switch ($maxStatus) {
0 { "status-ok"; break }
1 { "status-warning"; break }
2 { "status-critical"; break }
default { "status-warning" }
}
}
}
Show-Success "Found $($Components.Count) SCCM components"
return $Components
}
catch {
Show-Error "Failed to get SCCM components: $($_.Exception.Message)"
return $null
}
}
function Get-SCCMServices {
param($ComputerName, $Credential)
Show-Progress "Checking SCCM services..." -Status "SERVICES"
$Services = @(
@{Name="SMS_EXECUTIVE"; DisplayName="SMS Executive"; Critical=$true},
@{Name="SMS_SITE_COMPONENT_MANAGER"; DisplayName="Site Component Manager"; Critical=$true},
@{Name="SMS_NOTIFICATION_SERVER"; DisplayName="Notification Server"; Critical=$true},
@{Name="SMS_SQL_MONITOR"; DisplayName="SQL Monitor"; Critical=$true},
@{Name="CmRcService"; DisplayName="SMS Agent Host"; Critical=$false},
@{Name="wuauserv"; DisplayName="Windows Update"; Critical=$false},
@{Name="BITS"; DisplayName="Background Intelligent Transfer"; Critical=$false},
@{Name="Winmgmt"; DisplayName="Windows Management Instrumentation"; Critical=$true}
)
$ServiceResults = @()
foreach ($svc in $Services) {
try {
$Service = Get-Service -ComputerName $ComputerName -Name $svc.Name -ErrorAction Stop
$status = if ($Service.Status -eq "Running") { "Running" } else { "Stopped" }
$statusClass = if ($Service.Status -eq "Running") { "status-ok" } else {
if ($svc.Critical) { "status-critical" } else { "status-warning" }
}
$ServiceResults += [PSCustomObject]@{
Name = $svc.DisplayName
ServiceName = $svc.Name
Status = $status
StatusClass = $statusClass
Critical = $svc.Critical
}
if ($Service.Status -eq "Running") {
Show-Success "$($svc.DisplayName): Running"
} else {
if ($svc.Critical) {
Show-Error "$($svc.DisplayName): Stopped (CRITICAL)"
} else {
Show-Warning "$($svc.DisplayName): Stopped"
}
}
}
catch {
Show-Warning "$($svc.DisplayName): Not found"
$ServiceResults += [PSCustomObject]@{
Name = $svc.DisplayName
ServiceName = $svc.Name
Status = "Not Found"
StatusClass = "status-warning"
Critical = $svc.Critical
}
}
}
return $ServiceResults
}
function Get-SCCMDatabaseInfo {
param($CimSession, $SiteCode)
Show-Progress "Checking SCCM database information..." -Status "DATABASE"
try {
# Method 1: Try to get database info from SMS_Site class
$DatabaseInfo = Get-CimInstance -CimSession $CimSession `
-Namespace "root\sms\site_$SiteCode" `
-ClassName SMS_Site `
-ErrorAction Stop
if ($DatabaseInfo) {
# Create a custom object with the correct properties
$dbInfo = [PSCustomObject]@{
SiteCode = $DatabaseInfo.SiteCode
Version = $DatabaseInfo.Version
DatabaseName = $DatabaseInfo.DatabaseName
DatabaseServer = $DatabaseInfo.ServerName
ReportingServer = $DatabaseInfo.ReportingServerName
ReportingDatabase = $DatabaseInfo.ReportingDatabaseName
}
Show-Success "Site Code: $($dbInfo.SiteCode)"
Show-Success "SCCM Version: $($dbInfo.Version)"
if ($dbInfo.DatabaseServer -and $dbInfo.DatabaseName) {
Show-Success "Database: $($dbInfo.DatabaseName) on $($dbInfo.DatabaseServer)"
} else {
Show-Warning "Could not retrieve complete database information"
}
if ($dbInfo.ReportingServer) {
Show-Success "Reporting: $($dbInfo.ReportingDatabase) on $($dbInfo.ReportingServer)"
}
return $dbInfo
}
return $null
}
catch {
Show-Warning "Could not retrieve database information: $($_.Exception.Message)"
# Fallback: Try to get basic site info using alternative method
try {
$SiteInfo = Get-CimInstance -CimSession $CimSession `
-Namespace "root\sms\site_$SiteCode" `
-ClassName SMS_Identification `
-ErrorAction Stop
if ($SiteInfo) {
$dbInfo = [PSCustomObject]@{
SiteCode = $SiteInfo.SiteCode
Version = $SiteInfo.SMSVersion
DatabaseName = "Unknown"
DatabaseServer = "Unknown"
ReportingServer = "Unknown"
ReportingDatabase = "Unknown"
}
Show-Success "Site Code: $($dbInfo.SiteCode)"
Show-Success "SCCM Version: $($dbInfo.Version)"
Show-Warning "Could not retrieve database server details"
return $dbInfo
}
}
catch {
# Last resort: Create minimal info object
$dbInfo = [PSCustomObject]@{
SiteCode = $SiteCode
Version = "Unknown"
DatabaseName = "Unknown"
DatabaseServer = "Unknown"
ReportingServer = "Unknown"
ReportingDatabase = "Unknown"
}
Show-Success "Site Code: $SiteCode"
Show-Warning "SCCM Version: Unknown"
return $dbInfo
}
}
}
function Test-SCCMConnectivity {
param($SCCMServer, $Credential)
Show-Progress "Testing connectivity to SCCM server..." -Status "CONNECTIVITY"
# Test basic network connectivity
try {
if (Test-Connection -ComputerName $SCCMServer -Count 2 -Quiet -ErrorAction Stop) {
Show-Success "Network connectivity: SUCCESS"
} else {
Show-Error "Network connectivity: FAILED"
return $false
}
}
catch {
Show-Error "Network connectivity test failed: $($_.Exception.Message)"
return $false
}
# Test WinRM connectivity
try {
$session = New-PSSession -ComputerName $SCCMServer -Credential $Credential -ErrorAction Stop
Remove-PSSession $session
Show-Success "WinRM connectivity: SUCCESS"
return $true
}
catch {
Show-Warning "WinRM connectivity test failed. Trying alternative methods..."
# Try alternative connectivity methods
try {
# Try CIM connection
$cim = New-CimSession -ComputerName $SCCMServer -Credential $Credential -ErrorAction Stop
Remove-CimSession $cim
Show-Success "CIM connectivity: SUCCESS"
return $true
}
catch {
Show-Error "All connectivity tests failed. Please verify:"
Show-Error " 1. Server name is correct"
Show-Error " 2. Credentials have administrative privileges"
Show-Error " 3. WinRM is enabled on the SCCM server"
return $false
}
}
}
# ===================== MAIN EXECUTION ======================
try {
# Show script header
Show-ScriptHeader
# Step 1: Get SCCM Server Information
Write-Host "`n STEP 1: SCCM SERVER CONFIGURATION" -ForegroundColor Green
Write-Host " ----------------------------------" -ForegroundColor Green
$serverInfo = Get-SCCMServerInfo
if (-not $serverInfo) {
Show-Error "Server information is required to continue."
exit 1
}
$SCCMServer = $serverInfo.SCCMServer
$SMSProvider = $serverInfo.SMSProvider
$SiteCode = $serverInfo.SiteCode.ToUpper()
$OutputPath = $serverInfo.OutputPath
# Step 2: Get Credentials
Write-Host "`n STEP 2: CREDENTIALS" -ForegroundColor Green
Write-Host " --------------------" -ForegroundColor Green
$Credential = Get-SCCMCredentials
if (-not $Credential) {
Show-Error "Credentials are required to continue."
exit 1
}
# Step 3: Initialize Script
Write-Host "`n STEP 3: INITIALIZATION" -ForegroundColor Green
Write-Host " -----------------------" -ForegroundColor Green
if (-not (Initialize-Script -OutputPath $OutputPath)) {
Show-Error "Initialization failed. Exiting."
exit 1
}
# Step 4: Test Connectivity
Write-Host "`n STEP 4: CONNECTIVITY TEST" -ForegroundColor Green
Write-Host " --------------------------" -ForegroundColor Green
if (-not (Test-SCCMConnectivity -SCCMServer $SCCMServer -Credential $Credential)) {
Show-Error "Connectivity test failed. Exiting."
exit 1
}
# Step 5: Run Health Checks
Write-Host "`n STEP 5: RUNNING HEALTH CHECKS" -ForegroundColor Green
Write-Host " ------------------------------" -ForegroundColor Green
# Create CIM session for SCCM queries
Show-Progress "Connecting to SCCM SMS Provider..." -Status "CONNECTING"
try {
$CimSession = New-CimSession -ComputerName $SMSProvider -Credential $Credential -ErrorAction Stop
Show-Success "Connected to SCCM SMS Provider: $SMSProvider"
}
catch {
Show-Error "Failed to connect to SCCM SMS Provider: $($_.Exception.Message)"
Show-Error "Please verify the SMS Provider server name and credentials."
exit 1
}
# Run comprehensive health checks
$healthChecks = @()
# 1. Server Performance
$ServerPerf = Get-ServerPerformance -ComputerName $SCCMServer -Credential $Credential
# 2. SCCM Components
$Components = Get-SCCMComponents -CimSession $CimSession -SiteCode $SiteCode
# 3. SCCM Services
$Services = Get-SCCMServices -ComputerName $SCCMServer -Credential $Credential
# 4. Database Info
$DatabaseInfo = Get-SCCMDatabaseInfo -CimSession $CimSession -SiteCode $SiteCode
# Calculate summary
$CriticalComponents = if ($Components) { ($Components | Where-Object { $_.Status -eq 2 }).Count } else { 0 }
$WarningComponents = if ($Components) { ($Components | Where-Object { $_.Status -eq 1 }).Count } else { 0 }
$HealthyComponents = if ($Components) { ($Components | Where-Object { $_.Status -eq 0 }).Count } else { 0 }
$StoppedServices = if ($Services) { ($Services | Where-Object { $_.Status -ne "Running" -and $_.Critical -eq $true }).Count } else { 0 }
# Step 6: Generate Report
Write-Host "`n STEP 6: GENERATING REPORT" -ForegroundColor Green
Write-Host " --------------------------" -ForegroundColor Green
$ReportFile = "$OutputPath\SCCM_HealthCheck_$SCCMServer`_$(Get-Date -Format 'yyyyMMdd_HHmmss').html"
Show-Progress "Generating HTML report..." -Status "REPORT"
# Start HTML content
$HTML = Get-HTMLHeader -HostName $env:COMPUTERNAME -SCCMServer $SCCMServer -SiteCode $SiteCode
# ================= EXECUTIVE SUMMARY =================
$HTML += "<div class='card'><div class='card-header'><h2>Executive Summary</h2></div><div class='card-body'>"
$HTML += "<p>This report provides a comprehensive health assessment of the SCCM environment at <strong>$SCCMServer</strong>.</p>"
$HTML += "<div class='summary-grid'>"
$HTML += "<div class='summary-card total'><div class='count'>$($Components.Count)</div><div class='label'>Total Components</div></div>"
$HTML += "<div class='summary-card $(if($CriticalComponents -eq 0){'healthy'}else{'critical'})'><div class='count'>$CriticalComponents</div><div class='label'>Critical Components</div></div>"
$HTML += "<div class='summary-card $(if($WarningComponents -eq 0){'healthy'}else{'warning'})'><div class='count'>$WarningComponents</div><div class='label'>Warning Components</div></div>"
$HTML += "<div class='summary-card $(if($StoppedServices -eq 0){'healthy'}else{'critical'})'><div class='count'>$StoppedServices</div><div class='label'>Stopped Services</div></div>"
$HTML += "</div>"
$HTML += "<div class='recommendation'><h4>Key Findings</h4><ul>"
if ($CriticalComponents -gt 0) {
$HTML += "<li><strong>CRITICAL:</strong> $CriticalComponents component(s) require immediate attention</li>"
}
if ($WarningComponents -gt 0) {
$HTML += "<li><strong>WARNING:</strong> $WarningComponents component(s) need monitoring</li>"
}
if ($StoppedServices -gt 0) {
$HTML += "<li><strong>CRITICAL:</strong> $StoppedServices critical service(s) are stopped</li>"
}
if ($ServerPerf.CPUStatus -eq "Critical") {
$HTML += "<li><strong>PERFORMANCE:</strong> High CPU utilization detected</li>"
}
if ($ServerPerf.MemStatus -eq "Critical") {
$HTML += "<li><strong>PERFORMANCE:</strong> High memory utilization detected</li>"
}
$CriticalDisks = if ($ServerPerf.Disks) { ($ServerPerf.Disks | Where-Object { $_.DiskStatus -eq "Critical" }).Count } else { 0 }
if ($CriticalDisks -gt 0) {
$HTML += "<li><strong>STORAGE:</strong> Critical disk space issues detected on $CriticalDisks drive(s)</li>"
}
if ($CriticalComponents -eq 0 -and $WarningComponents -eq 0 -and $StoppedServices -eq 0) {
$HTML += "<li><strong>HEALTHY:</strong> No critical issues detected</li>"
}
$HTML += "</ul></div></div></div>"
# ================= SERVER PERFORMANCE =================
if ($ServerPerf) {
$HTML += "<div class='card'><div class='card-header'><h2>Server Performance</h2></div><div class='card-body'>"
$HTML += "<h3>CPU & Memory Utilization</h3><table class='data-table'><thead><tr><th>Metric</th><th>Current Value</th><th>Status</th><th>Details</th></tr></thead><tbody>"
# CPU Row
$cpuStatusClass = if($ServerPerf.CPUStatus -eq "Healthy"){"status-ok"}elseif($ServerPerf.CPUStatus -eq "Warning"){"status-warning"}else{"status-critical"}
$HTML += "<tr>
<td><strong>CPU Utilization</strong></td>
<td>
<div class='progress-container'>
<div class='progress-bar progress-cpu' style='width: $($ServerPerf.CPU)%'></div>
</div>
<span>$($ServerPerf.CPU)%</span>
</td>
<td><span class='status $cpuStatusClass'>$($ServerPerf.CPUStatus)</span></td>
<td>Threshold: < $($Thresholds.CPU_Green)% (Green), < $($Thresholds.CPU_Yellow)% (Yellow)</td>
</tr>"
# Memory Row
$memStatusClass = if($ServerPerf.MemStatus -eq "Healthy"){"status-ok"}elseif($ServerPerf.MemStatus -eq "Warning"){"status-warning"}else{"status-critical"}
$HTML += "<tr>
<td><strong>Memory Utilization</strong></td>
<td>
<div class='progress-container'>
<div class='progress-bar progress-memory' style='width: $($ServerPerf.Memory)%'></div>
</div>
<span>$($ServerPerf.Memory)% ($($ServerPerf.UsedMemoryGB) GB of $($ServerPerf.TotalMemoryGB) GB)</span>
</td>
<td><span class='status $memStatusClass'>$($ServerPerf.MemStatus)</span></td>
<td>Threshold: < $($Thresholds.MEM_Green)% (Green), < $($Thresholds.MEM_Yellow)% (Yellow)</td>
</tr>"
# Uptime Row
$uptimeStatusClass = if($ServerPerf.UptimeDays -le $Thresholds.MaxUptimeDays){"status-ok"}else{"status-warning"}
$uptimeStatusText = if($ServerPerf.UptimeDays -le $Thresholds.MaxUptimeDays){"OK"}else{"Warning"}
$HTML += "<tr>
<td><strong>System Uptime</strong></td>
<td>$($ServerPerf.UptimeDays) days, $($ServerPerf.UptimeHours) hours</td>
<td><span class='status $uptimeStatusClass'>$uptimeStatusText</span></td>
<td>Last reboot: $($ServerPerf.LastBoot.ToString('yyyy-MM-dd HH:mm'))</td>
</tr>"
$HTML += "</tbody></table>"
# Disk Usage
if ($ServerPerf.Disks.Count -gt 0) {
$HTML += "<h3>Disk Usage</h3><table class='data-table'><thead><tr><th>Drive</th><th>Label</th><th>Total Size</th><th>Free Space</th><th>Free %</th><th>Status</th></tr></thead><tbody>"
foreach ($Disk in $ServerPerf.Disks) {
$diskStatusClass = if($Disk.DiskStatus -eq "Healthy"){"status-ok"}elseif($Disk.DiskStatus -eq "Warning"){"status-warning"}else{"status-critical"}
$usedPct = 100 - $Disk.FreePct
$HTML += "<tr>
<td><strong>$($Disk.Drive)</strong></td>
<td>$($Disk.Label)</td>
<td>$($Disk.TotalGB) GB</td>
<td>$($Disk.FreeGB) GB</td>
<td>
<div class='progress-container'>
<div class='progress-bar progress-disk' style='width: $usedPct%'></div>
</div>
<span>$($Disk.FreePct)% free</span>
</td>
<td><span class='status $diskStatusClass'>$($Disk.DiskStatus)</span></td>
</tr>"
}
$HTML += "</tbody></table>"
}
$HTML += "</div></div>"
}
# ================= SCCM COMPONENTS =================
if ($Components) {
$HTML += "<div class='card'><div class='card-header'><h2>SCCM Component Health</h2></div><div class='card-body'>"
$HTML += "<p>Found $($Components.Count) SCCM components. Critical components are highlighted.</p>"
$HTML += "<table class='data-table'><thead><tr><th>Component Name</th><th>Status</th><th>Recommendation</th></tr></thead><tbody>"
# Sort by status (Critical first)
$SortedComponents = $Components | Sort-Object @{Expression={$_.Status}; Descending=$true}
foreach ($Comp in $SortedComponents) {
$Recommendation = switch ($Comp.Status) {
0 { "No action required" }
1 { "Monitor component and review related logs" }
2 {
switch -Wildcard ($Comp.Component) {
"*WSUS*" { "Verify WSUS synchronization, IIS health, and review WCM.log, WsyncMgr.log" }
"*HIERARCHY*" { "Check site replication status and review hman.log" }
"*SITE_COMPONENT_MANAGER*" { "Review sitecomp.log and restart if necessary" }
"*DISTRIBUTION*" { "Check distribution points and content availability" }
"*SQL*" { "Verify SQL Server connectivity and performance" }
"*MP_*" { "Check Management Point connectivity and certificates" }
default { "Review component-specific logs for detailed error information" }
}
}
}
$HTML += "<tr>
<td>$($Comp.Component)</td>
<td><span class='status $($Comp.StatusClass)'>$($Comp.StatusText)</span></td>
<td>$Recommendation</td>
</tr>"
}
$HTML += "</tbody></table></div></div>"
}
# ================= SCCM SERVICES =================
if ($Services) {
$HTML += "<div class='card'><div class='card-header'><h2>SCCM Services Status</h2></div><div class='card-body'>"
$HTML += "<table class='data-table'><thead><tr><th>Service Name</th><th>Status</th><th>Critical</th><th>Action Required</th></tr></thead><tbody>"
foreach ($Service in $Services) {
$ActionRequired = if ($Service.Status -eq "Running") {
"No action required"
} else {
if ($Service.Critical) {
"Immediate action required - restart service and investigate logs"
} else {
"Monitor and restart if impacting functionality"
}
}
$criticalText = if($Service.Critical){"Yes"}else{"No"}
$HTML += "<tr>
<td><strong>$($Service.Name)</strong><br><small>$($Service.ServiceName)</small></td>
<td><span class='status $($Service.StatusClass)'>$($Service.Status)</span></td>
<td>$criticalText</td>
<td>$ActionRequired</td>
</tr>"
}
$HTML += "</tbody></table></div></div>"
}
# ================= DATABASE INFORMATION =================
if ($DatabaseInfo) {
$HTML += "<div class='card'><div class='card-header'><h2>Database Information</h2></div><div class='card-body'>"
$HTML += "<table class='data-table'><tbody>"
# Site Code row
if ($DatabaseInfo.SiteCode) {
$HTML += "<tr><th>Site Code</th><td>$($DatabaseInfo.SiteCode)</td></tr>"
}
# SCCM Version row
if ($DatabaseInfo.Version) {
$HTML += "<tr><th>SCCM Version</th><td>$($DatabaseInfo.Version)</td></tr>"
}
# Database Server row
if ($DatabaseInfo.DatabaseServer -and $DatabaseInfo.DatabaseServer -ne "Unknown") {
$HTML += "<tr><th>Database Server</th><td>$($DatabaseInfo.DatabaseServer)</td></tr>"
} else {
$HTML += "<tr><th>Database Server</th><td><span class='status status-warning'>Not Available</span></td></tr>"
}
# Database Name row
if ($DatabaseInfo.DatabaseName -and $DatabaseInfo.DatabaseName -ne "Unknown") {
$HTML += "<tr><th>Database Name</th><td>$($DatabaseInfo.DatabaseName)</td></tr>"
} else {
$HTML += "<tr><th>Database Name</th><td><span class='status status-warning'>Not Available</span></td></tr>"
}
# Reporting Server (if available)
if ($DatabaseInfo.ReportingServer -and $DatabaseInfo.ReportingServer -ne "Unknown") {
$HTML += "<tr><th>Reporting Server</th><td>$($DatabaseInfo.ReportingServer)</td></tr>"
}
# Reporting Database (if available)
if ($DatabaseInfo.ReportingDatabase -and $DatabaseInfo.ReportingDatabase -ne "Unknown") {
$HTML += "<tr><th>Reporting Database</th><td>$($DatabaseInfo.ReportingDatabase)</td></tr>"
}
$HTML += "</tbody></table>"
# Add a note if database info is incomplete
if ($DatabaseInfo.DatabaseServer -eq "Unknown" -or $DatabaseInfo.DatabaseName -eq "Unknown") {
$HTML += "<div class='recommendation' style='margin-top: 15px;'>
<h4>Note</h4>
<p>Database server information could not be retrieved automatically. This may require manual verification.</p>
</div>"
}
$HTML += "</div></div>"
}
# ================= RECOMMENDATIONS =================
$HTML += "<div class='card'><div class='card-header'><h2>Recommendations & Next Steps</h2></div><div class='card-body'>"
$HTML += "<div class='recommendation'><h4>Priority Actions</h4><ul>"
if ($CriticalComponents -gt 0) {
$HTML += "<li><strong>CRITICAL:</strong> Address $CriticalComponents critical SCCM components immediately</li>"
}
if ($StoppedServices -gt 0) {
$HTML += "<li><strong>CRITICAL:</strong> Restart $StoppedServices stopped critical services</li>"
}
if ($ServerPerf.MemStatus -eq "Critical") {
$HTML += "<li><strong>MEMORY:</strong> Investigate high memory usage ($($ServerPerf.Memory)%). Check for memory leaks or insufficient RAM</li>"
}
$CriticalDisks = if ($ServerPerf.Disks) { ($ServerPerf.Disks | Where-Object { $_.DiskStatus -eq "Critical" }).Count } else { 0 }
if ($CriticalDisks -gt 0) {
$HTML += "<li><strong>STORAGE:</strong> Free up disk space on $CriticalDisks critical drive(s) immediately</li>"
}
$HTML += "</ul></div>"
$HTML += "<div class='recommendation'><h4>Service Issues Found</h4><ul>"
$stoppedServiceList = if ($Services) { $Services | Where-Object { $_.Status -ne "Running" } } else { @() }
foreach ($svc in $stoppedServiceList) {
$HTML += "<li><strong>$($svc.Name):</strong> $($svc.Status) - $(if($svc.Critical){'CRITICAL'}else{'Warning'})</li>"
}
$HTML += "</ul></div>"
$HTML += "<div class='recommendation'><h4>Preventive Actions</h4><ul>"
$HTML += "<li>Review SCCM component logs daily for early warning signs</li>"
$HTML += "<li>Implement regular SCCM health monitoring automation</li>"
$HTML += "<li>Schedule monthly SCCM maintenance windows</li>"
$HTML += "<li>Keep SCCM and SQL Server updated with latest cumulative updates</li>"
$HTML += "<li>Regularly validate backup and recovery procedures</li>"
$HTML += "</ul></div>"
$HTML += "<div class='recommendation'><h4>Next Steps</h4><ol>"
$HTML += "<li>Review this report with the SCCM administration team</li>"
$HTML += "<li>Create action items for all critical and warning findings</li>"
$HTML += "<li>Schedule follow-up review in 48 hours for critical items</li>"
$HTML += "<li>Implement proactive monitoring for key components</li>"
$HTML += "<li>Document resolution steps for recurring issues</li>"
$HTML += "</ol></div>"
$HTML += "</div></div>"
# Add footer
$HTML += Get-HTMLFooter
# Save HTML report
$HTML | Out-File $ReportFile -Encoding UTF8
# Clean up CIM session
Remove-CimSession $CimSession
# Step 7: Final Summary
Show-Progress "Report generation completed!" -Status "COMPLETE"
Write-Host "`n" + "="*60 -ForegroundColor Green
Write-Host " SCCM HEALTH CHECK COMPLETED!" -ForegroundColor Green
Write-Host "="*60 -ForegroundColor Green
Write-Host ""
Write-Host "📊 EXECUTIVE SUMMARY:" -ForegroundColor Cyan
Write-Host " Server Checked: $SCCMServer" -ForegroundColor White
Write-Host " Site Code: $SiteCode" -ForegroundColor White
Write-Host " Total Components: $($Components.Count)" -ForegroundColor White
Write-Host " Critical Issues: $CriticalComponents" -ForegroundColor $(if ($CriticalComponents -eq 0) { "Green" } else { "Red" })
Write-Host " Warnings: $WarningComponents" -ForegroundColor $(if ($WarningComponents -eq 0) { "Green" } else { "Yellow" })
Write-Host " Stopped Critical Services: $StoppedServices" -ForegroundColor $(if ($StoppedServices -eq 0) { "Green" } else { "Red" })
Write-Host ""
if ($CriticalComponents -gt 0 -or $StoppedServices -gt 0 -or $ServerPerf.MemStatus -eq "Critical") {
Write-Host "⚠️ ISSUES REQUIRING ATTENTION:" -ForegroundColor Yellow
if ($CriticalComponents -gt 0) {
Write-Host " • $CriticalComponents critical SCCM components" -ForegroundColor Red
}
if ($StoppedServices -gt 0) {
Write-Host " • $StoppedServices critical services stopped" -ForegroundColor Red
}
if ($ServerPerf.MemStatus -eq "Critical") {
Write-Host " • High memory usage: $($ServerPerf.Memory)%" -ForegroundColor Red
}
if ($CriticalDisks -gt 0) {
Write-Host " • $CriticalDisks drives with critical disk space" -ForegroundColor Red
}
} else {
Write-Host "✅ NO CRITICAL ISSUES DETECTED" -ForegroundColor Green
}
Write-Host ""
Write-Host "📁 REPORT GENERATED:" -ForegroundColor Cyan
Write-Host " Location: $ReportFile" -ForegroundColor Yellow
Write-Host ""
Write-Host "👤 Prepared by: $($CompanyInfo.Author)" -ForegroundColor Magenta
Write-Host "🏢 $($CompanyInfo.Name) - $($CompanyInfo.Practice)" -ForegroundColor Magenta
Write-Host ""
# Open report if requested
if ($OpenReport) {
Write-Host "Opening report in default browser..." -ForegroundColor Cyan
Start-Process $ReportFile
}
Write-Host "`nPress any key to exit..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
catch {
Show-Error "An unexpected error occurred: $($_.Exception.Message)"
Show-Error "Script execution failed."
Write-Host "`nPress any key to exit..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit 1
}
paste this script in your server with appropriate name. In my environment i have saved this script with name SCCM_V4.ps1.
Open PowerShell with Administrative privilege and change directory to the folder where script is saved. run the script.
.\SCCMVV4.ps1 ( change as per name of the your script).

The script will ask several details to inputs. in my case details are below
SCCM SERVER Name: mecm
SMS Provider Server Name: mecm
Report location where it will be stored after generating the reports.
SCCM server Username: mecm@mohammed.com.np
password of the user: ********
In your case these details may be different (Please change accordingly).
In the screenshot asked for server name
in my case it is : mecm

Provide the SMS Provider server name: in my case all components are installed in the same server so server name will be SMS provider name: mecm

Provide the location where you want to store the report generated by this script.

provide sccm administrator credential and enter.
In my case, Username will mecm because i have created user in my active directory with this name

provide the password of the mec@mohammed.com.np user.

Details will be displayed in you PowerShell script while it is running.

Go to provided location and check report has been generated or not.

Here is the Sample Report with good looking UI.
download Sample Report from here.

That’s it!
Did you enjoy the article ? share this with other hungry people and comment below your suggestions. also you might be intrested learning more about SCCM server. Here you will learn and understand about Configuring MECM Client Settings.