Last active
September 9, 2025 12:55
-
-
Save kevinblumenfeld/bbf582b7356f32d254f45bdb923c3779 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Get-ADFSReport - Comprehensive ADFS Environment Analysis and Health Check | |
.DESCRIPTION | |
This script performs a comprehensive analysis of an Active Directory Federation Services (ADFS) environment. | |
Returns structured PSCustomObjects that can be exported to CSV, JSON, XML, or other formats. | |
.PARAMETER IncludePerformanceCounters | |
Switch to include performance counter analysis (may take additional time) | |
.PARAMETER EventLogDays | |
Number of days of event logs to analyze. Default: 7 days | |
.PARAMETER SanitizeOutput | |
Switch to sanitize sensitive information in the output | |
.EXAMPLE | |
$report = Get-ADFSReport | |
$report | Export-Csv "adfs-analysis.csv" -NoTypeInformation | |
.EXAMPLE | |
$report = Get-ADFSReport -EventLogDays 30 | |
$report | ConvertTo-Json | Out-File "adfs-analysis.json" | |
.EXAMPLE | |
$report = Get-ADFSReport -SanitizeOutput | |
$report | Where-Object Status -eq "CRITICAL" | Format-Table | |
.NOTES | |
Author: ADFS Health Check Script | |
Version: 2.1 | |
Requires: PowerShell 5.0+, ADFS PowerShell Module | |
Permissions: Local Administrator on ADFS server | |
#> | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory = $false)] | |
[switch]$IncludePerformanceCounters, | |
[Parameter(Mandatory = $false)] | |
[int]$EventLogDays = 7, | |
[Parameter(Mandatory = $false)] | |
[switch]$SanitizeOutput | |
) | |
# ============================================================================= | |
# SCRIPT VARIABLES AND INITIALIZATION | |
# ============================================================================= | |
$script:ADFSVersionMajor = 0 | |
$script:ADFSVersionMinor = 0 | |
$script:ADFSFarmBehaviorLevel = 0 | |
# ============================================================================= | |
# UTILITY FUNCTIONS | |
# ============================================================================= | |
function New-ADFSAnalysisResult { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory = $true)] | |
[string]$Category, | |
[Parameter(Mandatory = $true)] | |
[string]$Property, | |
[Parameter(Mandatory = $true)] | |
[AllowEmptyString()] | |
[string]$Value, | |
[Parameter(Mandatory = $true)] | |
[string]$Relevance, | |
[Parameter(Mandatory = $true)] | |
[ValidateSet("OK", "WARNING", "CRITICAL", "ERROR", "INFO")] | |
[string]$Status, | |
[Parameter(Mandatory = $false)] | |
[string]$Recommendation = "", | |
[Parameter(Mandatory = $false)] | |
[string]$Server = $env:COMPUTERNAME | |
) | |
return [PSCustomObject]@{ | |
PSTypeName = 'ADFS.AnalysisResult' | |
Server = $Server | |
Category = $Category | |
Property = $Property | |
Value = $Value | |
Relevance = $Relevance | |
Status = $Status | |
Recommendation = $Recommendation | |
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" | |
} | |
} | |
function Get-SanitizedValue { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory = $true)] | |
[AllowEmptyString()] | |
[string]$Value, | |
[Parameter(Mandatory = $false)] | |
[string]$SanitizedText = "***REDACTED***" | |
) | |
if ($SanitizeOutput) { | |
return $SanitizedText | |
} else { | |
return $Value | |
} | |
} | |
function Initialize-ADFSAnalysis { | |
[CmdletBinding()] | |
param() | |
Write-Verbose "=== ADFS COMPREHENSIVE ANALYSIS TOOL ===" | |
Write-Verbose "Starting ADFS environment analysis..." | |
Write-Verbose "Analysis Time: $(Get-Date)" | |
# Check if running on Windows | |
if (-not ($PSVersionTable.PSVersion.Major -ge 5 -and [Environment]::OSVersion.Platform -eq "Win32NT")) { | |
throw "This script requires Windows PowerShell 5.0 or later on Windows." | |
} | |
# Check if ADFS PowerShell module is available | |
try { | |
Import-Module ADFS -ErrorAction Stop | |
Write-Verbose "ADFS PowerShell module loaded successfully" | |
} catch { | |
throw "ADFS PowerShell module not available. This script must run on an ADFS server." | |
} | |
# Detect ADFS version from Farm Behavior Level | |
try { | |
$farmInfo = Get-AdfsFarmInformation -ErrorAction Stop | |
$script:ADFSFarmBehaviorLevel = $farmInfo.FarmBehavior | |
# Map Farm Behavior Level to ADFS version | |
switch ($script:ADFSFarmBehaviorLevel) { | |
1 { $script:ADFSVersionMajor = 3 } | |
3 { $script:ADFSVersionMajor = 4 } | |
4 { $script:ADFSVersionMajor = 5 } | |
5 { $script:ADFSVersionMajor = 6 } | |
default { $script:ADFSVersionMajor = 0 } | |
} | |
Write-Verbose "ADFS Farm Behavior Level: $script:ADFSFarmBehaviorLevel" | |
} catch { | |
Write-Warning "Could not detect ADFS version: $($_.Exception.Message)" | |
} | |
return $true | |
} | |
# ============================================================================= | |
# ANALYSIS FUNCTIONS | |
# ============================================================================= | |
function Get-ADFSServiceAnalysis { | |
[CmdletBinding()] | |
param() | |
Write-Verbose "Analyzing ADFS Service and Farm Information..." | |
$results = @() | |
try { | |
$adfsService = Get-Service -Name "adfssrv" | |
$results += New-ADFSAnalysisResult -Category "Service" -Property "ADFS Service Status" -Value $adfsService.Status -Relevance "Critical - ADFS must be running for federation to work" -Status $(if ($adfsService.Status -eq "Running") { "OK" } else { "CRITICAL" }) | |
$adfsServiceAccount = (Get-WmiObject -Class Win32_Service -Filter "Name='adfssrv'").StartName | |
$results += New-ADFSAnalysisResult -Category "Service" -Property "Service Account" -Value $adfsServiceAccount -Relevance "Service account determines permissions and security context for ADFS operations" -Status "INFO" | |
} catch { | |
$results += New-ADFSAnalysisResult -Category "Service" -Property "ADFS Service" -Value "Error: $($_.Exception.Message)" -Relevance "Critical - Cannot analyze ADFS without service access" -Status "ERROR" | |
} | |
try { | |
$farmInfo = Get-AdfsFarmInformation | |
$results += New-ADFSAnalysisResult -Category "Farm" -Property "Farm Behavior Level" -Value $farmInfo.FarmBehavior -Relevance "Determines available features and compatibility with ADFS versions" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Farm" -Property "Current Farm Node" -Value $farmInfo.CurrentFarmNode -Relevance "Identifies which server in the farm you're analyzing" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Farm" -Property "Farm Nodes" -Value ($farmInfo.FarmNodes -join ", ") -Relevance "Shows all servers in the ADFS farm for redundancy and load balancing" -Status "INFO" | |
if ($farmInfo.FarmNodes.Count -eq 1) { | |
$results += New-ADFSAnalysisResult -Category "Farm" -Property "High Availability" -Value "Single Node" -Relevance "Single point of failure - consider adding more farm nodes" -Status "WARNING" -Recommendation "Add additional ADFS servers for redundancy" | |
} else { | |
$results += New-ADFSAnalysisResult -Category "Farm" -Property "High Availability" -Value "Multi-Node ($($farmInfo.FarmNodes.Count) nodes)" -Relevance "Good redundancy setup with multiple ADFS servers" -Status "OK" | |
} | |
} catch { | |
$results += New-ADFSAnalysisResult -Category "Farm" -Property "Farm Information" -Value "Error: $($_.Exception.Message)" -Relevance "Farm information shows topology and health" -Status "ERROR" | |
} | |
return $results | |
} | |
function Get-ADFSPropertiesAnalysis { | |
[CmdletBinding()] | |
param() | |
Write-Verbose "Analyzing ADFS Properties and Global Settings..." | |
$results = @() | |
try { | |
$adfsProperties = Get-AdfsProperties | |
$results += New-ADFSAnalysisResult -Category "Properties" -Property "Hostname" -Value $adfsProperties.HostName -Relevance "Public FQDN for ADFS - must match SSL certificate and DNS" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Properties" -Property "HTTPS Port" -Value $adfsProperties.HttpsPort -Relevance "Port for HTTPS communications - typically 443" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Properties" -Property "HTTP Port" -Value $adfsProperties.HttpPort -Relevance "HTTP port - should be disabled for security" -Status $(if ($adfsProperties.HttpPort -eq 80) { "WARNING" } else { "INFO" }) -Recommendation $(if ($adfsProperties.HttpPort -eq 80) { "Consider disabling HTTP for security" } else { "" }) | |
$results += New-ADFSAnalysisResult -Category "Properties" -Property "Federation Service Name" -Value $adfsProperties.FederationServiceName -Relevance "Identifier for the federation service in trust relationships" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Properties" -Property "Federation Service Display Name" -Value $adfsProperties.FederationServiceDisplayName -Relevance "User-friendly name shown in sign-in pages" -Status "INFO" | |
# Token Lifetime Settings | |
$results += New-ADFSAnalysisResult -Category "Tokens" -Property "Access Token Lifetime" -Value "$($adfsProperties.AccessTokenLifetime) minutes" -Relevance "How long access tokens are valid - affects security vs usability" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Tokens" -Property "ID Token Lifetime" -Value "$($adfsProperties.IdTokenLifetime) minutes" -Relevance "How long ID tokens are valid for OpenID Connect" -Status "INFO" | |
# Security Settings | |
$results += New-ADFSAnalysisResult -Category "Security" -Property "Extended Protection Mode" -Value $adfsProperties.ExtendedProtectionTokenCheck -Relevance "Protection against token replay attacks" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Security" -Property "Browser SSO Enabled" -Value $adfsProperties.BrowserSsoEnabled -Relevance "Enables single sign-on across browser sessions" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Security" -Property "Browser SSO Lifetime" -Value "$($adfsProperties.BrowserSsoLifetime) minutes" -Relevance "How long browser SSO sessions last" -Status "INFO" | |
} catch { | |
$results += New-ADFSAnalysisResult -Category "Properties" -Property "ADFS Properties" -Value "Error: $($_.Exception.Message)" -Relevance "Global settings affect all ADFS operations" -Status "ERROR" | |
} | |
return $results | |
} | |
function Get-ADFSCertificatesAnalysis { | |
[CmdletBinding()] | |
param() | |
Write-Verbose "Analyzing ADFS Certificates..." | |
$results = @() | |
try { | |
$certificates = Get-AdfsCertificate | |
foreach ($cert in $certificates) { | |
$daysUntilExpiry = ($cert.Certificate.NotAfter - (Get-Date)).Days | |
$status = if ($daysUntilExpiry -lt 30) { "CRITICAL" } elseif ($daysUntilExpiry -lt 90) { "WARNING" } else { "OK" } | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "$($cert.CertificateType) Certificate Subject" -Value $cert.Certificate.Subject -Relevance "Certificate subject identifies the certificate purpose" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "$($cert.CertificateType) Certificate Issuer" -Value $cert.Certificate.Issuer -Relevance "Certificate authority that issued this certificate" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "$($cert.CertificateType) Certificate Expiry" -Value $cert.Certificate.NotAfter.ToString("yyyy-MM-dd") -Relevance "Certificate expiration date" -Status $status -Recommendation $(if ($daysUntilExpiry -lt 90) { "Certificate expires in $daysUntilExpiry days - plan renewal" } else { "" }) | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "$($cert.CertificateType) Certificate Days Until Expiry" -Value $daysUntilExpiry -Relevance "Days remaining before certificate expires" -Status $status | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "$($cert.CertificateType) Certificate Thumbprint" -Value (Get-SanitizedValue -Value $cert.Certificate.Thumbprint) -Relevance "Unique certificate identifier" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "$($cert.CertificateType) Certificate Is Primary" -Value $cert.IsPrimary -Relevance "Whether this is the primary certificate for this type" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "$($cert.CertificateType) Certificate Has Private Key" -Value $cert.Certificate.HasPrivateKey -Relevance "Private key availability for signing/decryption operations" -Status $(if ($cert.Certificate.HasPrivateKey) { "OK" } else { "WARNING" }) | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "$($cert.CertificateType) Certificate Key Size" -Value $cert.Certificate.PublicKey.Key.KeySize -Relevance "Key strength - minimum 2048 bits recommended" -Status $(if ($cert.Certificate.PublicKey.Key.KeySize -ge 2048) { "OK" } else { "WARNING" }) | |
} | |
# Certificate rollover capability | |
$tokenSigningCerts = $certificates | Where-Object { $_.CertificateType -eq "Token-Signing" } | |
if ($tokenSigningCerts.Count -gt 1) { | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "Auto Certificate Rollover Capability" -Value "Multiple token signing certificates available" -Relevance "Multiple certificates enable zero-downtime certificate rollover" -Status "OK" | |
} else { | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "Auto Certificate Rollover Capability" -Value "Single token signing certificate" -Relevance "Single certificate may cause downtime during renewal" -Status "WARNING" -Recommendation "Configure additional token signing certificate for rollover capability" | |
} | |
} catch { | |
$results += New-ADFSAnalysisResult -Category "Certificates" -Property "Certificate Analysis" -Value "Error: $($_.Exception.Message)" -Relevance "Certificates are critical for ADFS security and trust" -Status "ERROR" | |
} | |
return $results | |
} | |
function Get-RelyingPartyTrustsAnalysis { | |
[CmdletBinding()] | |
param() | |
Write-Verbose "Analyzing Relying Party Trusts..." | |
$results = @() | |
try { | |
$relyingParties = Get-AdfsRelyingPartyTrust | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "Total Relying Party Trusts" -Value $relyingParties.Count -Relevance "Number of applications trusting this ADFS for authentication" -Status "INFO" | |
$enabledRPs = $relyingParties | Where-Object { $_.Enabled } | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "Enabled Relying Party Trusts" -Value $enabledRPs.Count -Relevance "Number of active application trusts" -Status "INFO" | |
# Analyze each relying party individually | |
foreach ($rp in $relyingParties) { | |
$rpPrefix = "RP_$($rp.Name.Replace(' ', '_'))" | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Name" -Value $rp.Name -Relevance "Relying party display name" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Enabled" -Value $rp.Enabled -Relevance "Whether this relying party is active" -Status $(if ($rp.Enabled) { "OK" } else { "INFO" }) | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Identifier" -Value ($rp.Identifier -join "; ") -Relevance "Unique identifiers for this application" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Signature Algorithm" -Value $rp.SignatureAlgorithm -Relevance "Algorithm used to sign tokens" -Status $(if ($rp.SignatureAlgorithm -eq "http://www.w3.org/2000/09/xmldsig#rsa-sha1") { "WARNING" } else { "OK" }) -Recommendation $(if ($rp.SignatureAlgorithm -eq "http://www.w3.org/2000/09/xmldsig#rsa-sha1") { "Upgrade to SHA256 signature algorithm" } else { "" }) | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Token Encryption" -Value $rp.EncryptClaims -Relevance "Whether tokens are encrypted for enhanced security" -Status $(if ($rp.EncryptClaims) { "OK" } else { "INFO" }) -Recommendation $(if (-not $rp.EncryptClaims) { "Consider enabling token encryption for sensitive applications" } else { "" }) | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Monitoring Enabled" -Value $rp.MonitoringEnabled -Relevance "Whether this RP is included in health monitoring" -Status $(if ($rp.MonitoringEnabled) { "OK" } else { "INFO" }) | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Auto Update" -Value $rp.AutoUpdateEnabled -Relevance "Whether metadata is automatically updated" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Metadata URL" -Value (Get-SanitizedValue -Value $rp.MetadataUrl -SanitizedText "***CONFIGURED***") -Relevance "Federation metadata URL for automatic updates" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Token Lifetime" -Value "$($rp.TokenLifetime) minutes" -Relevance "How long issued tokens remain valid" -Status $(if ($rp.TokenLifetime -gt 480) { "WARNING" } else { "OK" }) -Recommendation $(if ($rp.TokenLifetime -gt 480) { "Consider reducing token lifetime for better security" } else { "" }) | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix SAML Endpoints Count" -Value $(if ($rp.SamlEndpoints) { $rp.SamlEndpoints.Count } else { 0 }) -Relevance "Number of SAML 2.0 endpoints configured" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix WS-Fed Endpoint" -Value $rp.WSFedEndpoint -Relevance "WS-Federation endpoint URL" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Has Issuance Rules" -Value $(-not [string]::IsNullOrEmpty($rp.IssuanceTransformRules)) -Relevance "Whether claim issuance rules are configured" -Status $(if (-not [string]::IsNullOrEmpty($rp.IssuanceTransformRules)) { "OK" } else { "WARNING" }) -Recommendation $(if ([string]::IsNullOrEmpty($rp.IssuanceTransformRules)) { "Configure issuance transform rules for claim processing" } else { "" }) | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Has Authorization Rules" -Value $(-not [string]::IsNullOrEmpty($rp.IssuanceAuthorizationRules)) -Relevance "Whether authorization rules are configured" -Status "INFO" | |
# Count claim rules | |
if (-not [string]::IsNullOrEmpty($rp.IssuanceTransformRules)) { | |
$ruleCount = ($rp.IssuanceTransformRules -split '@RuleName').Count - 1 | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "$rpPrefix Issuance Rules Count" -Value $ruleCount -Relevance "Number of claim issuance rules" -Status $(if ($ruleCount -gt 10) { "WARNING" } else { "INFO" }) -Recommendation $(if ($ruleCount -gt 10) { "Consider consolidating rules for better performance" } else { "" }) | |
} | |
} | |
} catch { | |
$results += New-ADFSAnalysisResult -Category "Relying Parties" -Property "Relying Party Analysis" -Value "Error: $($_.Exception.Message)" -Relevance "Relying parties are applications that trust ADFS" -Status "ERROR" | |
} | |
return $results | |
} | |
function Get-ClaimsProviderTrustsAnalysis { | |
[CmdletBinding()] | |
param() | |
Write-Verbose "Analyzing Claims Provider Trusts..." | |
$results = @() | |
try { | |
$claimsProviders = Get-AdfsClaimsProviderTrust | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "Total Claims Provider Trusts" -Value $claimsProviders.Count -Relevance "Number of identity providers trusted by this ADFS" -Status "INFO" | |
foreach ($cp in $claimsProviders) { | |
$cpPrefix = "CP_$($cp.Name.Replace(' ', '_'))" | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "$cpPrefix Name" -Value $cp.Name -Relevance "Claims provider display name" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "$cpPrefix Enabled" -Value $cp.Enabled -Relevance "Whether this provider is active" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "$cpPrefix Identifier" -Value ($cp.Identifier -join "; ") -Relevance "Unique identifiers for this provider" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "$cpPrefix Signature Algorithm" -Value $cp.SignatureAlgorithm -Relevance "Algorithm used to verify incoming tokens" -Status $(if ($cp.SignatureAlgorithm -eq "http://www.w3.org/2000/09/xmldsig#rsa-sha1") { "WARNING" } else { "OK" }) -Recommendation $(if ($cp.SignatureAlgorithm -eq "http://www.w3.org/2000/09/xmldsig#rsa-sha1") { "Upgrade to SHA256 signature algorithm" } else { "" }) | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "$cpPrefix Monitoring Enabled" -Value $cp.MonitoringEnabled -Relevance "Whether this provider is included in health monitoring" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "$cpPrefix Auto Update" -Value $cp.AutoUpdateEnabled -Relevance "Whether provider metadata is automatically updated" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "$cpPrefix Metadata URL" -Value (Get-SanitizedValue -Value $cp.MetadataUrl -SanitizedText "***CONFIGURED***") -Relevance "Federation metadata URL" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "$cpPrefix WS-Fed Endpoint" -Value $cp.WSFedEndpoint -Relevance "WS-Federation endpoint URL" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "$cpPrefix Has Acceptance Rules" -Value $(-not [string]::IsNullOrEmpty($cp.AcceptanceTransformRules)) -Relevance "Whether claim acceptance rules are configured" -Status "INFO" | |
} | |
} catch { | |
$results += New-ADFSAnalysisResult -Category "Claims Providers" -Property "Claims Provider Analysis" -Value "Error: $($_.Exception.Message)" -Relevance "Claims providers are trusted identity sources" -Status "ERROR" | |
} | |
return $results | |
} | |
function Get-AuthenticationPoliciesAnalysis { | |
[CmdletBinding()] | |
param() | |
Write-Verbose "Analyzing Authentication Policies..." | |
$results = @() | |
try { | |
$globalAuthPolicy = Get-AdfsGlobalAuthenticationPolicy | |
$results += New-ADFSAnalysisResult -Category "Authentication" -Property "Primary Intranet Auth Methods" -Value ($globalAuthPolicy.PrimaryIntranetAuthenticationProvider -join ", ") -Relevance "Authentication methods for internal network users" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Authentication" -Property "Primary Extranet Auth Methods" -Value ($globalAuthPolicy.PrimaryExtranetAuthenticationProvider -join ", ") -Relevance "Authentication methods for external network users" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Authentication" -Property "Additional Auth Methods" -Value ($globalAuthPolicy.AdditionalAuthenticationProvider -join ", ") -Relevance "MFA methods available" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Authentication" -Property "Device Authentication Enabled" -Value $globalAuthPolicy.DeviceAuthenticationEnabled -Relevance "Whether device-based authentication is enabled" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Authentication" -Property "Windows Integrated Fallback" -Value $globalAuthPolicy.WindowsIntegratedFallbackEnabled -Relevance "Fallback to Windows authentication when other methods fail" -Status "INFO" | |
} catch { | |
$results += New-ADFSAnalysisResult -Category "Authentication" -Property "Authentication Policy Analysis" -Value "Error: $($_.Exception.Message)" -Relevance "Authentication policies control how users authenticate" -Status "ERROR" | |
} | |
return $results | |
} | |
function Get-HealthAndMonitoringAnalysis { | |
[CmdletBinding()] | |
param() | |
Write-Verbose "Performing Health Checks..." | |
$results = @() | |
# Event Log Analysis | |
try { | |
$startTime = (Get-Date).AddDays(-$EventLogDays) | |
$adfsAdminLogs = Get-WinEvent -FilterHashtable @{LogName = 'AD FS/Admin'; StartTime = $startTime } -MaxEvents 1000 -ErrorAction SilentlyContinue | |
if ($adfsAdminLogs) { | |
$errorCount = ($adfsAdminLogs | Where-Object { $_.LevelDisplayName -eq "Error" }).Count | |
$warningCount = ($adfsAdminLogs | Where-Object { $_.LevelDisplayName -eq "Warning" }).Count | |
$totalEvents = $adfsAdminLogs.Count | |
$results += New-ADFSAnalysisResult -Category "Health" -Property "Total Events ($EventLogDays days)" -Value $totalEvents -Relevance "Total ADFS admin log events indicate activity level" -Status "INFO" | |
$results += New-ADFSAnalysisResult -Category "Health" -Property "Errors ($EventLogDays days)" -Value $errorCount -Relevance "Error events in ADFS admin log indicate issues" -Status $(if ($errorCount -gt 0) { "WARNING" } else { "OK" }) | |
$results += New-ADFSAnalysisResult -Category "Health" -Property "Warnings ($EventLogDays days)" -Value $warningCount -Relevance "Warning events may indicate potential issues" -Status $(if ($warningCount -gt 10) { "WARNING" } else { "INFO" }) | |
} else { | |
$results += New-ADFSAnalysisResult -Category "Health" -Property "Event Log Access" -Value "No events found or access denied" -Relevance "May indicate permissions issue or very quiet period" -Status "WARNING" | |
} | |
} catch { | |
$results += New-ADFSAnalysisResult -Category "Health" -Property "Event Log Analysis" -Value "Error: $($_.Exception.Message)" -Relevance "Event logs provide operational health information" -Status "WARNING" | |
} | |
return $results | |
} | |
# ============================================================================= | |
# MAIN FUNCTION | |
# ============================================================================= | |
function Get-ADFSReport { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory = $false)] | |
[switch]$IncludePerformanceCounters, | |
[Parameter(Mandatory = $false)] | |
[int]$EventLogDays = 7, | |
[Parameter(Mandatory = $false)] | |
[switch]$SanitizeOutput | |
) | |
# Set script-level variables from parameters | |
$script:SanitizeOutput = $SanitizeOutput.IsPresent | |
try { | |
# Initialize the analysis | |
if (-not (Initialize-ADFSAnalysis)) { | |
return $null | |
} | |
# Collect all analysis results | |
$allResults = @() | |
$allResults += Get-ADFSServiceAnalysis | |
$allResults += Get-ADFSPropertiesAnalysis | |
$allResults += Get-ADFSCertificatesAnalysis | |
$allResults += Get-RelyingPartyTrustsAnalysis | |
$allResults += Get-ClaimsProviderTrustsAnalysis | |
$allResults += Get-AuthenticationPoliciesAnalysis | |
$allResults += Get-HealthAndMonitoringAnalysis | |
# Add performance counters if requested | |
if ($IncludePerformanceCounters) { | |
$allResults += Get-PerformanceCountersAnalysis | |
} | |
Write-Verbose "Analysis complete. Generated $($allResults.Count) analysis results." | |
# Return the complete collection of PSCustomObjects | |
return $allResults | |
} catch { | |
Write-Error "Error during analysis: $($_.Exception.Message)" | |
return @(New-ADFSAnalysisResult -Category "Error" -Property "Analysis Error" -Value $_.Exception.Message -Relevance "Critical error prevented complete analysis" -Status "CRITICAL") | |
} | |
} | |
function Get-PerformanceCountersAnalysis { | |
[CmdletBinding()] | |
param() | |
Write-Verbose "Collecting Performance Counters..." | |
$results = @() | |
try { | |
$perfCounters = @( | |
"\AD FS\Token Requests/Sec", | |
"\AD FS\Federation Metadata Requests/Sec", | |
"\AD FS\Artifact Resolution Requests/Sec", | |
"\AD FS\Extranet Account Lockouts/Sec", | |
"\AD FS\SQL Failures/Sec" | |
) | |
foreach ($counter in $perfCounters) { | |
try { | |
$value = (Get-Counter $counter -SampleInterval 1 -MaxSamples 3 -ErrorAction SilentlyContinue | | |
Select-Object -ExpandProperty CounterSamples | | |
Measure-Object -Property CookedValue -Average).Average | |
if ($null -ne $value) { | |
$counterName = (Split-Path $counter -Leaf).Replace("/Sec", " Per Second") | |
$results += New-ADFSAnalysisResult -Category "Performance" -Property $counterName -Value ([math]::Round($value, 2)) -Relevance "Performance metric for ADFS operations" -Status "INFO" | |
} | |
} catch { | |
$counterName = Split-Path $counter -Leaf | |
$results += New-ADFSAnalysisResult -Category "Performance" -Property $counterName -Value "Not Available" -Relevance "Performance counter may not exist in this ADFS version" -Status "INFO" | |
} | |
} | |
} catch { | |
$results += New-ADFSAnalysisResult -Category "Performance" -Property "Performance Counter Analysis" -Value "Error: $($_.Exception.Message)" -Relevance "Performance counters provide operational metrics" -Status "WARNING" | |
} | |
return $results | |
} | |
# ============================================================================= | |
# EXECUTION - Run the analysis and return results | |
# ============================================================================= | |
# Execute the main analysis function and return PSCustomObjects | |
Get-ADFSReport @PSBoundParameters |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment