Examples/14-CertificateManagement.ps1

<#
.SYNOPSIS
    Examples for VergeOS SSL/TLS certificate management.

.DESCRIPTION
    This script demonstrates certificate management capabilities:
    - Listing and filtering certificates
    - Creating self-signed certificates
    - Creating manual certificates (uploading existing certs)
    - Creating Let's Encrypt certificates (ACME)
    - Modifying certificate properties
    - Renewing and regenerating certificates
    - Certificate expiration monitoring
    - Common certificate workflows

.NOTES
    Prerequisites:
    - PowerShell 7.4 or later
    - PSVergeOS module installed
    - Connected to a VergeOS system

    Certificate Types:
    - Manual: Upload your own certificate and private key
    - LetsEncrypt: Automatically obtain via ACME protocol
    - SelfSigned: Generate a self-signed certificate
#>


# Import the module
Import-Module PSVergeOS

#region Listing Certificates
# ============================================================================
# LISTING AND FILTERING CERTIFICATES
# ============================================================================

# List all certificates
Get-VergeCertificate

# View certificates in a formatted table
Get-VergeCertificate | Format-Table Key, Domain, Type, Valid, DaysUntilExpiry, Expires -AutoSize

# Get a specific certificate by key
Get-VergeCertificate -Key 1

# Filter by certificate type
Get-VergeCertificate -Type SelfSigned
Get-VergeCertificate -Type LetsEncrypt
Get-VergeCertificate -Type Manual

# Get only valid (unexpired) certificates
Get-VergeCertificate -Valid

# Filter by domain (supports wildcards)
Get-VergeCertificate -Domain "*.example.com"
Get-VergeCertificate -Domain "api*"

# View detailed certificate information
Get-VergeCertificate -Key 1 | Format-List *

# Include sensitive key material (use with caution)
Get-VergeCertificate -Key 1 -IncludeKeys | Select-Object Domain, PublicKey, PrivateKey, Chain

#endregion

#region Creating Self-Signed Certificates
# ============================================================================
# CREATING SELF-SIGNED CERTIFICATES
# ============================================================================

# Create a basic self-signed certificate
New-VergeCertificate -DomainName "myapp.local" -Type SelfSigned

# Create with description and get the result
$cert = New-VergeCertificate -DomainName "internal.local" `
    -Type SelfSigned `
    -Description "Internal services certificate" `
    -PassThru

$cert | Format-List Key, Domain, Type, Valid, Expires

# Create with specific key type
New-VergeCertificate -DomainName "secure.local" `
    -Type SelfSigned `
    -KeyType RSA `
    -RSAKeySize 4096 `
    -Description "RSA 4096-bit certificate"

# Create with Subject Alternative Names (SANs)
New-VergeCertificate -DomainName "app.local" `
    -Type SelfSigned `
    -DomainList "www.app.local", "api.app.local", "admin.app.local" `
    -Description "Multi-domain certificate"

#endregion

#region Creating Manual Certificates
# ============================================================================
# UPLOADING MANUAL CERTIFICATES
# ============================================================================

# Upload an existing certificate from files
# Note: Replace paths with your actual certificate files

<#
# Read certificate files
$publicKey = Get-Content -Path "./cert.pem" -Raw
$privateKey = Get-Content -Path "./key.pem" -Raw
$chain = Get-Content -Path "./chain.pem" -Raw

# Upload the certificate
New-VergeCertificate -DomainName "example.com" `
    -Type Manual `
    -PublicKey $publicKey `
    -PrivateKey $privateKey `
    -Chain $chain `
    -Description "Uploaded production certificate"
#>


# Upload certificate from variables (example with placeholder content)
<#
$publicKey = @"
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAJC1HiIAZAiUMA0Gcz93vGr...
-----END CERTIFICATE-----
"@

$privateKey = @"
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwgg...
-----END PRIVATE KEY-----
"@

New-VergeCertificate -DomainName "example.com" `
    -Type Manual `
    -PublicKey $publicKey `
    -PrivateKey $privateKey `
    -Description "Manually uploaded certificate"
#>


#endregion

#region Creating Let's Encrypt Certificates
# ============================================================================
# CREATING LET'S ENCRYPT (ACME) CERTIFICATES
# ============================================================================

# Create a Let's Encrypt certificate
# Note: Requires proper DNS/HTTP validation setup
<#
New-VergeCertificate -DomainName "public.example.com" `
    -Type LetsEncrypt `
    -AgreeTOS `
    -ContactUserId 1 `
    -Description "Let's Encrypt production certificate"

# Create with custom ACME server (e.g., staging for testing)
New-VergeCertificate -DomainName "test.example.com" `
    -Type LetsEncrypt `
    -ACMEServer "https://acme-staging-v02.api.letsencrypt.org/directory" `
    -AgreeTOS `
    -ContactUserId 1 `
    -Description "Let's Encrypt staging certificate"

# Create with External Account Binding (for providers that require it)
New-VergeCertificate -DomainName "eab.example.com" `
    -Type LetsEncrypt `
    -ACMEServer "https://acme.provider.com/directory" `
    -EABKeyId "kid_12345" `
    -EABHMACKey "hmac_secret_key" `
    -AgreeTOS `
    -ContactUserId 1

# Create with multiple domains (SANs)
New-VergeCertificate -DomainName "example.com" `
    -DomainList "www.example.com", "api.example.com" `
    -Type LetsEncrypt `
    -AgreeTOS `
    -ContactUserId 1
#>


#endregion

#region Modifying Certificates
# ============================================================================
# MODIFYING CERTIFICATE PROPERTIES
# ============================================================================

# Update certificate description
Set-VergeCertificate -Key 1 -Description "Primary API certificate - updated"

# Update using pipeline
Get-VergeCertificate -Key 1 | Set-VergeCertificate -Description "Updated via pipeline" -PassThru

# Update multiple properties
Set-VergeCertificate -Key 1 `
    -Description "Production web certificate" `
    -DomainList "www.example.com", "api.example.com", "admin.example.com"

# Update ACME settings for Let's Encrypt certificate
<#
Set-VergeCertificate -Key 2 `
    -ACMEServer "https://acme-v02.api.letsencrypt.org/directory" `
    -ContactUserId 1
#>


# Update certificate keys (manual certificates only)
<#
$newPublicKey = Get-Content "./new-cert.pem" -Raw
$newPrivateKey = Get-Content "./new-key.pem" -Raw

Set-VergeCertificate -Key 1 `
    -PublicKey $newPublicKey `
    -PrivateKey $newPrivateKey `
    -Description "Renewed certificate"
#>


#endregion

#region Renewing and Regenerating Certificates
# ============================================================================
# RENEWING AND REGENERATING CERTIFICATES
# ============================================================================

# Regenerate a self-signed certificate (creates new key pair)
Update-VergeCertificate -Key 1 -Force

# Renew using pipeline
Get-VergeCertificate -Key 1 | Update-VergeCertificate -Force

# Renew a Let's Encrypt certificate
<#
Update-VergeCertificate -Domain "example.com"
#>


# Renew with PassThru to see the updated certificate
$renewed = Update-VergeCertificate -Key 1 -Force -PassThru
$renewed | Format-List Domain, Type, Valid, Expires, DaysUntilExpiry

# Renew all certificates expiring within 30 days
Get-VergeCertificate | Where-Object { $_.DaysUntilExpiry -lt 30 } | ForEach-Object {
    Write-Host "Renewing certificate: $($_.Domain) (expires in $($_.DaysUntilExpiry) days)"
    Update-VergeCertificate -Key $_.Key -Force
}

#endregion

#region Removing Certificates
# ============================================================================
# REMOVING CERTIFICATES
# ============================================================================

# Remove a certificate by key
Remove-VergeCertificate -Key 2

# Remove without confirmation (for automation)
Remove-VergeCertificate -Key 2 -Confirm:$false

# Remove using pipeline
Get-VergeCertificate -Key 2 | Remove-VergeCertificate

# Remove multiple certificates matching criteria
Get-VergeCertificate | Where-Object { $_.Description -like "*test*" } | Remove-VergeCertificate -Confirm:$false

# Safe removal with confirmation
$certToRemove = Get-VergeCertificate -Key 2
if ($certToRemove) {
    Write-Host "About to remove certificate: $($certToRemove.Domain)"
    Remove-VergeCertificate -Key $certToRemove.Key
}

#endregion

#region Certificate Expiration Monitoring
# ============================================================================
# MONITORING CERTIFICATE EXPIRATION
# ============================================================================

# List certificates sorted by expiration
Get-VergeCertificate | Sort-Object Expires | Format-Table Domain, Type, Valid, DaysUntilExpiry, Expires -AutoSize

# Find certificates expiring within specified days
function Get-ExpiringCertificates {
    param(
        [int]$Days = 30
    )

    Get-VergeCertificate | Where-Object {
        $_.DaysUntilExpiry -lt $Days -and $_.DaysUntilExpiry -ge 0
    } | Sort-Object DaysUntilExpiry
}

# Check for certificates expiring in 30 days
$expiring = Get-ExpiringCertificates -Days 30
if ($expiring) {
    Write-Host "Certificates expiring within 30 days:" -ForegroundColor Yellow
    $expiring | Format-Table Domain, Type, DaysUntilExpiry, Expires -AutoSize
} else {
    Write-Host "No certificates expiring within 30 days." -ForegroundColor Green
}

# Find already expired certificates
$expired = Get-VergeCertificate | Where-Object { $_.DaysUntilExpiry -lt 0 }
if ($expired) {
    Write-Host "EXPIRED CERTIFICATES:" -ForegroundColor Red
    $expired | Format-Table Domain, Type, Expires -AutoSize
}

# Generate certificate health report
function Get-CertificateHealthReport {
    $certs = Get-VergeCertificate

    $report = @{
        Total    = $certs.Count
        Valid    = ($certs | Where-Object Valid).Count
        Expired  = ($certs | Where-Object { $_.DaysUntilExpiry -lt 0 }).Count
        Critical = ($certs | Where-Object { $_.DaysUntilExpiry -ge 0 -and $_.DaysUntilExpiry -lt 7 }).Count
        Warning  = ($certs | Where-Object { $_.DaysUntilExpiry -ge 7 -and $_.DaysUntilExpiry -lt 30 }).Count
        Healthy  = ($certs | Where-Object { $_.DaysUntilExpiry -ge 30 }).Count
    }

    [PSCustomObject]$report
}

Write-Host "`nCertificate Health Summary:"
Get-CertificateHealthReport | Format-List

#endregion

#region Common Certificate Workflows
# ============================================================================
# PRACTICAL CERTIFICATE WORKFLOWS
# ============================================================================

# Workflow: Auto-renew all expiring certificates
function Invoke-CertificateAutoRenewal {
    param(
        [int]$DaysThreshold = 14
    )

    $expiring = Get-VergeCertificate | Where-Object {
        $_.DaysUntilExpiry -lt $DaysThreshold -and
        $_.DaysUntilExpiry -ge 0 -and
        $_.TypeValue -in @('letsencrypt', 'self_signed')
    }

    if (-not $expiring) {
        Write-Host "No certificates need renewal." -ForegroundColor Green
        return
    }

    foreach ($cert in $expiring) {
        Write-Host "Renewing: $($cert.Domain) (Type: $($cert.Type), Expires in: $($cert.DaysUntilExpiry) days)"
        try {
            Update-VergeCertificate -Key $cert.Key -Force
            Write-Host " Success" -ForegroundColor Green
        }
        catch {
            Write-Host " Failed: $_" -ForegroundColor Red
        }
    }
}

# Invoke-CertificateAutoRenewal -DaysThreshold 30

# Workflow: Export certificate inventory to CSV
function Export-CertificateInventory {
    param(
        [string]$Path = "certificate-inventory.csv"
    )

    Get-VergeCertificate | Select-Object `
        Key,
        Domain,
        @{N='AdditionalDomains';E={$_.DomainList -join '; '}},
        Type,
        Valid,
        KeyType,
        DaysUntilExpiry,
        Expires,
        Created,
        Description |
        Export-Csv -Path $Path -NoTypeInformation

    Write-Host "Certificate inventory exported to: $Path"
}

# Export-CertificateInventory -Path "certs.csv"

# Workflow: Certificate type summary
function Get-CertificateTypeSummary {
    Get-VergeCertificate | Group-Object Type | ForEach-Object {
        [PSCustomObject]@{
            Type        = $_.Name
            Count       = $_.Count
            ValidCount  = ($_.Group | Where-Object Valid).Count
            AvgDaysLeft = [math]::Round(($_.Group | Where-Object { $_.DaysUntilExpiry -ge 0 } |
                Measure-Object DaysUntilExpiry -Average).Average, 0)
        }
    }
}

Write-Host "`nCertificate Summary by Type:"
Get-CertificateTypeSummary | Format-Table -AutoSize

# Workflow: Backup certificate keys (manual certs only)
function Backup-CertificateKeys {
    param(
        [int]$Key,
        [string]$OutputDirectory = "."
    )

    $cert = Get-VergeCertificate -Key $Key -IncludeKeys

    if (-not $cert) {
        Write-Error "Certificate with Key $Key not found."
        return
    }

    $safeDomain = $cert.Domain -replace '[^a-zA-Z0-9\-\.]', '_'
    $timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
    $baseFileName = "$safeDomain-$timestamp"

    if ($cert.PublicKey) {
        $pubPath = Join-Path $OutputDirectory "$baseFileName.crt"
        $cert.PublicKey | Set-Content -Path $pubPath -NoNewline
        Write-Host "Public key saved to: $pubPath"
    }

    if ($cert.PrivateKey) {
        $keyPath = Join-Path $OutputDirectory "$baseFileName.key"
        $cert.PrivateKey | Set-Content -Path $keyPath -NoNewline
        Write-Host "Private key saved to: $keyPath"
    }

    if ($cert.Chain) {
        $chainPath = Join-Path $OutputDirectory "$baseFileName-chain.crt"
        $cert.Chain | Set-Content -Path $chainPath -NoNewline
        Write-Host "Chain saved to: $chainPath"
    }
}

# Backup-CertificateKeys -Key 1 -OutputDirectory "./cert-backup"

#endregion

#region Certificate Alerting
# ============================================================================
# CERTIFICATE ALERTING FUNCTIONS
# ============================================================================

# Function to check certificate health and return status
function Test-CertificateHealth {
    param(
        [int]$CriticalDays = 7,
        [int]$WarningDays = 30
    )

    $certs = Get-VergeCertificate
    $results = @()

    foreach ($cert in $certs) {
        $status = switch ($true) {
            { $cert.DaysUntilExpiry -lt 0 } { 'Expired' }
            { $cert.DaysUntilExpiry -lt $CriticalDays } { 'Critical' }
            { $cert.DaysUntilExpiry -lt $WarningDays } { 'Warning' }
            default { 'Healthy' }
        }

        $results += [PSCustomObject]@{
            Domain          = $cert.Domain
            Type            = $cert.Type
            Status          = $status
            DaysUntilExpiry = $cert.DaysUntilExpiry
            Expires         = $cert.Expires
        }
    }

    return $results
}

# Check certificate health
$health = Test-CertificateHealth
Write-Host "`nCertificate Health Status:"
$health | Sort-Object @{E={
    switch ($_.Status) {
        'Expired' { 0 }
        'Critical' { 1 }
        'Warning' { 2 }
        'Healthy' { 3 }
    }
}} | Format-Table Domain, Type, Status, DaysUntilExpiry, Expires -AutoSize

# Show only certificates needing attention
$attention = $health | Where-Object { $_.Status -in @('Expired', 'Critical', 'Warning') }
if ($attention) {
    Write-Host "`nCertificates Needing Attention:" -ForegroundColor Yellow
    $attention | Format-Table -AutoSize
}

#endregion