FAQ - IIS Functionality Testing
Overview
Test IIS certificate binding validation functionality in Nodinite. This page provides guidance on validating Phase 5 monitoring, which detects:
- ✅ Orphaned bindings—HTTPS configured but certificate missing
- ✅ Expired certificates—Binding uses expired or expiring certificates
- ✅ Hostname mismatches—Certificate doesn't match binding hostname
- ✅ Stale bindings—Old certificate in use after renewal
Understanding IIS Certificate Binding Validation
Nodinite Phase 5 monitors HTTPS bindings in IIS and cross-references them with certificates in the certificate store to ensure consistency and detect potential production issues.
Issues Detected
Nodinite Phase 5 detects the following IIS HTTPS binding issues:
| Issue | Description | Impact |
|---|---|---|
| Orphaned Bindings | IIS site configured with HTTPS binding, but the certificate referenced by thumbprint/hash is missing from the certificate store | Website fails with SSL error |
| Wrong Certificate | Binding points to certificate that doesn't match the site's hostname (e.g., binding for www.example.com but certificate is for *.contoso.com) |
SSL certificate mismatch warnings |
| Expired Bound Certificate | IIS binding uses a certificate that has already expired or is about to expire soon | Website becomes inaccessible |
| Stale Bindings | Certificate was renewed, but IIS still uses the old one | Service interruption during renewal cycle |
Why Monitor IIS Bindings?
Production Outage Prevention
Websites fail silently when bound certificates are missing or expired. Administrators often don't notice until users report issues.
Post-Renewal Issues
A common problem occurs after certificate renewal—the certificate is updated in the store, but IIS binding isn't updated to reference the new certificate.
Configuration Drift Detection
Detects when certificate stores and IIS configurations become out of sync, often due to manual interventions or partial automation failures.
Centralized View
See which certificates are actually in use by IIS vs. just installed in the certificate store, making inventory management easier.
Prerequisites
- IIS Installed: Feature only works if Internet Information Services (IIS) is installed on the monitored server
- Permissions: Account running Nodinite must have permissions to read IIS configuration and certificate stores
- Feature Enabled: IIS binding monitoring is opt-in (disabled by default)
Why Disabled by Default?
- IIS Requirement: Only works if IIS is installed (not all servers have IIS)
- Feature Server Role: Monitoring IIS is a specific use case, not universal
- Graceful Handling: If enabled but IIS not installed, reports as INFO/WARNING, not ERROR
Test Scenarios
Scenario 1: Healthy IIS Binding
Setup: Create IIS site with valid HTTPS binding and matching certificate.
# Create IIS site with valid HTTPS binding
Import-Module WebAdministration
# Create test certificate
$cert = New-SelfSignedCertificate `
-DnsName "www.testsite.local", "testsite.local" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddYears(1)
# Create IIS site
New-IISSite -Name "TestSite" `
-PhysicalPath "C:\inetpub\wwwroot\testsite" `
-BindingInformation "*:443:www.testsite.local" `
-CertificateThumbPrint $cert.Thumbprint `
-CertStoreLocation "Cert:\LocalMachine\My" `
-Protocol https
Expected Result: Nodinite shows OK status
- Certificate is present in store
- Certificate is not expired
- Hostname matches certificate subject/SAN
Scenario 2: Orphaned Binding (Missing Certificate)
Setup: Delete certificate while leaving IIS binding intact.
# Delete certificate but leave IIS binding
$thumbprint = $cert.Thumbprint
Remove-Item "Cert:\LocalMachine\My\$thumbprint" -Force
Expected Result: Nodinite shows ERROR
- Error message:
IIS site 'TestSite' binding references missing certificate (Thumbprint: ABC123...) - Binding is unresolvable and site will fail SSL handshake
Scenario 3: Expired Certificate Binding
Setup: Create site with certificate expiring within warning threshold.
# Create certificate expiring in 1 day
$shortLivedCert = New-SelfSignedCertificate `
-DnsName "expiring.testsite.local" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddDays(1)
New-IISSite -Name "ExpiringSite" `
-PhysicalPath "C:\inetpub\wwwroot\expiring" `
-BindingInformation "*:443:expiring.testsite.local" `
-CertificateThumbPrint $shortLivedCert.Thumbprint `
-CertStoreLocation "Cert:\LocalMachine\My" `
-Protocol https
# Configure Nodinite: IISBindingWarningDays = 7
Expected Result: Nodinite shows WARNING
- Warning message:
IIS site 'ExpiringSite' certificate expires in 1 day - Alert triggers before certificate actually expires, allowing time for renewal
Scenario 4: Hostname Mismatch
Setup: Create certificate for one hostname, bind to different hostname.
# Create certificate for 'site-a.local'
$cert = New-SelfSignedCertificate `
-DnsName "site-a.local" `
-CertStoreLocation Cert:\LocalMachine\My
# Bind to different hostname 'site-b.local'
New-IISSite -Name "MismatchSite" `
-PhysicalPath "C:\inetpub\wwwroot\mismatch" `
-BindingInformation "*:443:site-b.local" `
-CertificateThumbPrint $cert.Thumbprint `
-CertStoreLocation "Cert:\LocalMachine\My" `
-Protocol https
Expected Result: Nodinite shows WARNING
- Warning message:
IIS site 'MismatchSite' hostname 'site-b.local' does not match certificate 'site-a.local' - Clients connecting to this site will see SSL certificate mismatch warnings
Scenario 5: Wildcard Certificate Binding
Setup: Create wildcard certificate and bind to subdomain.
# Create wildcard certificate
$wildcard = New-SelfSignedCertificate `
-DnsName "*.example.com", "example.com" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddYears(1)
# Bind subdomain to wildcard certificate
New-IISSite -Name "SubdomainSite" `
-PhysicalPath "C:\inetpub\wwwroot\subdomain" `
-BindingInformation "*:443:www.example.com" `
-CertificateThumbPrint $wildcard.Thumbprint `
-CertStoreLocation "Cert:\LocalMachine\My" `
-Protocol https
Expected Result: Nodinite shows OK status
- Wildcard certificate
*.example.comcorrectly matches hostnamewww.example.com - Details page emphasizes wildcard usage for clarity
Note: Many organizations use wildcard certificates for convenience. The details page will clearly indicate when a binding uses a wildcard certificate to help administrators understand their certificate strategy.
Scenario 6: IIS Not Installed
Setup: Enable IIS binding monitoring on server without IIS.
# Attempt to monitor IIS bindings on server without IIS installed
# Enable "MonitorIISCertificateBindings" in Nodinite configuration
Expected Result: Nodinite shows INFO or WARNING (graceful degradation)
- Message:
IIS binding monitoring enabled but IIS is not installed on server {ServerName} - Does NOT show ERROR—gracefully handles missing IIS
- Monitoring continues on other servers in the group
Implementation Details
Data Collection Method
Nodinite uses the Microsoft.Web.Administration assembly (part of IIS) to:
- Check if IIS management APIs are available
- Enumerate all IIS sites and HTTPS bindings
- Extract certificate thumbprints from bindings
- Lookup certificates in the certificate store by thumbprint
- Cross-reference hostname in binding against certificate Subject and Subject Alternative Names (SAN)
- Evaluate certificate expiration dates
Wildcard Certificate Matching
Nodinite implements proper wildcard matching logic:
*.example.commatcheswww.example.com✅*.example.commatchesapi.example.com✅*.example.comdoes NOT matchexample.com(base domain) ✅- Multi-level wildcards like
*.*.example.comare handled per RFC 6125
SNI Support
Modern IIS configurations use Server Name Indication (SNI) to host multiple HTTPS sites on the same IP address. Nodinite collects and displays SNI information in diagnostic views for troubleshooting purposes.
Per-Server Monitoring
In a group scenario, each server is monitored independently:
- One resource per IIS site per server
- Each site shows its certificate binding status
- Group-level view summarizes bindings across all servers
Troubleshooting
Feature Is Enabled But No IIS Bindings Appear
Check:
- Is IIS actually installed? (
Get-WindowsFeature Web-Server) - Are there any HTTPS bindings configured? (
Get-IISSite | Select-Object -ExpandProperty Bindings) - Does Nodinite have permissions to read IIS configuration?
False Positive: "Expired Certificate" Warning
Check:
- Verify the warning threshold setting (
IISBindingWarningDays) - Confirm certificate expiration date:
Get-Item Cert:\LocalMachine\My\{Thumbprint} | Select-Object NotAfter - Check server system time is correct
Wildcard Certificate Shows as Mismatch
Check:
- Wildcard certificate must be formatted as
*.domain.com, notdomain.com - Certificate SAN must include the wildcard entry
- Verify binding hostname falls under wildcard scope
Best Practices
- Monitor Early: Enable IIS binding monitoring to catch issues before they impact users
- Set Appropriate Warnings: Configure warning threshold (typically 30-60 days before expiration)
- Track Wildcard Usage: Note which sites use wildcard certificates in your inventory
- Automate Renewals: Integrate certificate renewal with IIS binding updates
- Regular Audits: Periodically review bindings to ensure no stale certificates remain in use
Batch IIS Functionality Testing Script
Create all test scenarios at once with this comprehensive testing suite:
# Nodinite IIS Certificate Binding Functionality Testing Suite
# Note: This script requires Windows PowerShell 5.1 due to WebAdministration module requirements.
# If running in PowerShell 7+, it will invoke Windows PowerShell as a subprocess.
$setupScript = {
Write-Host "=== Nodinite IIS Certificate Binding Functionality Testing Suite ===" -ForegroundColor Magenta
Write-Host "Creating comprehensive IIS binding test scenarios..." -ForegroundColor Cyan
Import-Module WebAdministration -ErrorAction SilentlyContinue
$testSites = @()
$certificates = @()
$setupErrors = @()
try {
Write-Host "`n Creating comprehensive IIS binding test scenarios" -ForegroundColor Blue
# Helper function: Create IIS site with HTTPS binding using appcmd
function New-TestIISSite {
param(
[string]$SiteName,
[string]$PhysicalPath,
[string]$Hostname,
[int]$Port,
[string]$CertThumbprint
)
try {
# Create physical directory
if (-not (Test-Path $PhysicalPath)) {
New-Item -ItemType Directory -Path $PhysicalPath -Force | Out-Null
}
"Test Site" | Out-File -FilePath "$PhysicalPath\index.html"
# Remove existing site if present
Remove-IISSite -Name $SiteName -Confirm:$false -ErrorAction SilentlyContinue
# Create HTTP site first
New-IISSite -Name $SiteName -PhysicalPath $PhysicalPath -BindingInformation "*:80:$Hostname" -Protocol http -ErrorAction Stop
# Use appcmd to add HTTPS binding with certificate
$appCmdPath = "C:\Windows\System32\inetsrv\appcmd.exe"
if (Test-Path $appCmdPath) {
& $appCmdPath set site "$SiteName" /+bindings."[protocol='https',bindingInformation='*:$($Port):$Hostname']" 2>$null | Out-Null
& $appCmdPath set ssl site.name="$SiteName" /socket=$CertThumbprint 2>$null | Out-Null
}
return $true
}
catch {
Write-Host "Error creating site: $_" -ForegroundColor Red
return $false
}
}
# Scenario 1: Healthy IIS Binding
Write-Host "`n[Scenario 1/6] Creating healthy IIS binding with valid certificate..." -ForegroundColor Green
try {
$healthyCert = New-SelfSignedCertificate `
-DnsName "www.testsite.local", "testsite.local" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddYears(1) `
-FriendlyName "TestSite Healthy Certificate" `
-ErrorAction Stop
$siteDir = "C:\inetpub\wwwroot\testsite"
if (New-TestIISSite -SiteName "NodiniteTestSite" -PhysicalPath $siteDir -Hostname "www.testsite.local" -Port 443 -CertThumbprint $healthyCert.Thumbprint) {
$certificates += @{ Name = "Healthy"; Thumbprint = $healthyCert.Thumbprint }
$testSites += @{ Name = "NodiniteTestSite"; Cert = $healthyCert; Status = "Healthy" }
Write-Host " Successfully created healthy binding site (Thumbprint: $($healthyCert.Thumbprint.Substring(0,8))...)" -ForegroundColor Green
}
}
catch {
$setupErrors += "Scenario 1 (Healthy Binding): $_"
Write-Host " Failed to create healthy binding: $_" -ForegroundColor Red
}
# Scenario 2: Orphaned Binding (Missing Certificate)
Write-Host "`n[Scenario 2/6] Creating orphaned binding (will delete certificate)..." -ForegroundColor Green
try {
$orphanedCert = New-SelfSignedCertificate `
-DnsName "orphaned.testsite.local" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddYears(1) `
-FriendlyName "TestSite Orphaned Certificate" `
-ErrorAction Stop
$orphanDir = "C:\inetpub\wwwroot\orphaned"
if (New-TestIISSite -SiteName "NodiniteOrphanedSite" -PhysicalPath $orphanDir -Hostname "orphaned.testsite.local" -Port 8443 -CertThumbprint $orphanedCert.Thumbprint) {
# Delete certificate to create orphaned binding
Remove-Item "Cert:\LocalMachine\My\$($orphanedCert.Thumbprint)" -Force -ErrorAction SilentlyContinue
$certificates += @{ Name = "Orphaned"; Thumbprint = $orphanedCert.Thumbprint; Action = "Deleted" }
$testSites += @{ Name = "NodiniteOrphanedSite"; Thumbprint = $orphanedCert.Thumbprint; Status = "Orphaned" }
Write-Host " Successfully created orphaned binding (Thumbprint: $($orphanedCert.Thumbprint.Substring(0,8))... DELETED)" -ForegroundColor Green
}
}
catch {
$setupErrors += "Scenario 2 (Orphaned Binding): $_"
Write-Host " Failed to create orphaned binding: $_" -ForegroundColor Red
}
# Scenario 3: Expired Certificate Binding
Write-Host "`n[Scenario 3/6] Creating binding with expiring certificate (expires in 1 day)..." -ForegroundColor Green
try {
$expiringCert = New-SelfSignedCertificate `
-DnsName "expiring.testsite.local" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddDays(1) `
-FriendlyName "TestSite Expiring Certificate" `
-ErrorAction Stop
$expiringDir = "C:\inetpub\wwwroot\expiring"
if (New-TestIISSite -SiteName "NodiniteExpiringSite" -PhysicalPath $expiringDir -Hostname "expiring.testsite.local" -Port 9443 -CertThumbprint $expiringCert.Thumbprint) {
$certificates += @{ Name = "Expiring"; Thumbprint = $expiringCert.Thumbprint; ExpiresIn = "1 day" }
$testSites += @{ Name = "NodiniteExpiringSite"; Cert = $expiringCert; Status = "Expiring" }
Write-Host " Successfully created binding with expiring certificate (Expires: $(($expiringCert.NotAfter).ToString('yyyy-MM-dd')))" -ForegroundColor Green
}
}
catch {
$setupErrors += "Scenario 3 (Expiring Binding): $_"
Write-Host " Failed to create expiring binding: $_" -ForegroundColor Red
}
# Scenario 4: Hostname Mismatch
Write-Host "`n[Scenario 4/6] Creating binding with hostname mismatch..." -ForegroundColor Green
try {
$mismatchCert = New-SelfSignedCertificate `
-DnsName "site-a.local" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddYears(1) `
-FriendlyName "TestSite Mismatch Certificate" `
-ErrorAction Stop
$mismatchDir = "C:\inetpub\wwwroot\mismatch"
# Bind to different hostname but use certificate for site-a.local
if (New-TestIISSite -SiteName "NodiniteMismatchSite" -PhysicalPath $mismatchDir -Hostname "site-b.local" -Port 10443 -CertThumbprint $mismatchCert.Thumbprint) {
$certificates += @{ Name = "Mismatch"; Thumbprint = $mismatchCert.Thumbprint; Subject = "site-a.local"; BoundTo = "site-b.local" }
$testSites += @{ Name = "NodiniteMismatchSite"; Cert = $mismatchCert; Status = "Mismatch" }
Write-Host " Successfully created hostname mismatch binding (Cert: site-a.local, Binding: site-b.local)" -ForegroundColor Green
}
}
catch {
$setupErrors += "Scenario 4 (Hostname Mismatch): $_"
Write-Host " Failed to create mismatch binding: $_" -ForegroundColor Red
}
# Scenario 5: Wildcard Certificate
Write-Host "`n[Scenario 5/6] Creating binding with wildcard certificate..." -ForegroundColor Green
try {
$wildcardCert = New-SelfSignedCertificate `
-DnsName "*.example.local", "example.local" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddYears(1) `
-FriendlyName "TestSite Wildcard Certificate" `
-ErrorAction Stop
$wildcardDir = "C:\inetpub\wwwroot\wildcard"
if (New-TestIISSite -SiteName "NodiniteWildcardSite" -PhysicalPath $wildcardDir -Hostname "www.example.local" -Port 11443 -CertThumbprint $wildcardCert.Thumbprint) {
$certificates += @{ Name = "Wildcard"; Thumbprint = $wildcardCert.Thumbprint; Subject = "*.example.local" }
$testSites += @{ Name = "NodiniteWildcardSite"; Cert = $wildcardCert; Status = "Wildcard" }
Write-Host " Successfully created wildcard certificate binding (*.example.local bound to www.example.local)" -ForegroundColor Green
}
}
catch {
$setupErrors += "Scenario 5 (Wildcard Certificate): $_"
Write-Host " Failed to create wildcard binding: $_" -ForegroundColor Red
}
# Scenario 6: SNI Enabled Binding
Write-Host "`n[Scenario 6/6] Creating binding with SNI enabled..." -ForegroundColor Green
try {
$sniCert = New-SelfSignedCertificate `
-DnsName "sni.testsite.local" `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddYears(1) `
-FriendlyName "TestSite SNI Certificate" `
-ErrorAction Stop
$sniDir = "C:\inetpub\wwwroot\sni"
if (New-TestIISSite -SiteName "NodiniteSNISite" -PhysicalPath $sniDir -Hostname "sni.testsite.local" -Port 12443 -CertThumbprint $sniCert.Thumbprint) {
$certificates += @{ Name = "SNI"; Thumbprint = $sniCert.Thumbprint; SNIEnabled = "Yes" }
$testSites += @{ Name = "NodiniteSNISite"; Cert = $sniCert; Status = "SNI" }
Write-Host " Successfully created SNI-enabled binding (sni.testsite.local)" -ForegroundColor Green
}
}
catch {
$setupErrors += "Scenario 6 (SNI Binding): $_"
Write-Host " Failed to create SNI binding: $_" -ForegroundColor Red
}
}
catch {
Write-Host " Unexpected error during test setup: $_" -ForegroundColor Red
}
# Summary Report
Write-Host "`n" -ForegroundColor White
Write-Host "=== Test Setup Summary ===" -ForegroundColor Cyan
Write-Host " IIS Sites Created: $($testSites.Count)" -ForegroundColor Blue
Write-Host " Certificates Created: $($certificates.Count)" -ForegroundColor Blue
if ($testSites.Count -gt 0) {
Write-Host "`nIIS Test Sites:" -ForegroundColor Green
foreach ($site in $testSites) {
Write-Host " • $($site.Name) - Status: $($site.Status)" -ForegroundColor White
}
}
if ($setupErrors.Count -gt 0) {
Write-Host "`n Errors Encountered:" -ForegroundColor Yellow
foreach ($setupError in $setupErrors) {
Write-Host " • $setupError" -ForegroundColor Yellow
}
}
Write-Host "`n Configuration:" -ForegroundColor Blue
Write-Host " • Set MonitorIISCertificateBindings=true in Nodinite configuration" -ForegroundColor White
Write-Host " • Set IISBindingWarningDays=7 (or appropriate threshold)" -ForegroundColor White
Write-Host " • Restart Nodinite monitoring service" -ForegroundColor White
Write-Host " • Verify IIS binding resources appear in Monitor View" -ForegroundColor White
Write-Host "`n Setup complete! IIS binding scenarios ready for monitoring validation." -ForegroundColor Green
}
# For PowerShell 7+, invoke Windows PowerShell 5.1
if ($PSVersionTable.PSVersion.Major -ge 7) {
Write-Host "`nDetected PowerShell $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor). Invoking Windows PowerShell 5.1 for module compatibility..." -ForegroundColor Yellow
& "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -Command $setupScript
} else {
# Windows PowerShell 5.1 - run directly
& $setupScript
}
Cleanup: Remove IIS Binding Test Scenarios
Remove all test IIS sites and certificates after validation:
# Cleanup Nodinite IIS Certificate Binding Test Scenarios
# Note: This script requires Windows PowerShell 5.1 due to WebAdministration module requirements.
# If running in PowerShell 7+, it will invoke Windows PowerShell as a subprocess.
$cleanupScript = {
Import-Module WebAdministration -ErrorAction SilentlyContinue
Write-Host "=== Cleanup: Nodinite IIS Binding Test Scenarios ===" -ForegroundColor Magenta
Write-Host "Removing all test IIS sites and certificates..." -ForegroundColor Cyan
$testSiteNames = @(
"NodiniteTestSite",
"NodiniteOrphanedSite",
"NodiniteExpiringSite",
"NodiniteMismatchSite",
"NodiniteWildcardSite",
"NodiniteSNISite"
)
$testCertFriendlyNames = @(
"TestSite Healthy Certificate",
"TestSite Orphaned Certificate",
"TestSite Expiring Certificate",
"TestSite Mismatch Certificate",
"TestSite Wildcard Certificate",
"TestSite SNI Certificate"
)
$removedSites = 0
$removedCerts = 0
$cleanupErrors = @()
# Remove IIS Sites
Write-Host "`n Removing IIS test sites..." -ForegroundColor Blue
foreach ($siteName in $testSiteNames) {
try {
Remove-IISSite -Name $siteName -Confirm:$false -ErrorAction SilentlyContinue
Write-Host " Removed IIS site '$siteName'" -ForegroundColor Green
$removedSites++
}
catch {
$cleanupErrors += "Failed to remove site '$siteName': $_"
Write-Host " Failed to remove site '$siteName': $_" -ForegroundColor Red
}
}
# Remove Physical Directories
Write-Host "`n Removing test site directories..." -ForegroundColor Blue
$testDirs = @(
"C:\inetpub\wwwroot\testsite",
"C:\inetpub\wwwroot\orphaned",
"C:\inetpub\wwwroot\expiring",
"C:\inetpub\wwwroot\mismatch",
"C:\inetpub\wwwroot\wildcard",
"C:\inetpub\wwwroot\sni"
)
foreach ($dir in $testDirs) {
try {
if (Test-Path $dir) {
Remove-Item -Path $dir -Recurse -Force -ErrorAction Stop
Write-Host " Removed directory '$dir'" -ForegroundColor Green
}
}
catch {
$cleanupErrors += "Failed to remove directory '$dir': $_"
Write-Host " Failed to remove directory '$dir': $_" -ForegroundColor Yellow
}
}
# Remove Certificates
Write-Host "`n Removing test certificates..." -ForegroundColor Blue
foreach ($friendlyName in $testCertFriendlyNames) {
try {
$certs = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.FriendlyName -eq $friendlyName }
foreach ($cert in $certs) {
Remove-Item "Cert:\LocalMachine\My\$($cert.Thumbprint)" -Force -ErrorAction Stop
Write-Host " Removed certificate '$friendlyName' (Thumbprint: $($cert.Thumbprint.Substring(0,8))...)" -ForegroundColor Green
$removedCerts++
}
}
catch {
$cleanupErrors += "Failed to remove certificate '$friendlyName': $_"
Write-Host " Failed to remove certificate '$friendlyName': $_" -ForegroundColor Yellow
}
}
# Cleanup Summary
Write-Host "`n=== Cleanup Summary ===" -ForegroundColor Cyan
Write-Host " IIS Sites Removed: $removedSites" -ForegroundColor Green
Write-Host " Certificates Removed: $removedCerts" -ForegroundColor Green
if ($cleanupErrors.Count -gt 0) {
Write-Host "`n Errors During Cleanup:" -ForegroundColor Yellow
foreach ($cleanupError in $cleanupErrors) {
Write-Host " • $cleanupError" -ForegroundColor Yellow
}
Write-Host "`nNote: Some resources may have already been removed. Review above for any critical errors." -ForegroundColor Yellow
}
Write-Host "`n Cleanup complete! Test scenarios removed." -ForegroundColor Green
}
# For PowerShell 7+, invoke Windows PowerShell 5.1
if ($PSVersionTable.PSVersion.Major -ge 7) {
Write-Host "Detected PowerShell $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor). Invoking Windows PowerShell 5.1 for module compatibility..." -ForegroundColor Yellow
& "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -Command $cleanupScript
} else {
# Windows PowerShell 5.1 - run directly
& $cleanupScript
}