$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 = 
      "
       
        "
       
     
 
      
     
      $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.