Files
Cloud-20Engineering/Powershell/Lists/Azure/Certificates.ps1
Jurjen Ladenius a226ca97ac added documetation
2025-11-03 08:12:01 +01:00

357 lines
17 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<#
.SYNOPSIS
Inventories and monitors Azure App Service certificates across all enabled subscriptions.
.DESCRIPTION
This script performs a comprehensive audit of all Azure App Service certificates across all
enabled subscriptions in your Azure tenant. It extracts certificate details including expiration
dates, thumbprints, subject names, and calculates the remaining days until expiration.
The script is designed for:
- Certificate lifecycle management and monitoring
- Proactive identification of expiring certificates
- Compliance auditing and reporting
- Security assessments of certificate inventory
- Planning certificate renewal activities
The script processes all enabled subscriptions automatically and exports results to a timestamped
CSV file, making it suitable for automated monitoring and reporting workflows.
Key features:
- Multi-subscription certificate discovery
- Expiration date calculation with days remaining
- Error handling for inaccessible or invalid certificates
- Detailed logging and progress reporting
- CSV export for further analysis and alerting
.PARAMETER None
This script does not accept parameters and processes all enabled subscriptions automatically.
.EXAMPLE
.\Certificates.ps1
Runs the certificate inventory across all enabled subscriptions and exports results to a
timestamped CSV file in the current directory.
.EXAMPLE
# Schedule for automated monitoring
$scriptPath = "C:\Scripts\Certificates.ps1"
& $scriptPath
Executes the script from a scheduled task or automation workflow for regular certificate monitoring.
.EXAMPLE
# Run and immediately view results
.\Certificates.ps1
Get-Content ".\$(Get-Date -Format 'yyyy-MM-dd HHmm') azure_appservice_certificates.csv"
Runs the script and displays the generated CSV content for immediate review.
.NOTES
Author: Cloud Engineering Team
Version: 1.0
Prerequisites:
- Azure PowerShell module (Az) must be installed
- User must be authenticated to Azure (Connect-AzAccount)
- User must have at least 'Reader' permissions across target subscriptions
- Access to Microsoft.Web/certificates resources
Required Permissions:
- Reader access to subscriptions containing App Service certificates
- Web App Certificate Reader or App Service Certificate Reader permissions
- Resource Group Reader permissions for certificate resource groups
Output File:
- Format: "YYYY-MM-DD HHMM azure_appservice_certificates.csv"
- Location: Current directory
- Content: Certificate inventory with expiration analysis
Certificate Status Analysis:
- TotalDays > 30: Certificate is healthy
- TotalDays 7-30: Certificate expires soon (warning)
- TotalDays < 7: Certificate expires very soon (critical)
- TotalDays < 0: Certificate has already expired (urgent action required)
Performance Considerations:
- Processing time depends on the number of subscriptions and certificates
- Large tenants with many certificates may require extended execution time
- Network latency affects certificate detail retrieval
Security and Compliance:
- Certificate thumbprints and subject names are included in output
- Ensure proper access controls on generated CSV files
- Consider encryption for sensitive certificate inventory data
- Regular execution recommended for proactive certificate management
Common Use Cases:
- Monthly certificate expiration reports
- Pre-renewal planning and notifications
- Compliance audits requiring certificate inventory
- Security assessments of certificate lifecycle management
.LINK
https://docs.microsoft.com/en-us/azure/app-service/configure-ssl-certificate
https://docs.microsoft.com/en-us/powershell/module/az.websites/
#>
# Ensure user is authenticated to Azure
# Uncomment the following line if authentication is needed:
# Connect-AzAccount
# Display script header and configuration
Write-Host "======================================================================================================================================================================"
Write-Host "Azure App Service Certificate Inventory and Monitoring"
Write-Host "======================================================================================================================================================================"
Write-Host "Starting certificate discovery across all enabled subscriptions..."
Write-Host "Script execution started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host ""
# Generate timestamped filename for export
[string] $date = Get-Date -Format "yyyy-MM-dd HHmm"
$fileName = ".\$date azure_appservice_certificates.csv"
Write-Host "Export file: $fileName"
Write-Host ""
# Get all enabled subscriptions for processing
Write-Host "Retrieving enabled subscriptions..."
try {
$subscriptions = Get-AzSubscription | Where-Object State -eq "Enabled"
Write-Host "✓ Found $($subscriptions.Count) enabled subscription(s) to process:"
foreach ($sub in $subscriptions) {
Write-Host " - $($sub.Name) ($($sub.Id))"
}
} catch {
Write-Error "Failed to retrieve Azure subscriptions. Please ensure you are authenticated (Connect-AzAccount)"
throw $_
}
Write-Host ""
# Define certificate information class for structured data collection
class CertificateCheck {
# Azure subscription identifier containing the certificate
[string] $SubscriptionId = ""
# Full Azure resource ID of the certificate
[string] $CertificateId = ""
# Resource group name where the certificate is deployed
[string] $ResourceGroupName = ""
# Certificate subject name (Common Name and additional fields)
[string] $SubjectName = ""
# Certificate thumbprint (SHA-1 hash identifier)
[string] $ThumbPrint = ""
# Certificate expiration date and time
[DateTime] $ExpirationDate
# Number of days remaining until expiration (negative if expired)
[double] $TotalDays
# Certificate health status (Expired, Critical, Warning, Healthy, Error)
[string] $Health = ""
# Error messages or status comments for problematic certificates
[string] $Comment = ""
}
# Initialize result collection and processing variables
[CertificateCheck[]]$Result = @()
$StartDate = (Get-Date)
$totalCertificates = 0
$processedSubscriptions = 0
$certificatesWithIssues = 0
Write-Host "======================================================================================================================================================================"
Write-Host "Processing Certificates by Subscription"
Write-Host "======================================================================================================================================================================"
# Process each enabled subscription
foreach ($subscription in $subscriptions) {
Write-Host ""
Write-Host "Processing subscription: $($subscription.Name) ($($subscription.Id))"
try {
# Set Azure context to current subscription
Set-AzContext -SubscriptionId $subscription.Id -ErrorAction Stop | Out-Null
Write-Host "✓ Successfully connected to subscription"
# Retrieve all App Service certificates in the subscription
Write-Host "Discovering App Service certificates..."
$certs = Get-AzResource -ResourceType Microsoft.Web/certificates -ApiVersion "2018-02-01" -ExpandProperties | Select-Object * -ExpandProperty Properties
if ($certs) {
Write-Host "✓ Found $($certs.Count) certificate(s) in subscription"
$subscriptionCertCount = 0
# Process each certificate found
foreach ($cert in $certs) {
$id = $cert.Id
Write-Host " Processing certificate: $($cert.Name)"
# Create new certificate check instance
[CertificateCheck] $certificateCheck = [CertificateCheck]::new()
# Populate basic certificate information
$certificateCheck.SubscriptionId = $subscription.Id
$certificateCheck.CertificateId = $id
$certificateCheck.ThumbPrint = $cert.Properties.thumbprint
$certificateCheck.ResourceGroupName = $cert.ResourceGroupName
try {
$thumbprint = $certificateCheck.ThumbPrint
# Retrieve detailed certificate information
$certificate = Get-AzWebAppCertificate -ResourceGroupName $certificateCheck.ResourceGroupName -Thumbprint $thumbprint -ErrorAction Stop
if ($null -eq $certificate) {
$certificateCheck.Health = "Error"
$certificateCheck.Comment = "Could not find certificate details"
$certificatesWithIssues++
Write-Host " ⚠ Warning: Certificate details not accessible"
} else {
try {
# Extract certificate subject name and expiration details
$subjectname = $certificate.SubjectName
$certificateCheck.SubjectName = $subjectname
Write-Host " ✓ Subject: $subjectname"
# Calculate expiration and days remaining
$EndDate = [datetime]$certificate.ExpirationDate
$certificateCheck.ExpirationDate = $EndDate
$span = New-TimeSpan -Start $StartDate -End $EndDate
$certificateCheck.TotalDays = [Math]::Round($span.TotalDays, 1)
# Determine and assign health status based on expiration
if ($certificateCheck.TotalDays -lt 0) {
$certificateCheck.Health = "Expired"
Write-Host " 🔴 EXPIRED: $([Math]::Abs($certificateCheck.TotalDays)) days ago" -ForegroundColor Red
$certificatesWithIssues++
} elseif ($certificateCheck.TotalDays -lt 7) {
$certificateCheck.Health = "Critical"
Write-Host " 🟠 CRITICAL: Expires in $($certificateCheck.TotalDays) days" -ForegroundColor Yellow
$certificatesWithIssues++
} elseif ($certificateCheck.TotalDays -lt 30) {
$certificateCheck.Health = "Warning"
Write-Host " 🟡 WARNING: Expires in $($certificateCheck.TotalDays) days" -ForegroundColor Yellow
} else {
$certificateCheck.Health = "Healthy"
Write-Host " ✓ Healthy: Expires in $($certificateCheck.TotalDays) days"
}
} catch {
$certificateCheck.Health = "Error"
$certificateCheck.Comment = "Could not determine expiration date"
$certificatesWithIssues++
Write-Host " ⚠ Warning: Could not determine expiration date"
}
}
} catch {
$certificateCheck.Health = "Error"
$certificateCheck.Comment = "Could not load certificate details"
$certificatesWithIssues++
Write-Host " ❌ Error: Could not load certificate details"
}
# Add certificate to results collection
$Result += $certificateCheck
$totalCertificates++
$subscriptionCertCount++
}
Write-Host " ✓ Processed $subscriptionCertCount certificate(s) in subscription"
} else {
Write-Host " No App Service certificates found in this subscription"
}
$processedSubscriptions++
} catch {
Write-Host " ❌ Error processing subscription: $($_.Exception.Message)" -ForegroundColor Red
Write-Host " Please verify permissions and subscription access"
}
}
Write-Host ""
Write-Host "======================================================================================================================================================================"
Write-Host "Exporting Results and Analysis"
Write-Host "======================================================================================================================================================================"
# Export results to CSV file
Write-Host "Exporting certificate inventory to CSV file..."
try {
$Result | Export-Csv -Path $fileName -NoTypeInformation -Force
Write-Host "✓ Successfully exported $($Result.Count) certificate records to: $fileName"
} catch {
Write-Error "Failed to export results to CSV file: $($_.Exception.Message)"
throw $_
}
# Display results summary table
Write-Host ""
Write-Host "Certificate Inventory Summary:"
Write-Host "======================================================================================================================================================================"
$Result | Format-Table -AutoSize
# Generate detailed analysis and statistics
Write-Host ""
Write-Host "======================================================================================================================================================================"
Write-Host "Certificate Analysis Summary"
Write-Host "======================================================================================================================================================================"
Write-Host "Execution completed: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host ""
Write-Host "Processing Statistics:"
Write-Host " Subscriptions processed: $processedSubscriptions"
Write-Host " Total certificates discovered: $totalCertificates"
Write-Host " Certificates with issues: $certificatesWithIssues"
Write-Host ""
# Analyze certificate expiration status using Health property
if ($Result.Count -gt 0) {
$expiredCerts = $Result | Where-Object { $_.Health -eq "Expired" }
$criticalCerts = $Result | Where-Object { $_.Health -eq "Critical" }
$warnCerts = $Result | Where-Object { $_.Health -eq "Warning" }
$healthyCerts = $Result | Where-Object { $_.Health -eq "Healthy" }
$errorCerts = $Result | Where-Object { $_.Health -eq "Error" }
Write-Host "Certificate Status Analysis:"
Write-Host " 🔴 Expired certificates: $($expiredCerts.Count)"
Write-Host " 🟠 Critical (< 7 days): $($criticalCerts.Count)"
Write-Host " 🟡 Warning (7-30 days): $($warnCerts.Count)"
Write-Host " ✓ Healthy (> 30 days): $($healthyCerts.Count)"
Write-Host " ❌ Error/Inaccessible: $($errorCerts.Count)"
Write-Host ""
# Display urgent action items
if ($expiredCerts.Count -gt 0 -or $criticalCerts.Count -gt 0) {
Write-Host "🚨 URGENT ACTION REQUIRED:"
if ($expiredCerts.Count -gt 0) {
Write-Host " - $($expiredCerts.Count) certificate(s) have already expired"
}
if ($criticalCerts.Count -gt 0) {
Write-Host " - $($criticalCerts.Count) certificate(s) expire within 7 days"
}
Write-Host " Review the CSV file for detailed certificate information"
}
if ($warnCerts.Count -gt 0) {
Write-Host "⚠ RENEWAL PLANNING NEEDED:"
Write-Host " - $($warnCerts.Count) certificate(s) expire within 30 days"
}
} else {
Write-Host " No certificates found across all processed subscriptions"
}
Write-Host ""
Write-Host "Output File Information:"
Write-Host " File Path: $fileName"
Write-Host " File Size: $([Math]::Round((Get-Item $fileName).Length / 1KB, 2)) KB"
Write-Host ""
Write-Host "Recommendations:"
Write-Host " - Schedule regular execution for proactive certificate monitoring"
Write-Host " - Set up alerts for certificates expiring within 30 days"
Write-Host " - Implement automated renewal processes where possible"
Write-Host " - Review and resolve any certificates with error status"
Write-Host "======================================================================================================================================================================"