$OutputDir = "C:\eclipsys\oci_cert_renewal\output"
$CertifyExe = "C:\Program Files\CertifyTheWeb\certify.exe"
$OpenSSL = "C:\Program Files\OpenSSL-Win64\bin\openssl.exe"
$CertSubjectMatch = "*reports.eclipsys.ca*"
$PfxPassword = "<YOUR_PFX_PASSWORD>"
$OciRegion = "ca-toronto-1"
$OciCertificateId = "ocid1.certificate.oc1.ca-toronto-1.<...>"
$SecurityListId = "ocid1.securitylist.oc1.ca-toronto-1.<...>"
$BackupIngressJson = "C:\eclipsys\oci_cert_renewal\uleth_ingress_rules.json"
$OpenIngressJson = "C:\eclipsys\oci_cert_renewal\open_port80_ingress.json"
$CloseIngressJson = "C:\eclipsys\oci_cert_renewal\close_port80_ingress.json"
$PfxFile = "$OutputDir\fastcert.pfx"
$CertPem = "$OutputDir\certificate.pem"
$KeyProtected = "$OutputDir\private_key_nonrsa.pem"
$KeyPem = "$OutputDir\privatekey.pem"
$CaPem = "$OutputDir\eclipsys_cert_ca.crt"
$RootPem = "$OutputDir\eclipsys_cert_root.crt"
$FullChainPem = "$OutputDir\certificatechain.pem"
$LogPath = "$OutputDir\cert_renewal.log
Purpose: Centralize configuration so values aren’t scattered throughout the script.
Backup current ingress, then open Port 80 (ACME HTTP-01)
oci network security-list get `
--security-list-id $SecurityListId `
--query data.'ingress-security-rules' `
--output json > $BackupIngressJson
oci network security-list update `
--security-list-id $SecurityListId `
--ingress-security-rules file://$OpenIngressJson `
--force
What it does: Backs up current rules and temporarily opens TCP/80 to complete the ACME challenge.
Renew with CertifyTheWeb (CTW) and log output
Start-Process -FilePath $CertifyExe `
-ArgumentList "renew --force-renew-all --verbose" `
-RedirectStandardOutput $LogPath `
-Wait -NoNewWindow
What it does: Forces a renewal and captures verbose logs for audit/troubleshooting.
Export the newest matching certificate (PFX) from the Windows store
New-Item -Path $OutputDir -ItemType Directory -Force | Out-Null
$cert = Get-ChildItem Cert:\LocalMachine\My |
Where-Object { $_.Subject -like $CertSubjectMatch } |
Sort-Object NotAfter -Descending | Select-Object -First 1
if (-not $cert) { throw "No certificate found for $CertSubjectMatch" }
$bytes = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx, $PfxPassword)
[System.IO.File]::WriteAllBytes($PfxFile, $bytes)
What it does: Selects the most recent matching cert and exports it as PFX (includes private key).
OpenSSL: split PFX → private key + leaf cert, and capture CA
& $OpenSSL pkcs12 -in $PfxFile -nocerts -out $KeyProtected -passin pass:$PfxPassword -passout pass:$PfxPassword
& $OpenSSL rsa -in $KeyProtected -out $KeyPem -passin pass:$PfxPassword
& $OpenSSL pkcs12 -in $PfxFile -clcerts -nokeys -out $CertPem -passin pass:$PfxPassword
& $OpenSSL pkcs12 -in $PfxFile -cacerts -nokeys -out $CaPem -passin pass:$PfxPassword
What it does: Extracts private key, leaf certificate, and intermediate(s).
$issuer = (& $OpenSSL x509 -in $CertPem -noout -issuer)
$intermediateURL = switch -Regex ($issuer) {
"R13" { "https://letsencrypt.org/certs/2024/r13.pem" ; break }
"R12" { "https://letsencrypt.org/certs/2024/r12.pem" ; break }
"R11" { "https://letsencrypt.org/certs/2024/r11.pem" ; break }
default { "https://letsencrypt.org/certs/2024/r10.pem" }
}
$rootURL = "https://letsencrypt.org/certs/isrgrootx1.pem"
Invoke-WebRequest -Uri $intermediateURL -OutFile $CaPem
Invoke-WebRequest -Uri $rootURL -OutFile $RootPem
Get-Content $CaPem, $RootPem | Set-Content $FullChainPem
What it does: Ensures you always include the correct intermediate and root for a valid trust chain.