Public/System/New-VergeCertificate.ps1
|
function New-VergeCertificate { <# .SYNOPSIS Creates a new SSL/TLS certificate in VergeOS. .DESCRIPTION New-VergeCertificate creates a new SSL/TLS certificate in VergeOS. Three certificate types are supported: - Manual: Upload your own certificate and private key - LetsEncrypt: Automatically obtain a certificate via ACME protocol - SelfSigned: Generate a self-signed certificate .PARAMETER DomainName The primary domain for the certificate. Required for all certificate types. .PARAMETER DomainList Additional domain names (Subject Alternative Names) for the certificate. Can be specified as an array or comma-separated string. .PARAMETER Description An optional description for the certificate. .PARAMETER Type The certificate type: Manual, LetsEncrypt, or SelfSigned. Defaults to SelfSigned. .PARAMETER PublicKey The public certificate in PEM format. Required for Manual type. .PARAMETER PrivateKey The private key in PEM format. Required for Manual type. .PARAMETER Chain The certificate chain in PEM format. Optional for Manual type. .PARAMETER ACMEServer The ACME server URL for Let's Encrypt certificates. Defaults to https://acme-v02.api.letsencrypt.org/directory .PARAMETER EABKeyId Key Identifier for External Account Binding (ACME). Used with some ACME providers that require EAB. .PARAMETER EABHMACKey HMAC key for External Account Binding (ACME). .PARAMETER KeyType The key type for self-signed or Let's Encrypt certificates: ECDSA or RSA. Defaults to ECDSA. .PARAMETER RSAKeySize The RSA key size when using RSA key type. Default is 2048. .PARAMETER ContactUserId The user ID to use as contact for Let's Encrypt certificates. .PARAMETER AgreeTOS Accept the Let's Encrypt Terms of Service. Required for Let's Encrypt certificates. .PARAMETER PassThru Return the created certificate object. .PARAMETER Server The VergeOS connection to use. Defaults to the current default connection. .EXAMPLE New-VergeCertificate -DomainName "example.com" -Type SelfSigned Creates a self-signed certificate for example.com. .EXAMPLE New-VergeCertificate -DomainName "example.com" -DomainList "www.example.com","api.example.com" -Type SelfSigned -PassThru Creates a self-signed certificate with SANs and returns the created object. .EXAMPLE $pubKey = Get-Content ./cert.pem -Raw $privKey = Get-Content ./key.pem -Raw New-VergeCertificate -DomainName "example.com" -Type Manual -PublicKey $pubKey -PrivateKey $privKey Uploads a manual certificate with public and private keys. .EXAMPLE New-VergeCertificate -DomainName "example.com" -Type LetsEncrypt -AgreeTOS -ContactUserId 1 Creates a Let's Encrypt certificate (requires proper DNS/HTTP validation). .OUTPUTS None by default. Verge.Certificate when -PassThru is specified. .NOTES For Let's Encrypt certificates, ensure your domain's DNS points to the VergeOS system and that port 80 is accessible for HTTP validation. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] [OutputType([PSCustomObject])] param( [Parameter(Mandatory, Position = 0)] [ValidateNotNullOrEmpty()] [string]$DomainName, [Parameter()] [string[]]$DomainList, [Parameter()] [string]$Description, [Parameter()] [ValidateSet('Manual', 'LetsEncrypt', 'SelfSigned')] [string]$Type = 'SelfSigned', [Parameter()] [string]$PublicKey, [Parameter()] [string]$PrivateKey, [Parameter()] [string]$Chain, [Parameter()] [string]$ACMEServer, [Parameter()] [string]$EABKeyId, [Parameter()] [string]$EABHMACKey, [Parameter()] [ValidateSet('ECDSA', 'RSA')] [string]$KeyType, [Parameter()] [ValidateSet('2048', '3072', '4096')] [string]$RSAKeySize, [Parameter()] [int]$ContactUserId, [Parameter()] [switch]$AgreeTOS, [Parameter()] [switch]$PassThru, [Parameter()] [object]$Server ) begin { # Resolve connection if (-not $Server) { $Server = $script:DefaultConnection } if (-not $Server) { throw [System.InvalidOperationException]::new( 'Not connected to VergeOS. Use Connect-VergeOS to establish a connection.' ) } # Map friendly type names to API values $typeMapping = @{ 'Manual' = 'manual' 'LetsEncrypt' = 'letsencrypt' 'SelfSigned' = 'self_signed' } } process { # Validate parameters based on certificate type if ($Type -eq 'Manual') { if (-not $PublicKey) { throw "PublicKey is required for Manual certificate type." } if (-not $PrivateKey) { throw "PrivateKey is required for Manual certificate type." } } elseif ($Type -eq 'LetsEncrypt') { if (-not $AgreeTOS) { throw "You must accept the Let's Encrypt Terms of Service using -AgreeTOS." } } # Build request body $body = @{ domainname = $DomainName type = $typeMapping[$Type] } # Add domain list (SANs) if ($DomainList) { $body['domainlist'] = $DomainList -join ',' } # Add optional description if ($Description) { $body['description'] = $Description } # Manual certificate specific fields if ($Type -eq 'Manual') { $body['public'] = $PublicKey $body['private'] = $PrivateKey if ($Chain) { $body['chain'] = $Chain } } # Let's Encrypt specific fields if ($Type -eq 'LetsEncrypt') { $body['agree_tos'] = $true if ($ACMEServer) { $body['acme_server'] = $ACMEServer } if ($EABKeyId) { $body['eab_kid'] = $EABKeyId } if ($EABHMACKey) { $body['eab_hmac_key'] = $EABHMACKey } if ($ContactUserId) { $body['contact'] = $ContactUserId } } # Key type for self-signed and Let's Encrypt if ($Type -ne 'Manual') { if ($KeyType) { $body['key_type'] = $KeyType.ToLower() } if ($RSAKeySize) { $body['rsa_key_size'] = $RSAKeySize } } # Determine display name for confirmation $typeDisplay = switch ($Type) { 'Manual' { 'Manual' } 'LetsEncrypt' { "Let's Encrypt" } 'SelfSigned' { 'Self-Signed' } } if ($PSCmdlet.ShouldProcess("$DomainName ($typeDisplay)", 'Create Certificate')) { try { Write-Verbose "Creating $typeDisplay certificate for domain '$DomainName'" $response = Invoke-VergeAPI -Method POST -Endpoint 'certificates' -Body $body -Connection $Server # Get the created certificate key $certKey = $response.'$key' if (-not $certKey -and $response.key) { $certKey = $response.key } Write-Verbose "Certificate created with Key: $certKey" if ($PassThru -and $certKey) { # Return the created certificate Start-Sleep -Milliseconds 500 Get-VergeCertificate -Key $certKey -Server $Server } } catch { $errorMessage = $_.Exception.Message if ($errorMessage -match 'already exists' -or $errorMessage -match 'duplicate') { throw "A certificate for domain '$DomainName' already exists." } throw "Failed to create certificate for '$DomainName': $errorMessage" } } } } |