Lets Encrypt - Route53

Octopus.Script exported 2024-08-01 by harrisonmeister belongs to ‘Lets Encrypt’ category.

Request (or renew) a X.509 SSL Certificate from the Let’s Encrypt Certificate Authority.

Features

  • ACME v2 protocol support which allows generating wildcard certificates (*.example.com)
  • AWS Route53 Challenge for TLD, CNAME and Wildcard domains.
  • Publishes/Updates SSL Certificates in the Octopus Deploy Certificate Store.
  • Verified to work on both Windows (PowerShell 5+) and Linux (PowerShell 6+) deployment Targets or Workers.

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

Certificate Domain

LE_Route53_CertificateDomain =

Domain (TLD, CNAME or Wildcard) to create a certificate for.

PFX Password

LE_Route53_PfxPassword =

Password to use when converting to / from PFX.

Replace expiring certificate before N days

LE_Route53_ReplaceIfExpiresInDays = 30

Replace the certificate if it expiries within N days

AWS account

LE_Route53_AWSAccount =

An AWS Account that has API access to make Route53 changes.

Octopus Deploy API key

LE_Route53_Octopus_APIKey =

A Octopus Deploy API key with access to change Certificates in the Certificate Store.

Use Lets Encrypt Staging

LE_Route53_Use_Staging = false

Should the Certificate be generated using the Lets Encrypt Staging infrastructure?

Contact Email Address

LE_Route53_ContactEmailAddress = #{Octopus.Deployment.CreatedBy.EmailAddress}

Email Address

Create Wildcard SAN

LE_Route53_CreateWildcardSAN = false

Should the certificate have a Subject Alternative Name (SAN) excluding the wildcard?

e.g. a certificate domain of *.internal.example-domain.com could also have a SAN of internal.example-domain.com

Script body

Steps based on this template will execute the following PowerShell script.

###############################################################################
# TLS 1.2
###############################################################################
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

###############################################################################
# Required Modules folder
###############################################################################
Write-Host "Checking for required powershell modules folder"
$ModulesFolder = "$HOME\Documents\WindowsPowerShell\Modules"
if ($PSEdition -eq "Core") {
    if ($PSVersionTable.Platform -eq "Unix") {
        $ModulesFolder = "$HOME/.local/share/powershell/Modules"
    }
    else {
        $ModulesFolder = "$HOME\Documents\PowerShell\Modules"
    }
}
$PSModuleFolderExists = (Test-Path $ModulesFolder)
if ($PSModuleFolderExists -eq $False) {
	Write-Host "Creating directory: $ModulesFolder"
	New-Item $ModulesFolder -ItemType Directory -Force
    $env:PSModulePath = $ModulesFolder + [System.IO.Path]::PathSeparator + $env:PSModulePath
}

###############################################################################
# Required Modules
###############################################################################
Write-Host "Checking for required modules."
$required_posh_acme_version = 3.12.0
$module_check = Get-Module -ListAvailable -Name Posh-Acme | Where-Object { $_.Version -ge $required_posh_acme_version }

if (-not ($module_check)) {
    Write-Host "Ensuring NuGet provider is bootstrapped."
    Get-PackageProvider NuGet -ForceBootstrap | Out-Null
    Write-Host "Installing Posh-ACME."
    Install-Module -Name Posh-ACME -MinimumVersion 3.12.0 -Scope CurrentUser -Force
}

Import-Module Posh-ACME

###############################################################################
# Constants
###############################################################################
$LE_Route53_CertificateDomain = $OctopusParameters["LE_Route53_CertificateDomain"]
$LE_Route53_CertificateName = "Lets Encrypt - $($LE_Route53_CertificateDomain)"

# Issuer used in a cert could be one of multiple, including ones no longer supported by Let's Encrypt
$LE_Route53_Fake_Issuers = @("Fake LE Intermediate X1", "(STAGING) Artificial Apricot R3", "(STAGING) Ersatz Edamame E1", "(STAGING) Pseudo Plum E5", "(STAGING) False Fennel E6", "(STAGING) Puzzling Parsnip E7", "(STAGING) Mysterious Mulberry E8", "(STAGING) Fake Fig E9", "(STAGING) Counterfeit Cashew R10", "(STAGING) Wannabe Watercress R11", "(STAGING) Riddling Rhubarb R12", "(STAGING) Tenuous Tomato R13", "(STAGING) Not Nectarine R14")
$LE_Route53_Issuers = @("Let's Encrypt Authority X3", "E1", "E2", "R3", "R4", "R5", "R6", "R10", "R11")

###############################################################################
# Helpers
###############################################################################
function Get-WebRequestErrorBody {
    param (
        $RequestError
    )

    # Powershell < 6 you can read the Exception
    if ($PSVersionTable.PSVersion.Major -lt 6) {
        if ($RequestError.Exception.Response) {
            $reader = New-Object System.IO.StreamReader($RequestError.Exception.Response.GetResponseStream())
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $response = $reader.ReadToEnd()

            return $response | ConvertFrom-Json
        }
    }
    else {
        return $RequestError.ErrorDetails.Message
    }
}

###############################################################################
# Functions
###############################################################################
function Get-LetsEncryptCertificate {
    Write-Debug "Entering: Get-LetsEncryptCertificate"

    if ($OctopusParameters["LE_Route53_Use_Staging"] -eq $True) {
        Write-Host "Using Lets Encrypt Server: Staging"
        Set-PAServer LE_STAGE;
    }
    else {
        Write-Host "Using Lets Encrypt Server: Production"
        Set-PAServer LE_PROD;
    }

    # Clobber account if it exists.
    $le_account = Get-PAAccount
    if ($le_account) {
        Remove-PAAccount $le_account.Id -Force
    }

    $aws_secret_key = ConvertTo-SecureString -String $OctopusParameters["LE_Route53_AWSAccount.SecretKey"] -AsPlainText -Force
    $route53_params = @{
        R53AccessKey = $OctopusParameters["LE_Route53_AWSAccount.AccessKey"];
        R53SecretKey = $aws_secret_key
    }

    try {
        $DnsPlugins = @("Route53")
        $DomainList = @($LE_Route53_CertificateDomain)
        
        # If domain is a wildcard e.g. *.example-domain.com, check if a SAN has been requested e.g. example-domain.com.
        if ($LE_Route53_CertificateDomain -match "\*." -and $OctopusParameters["LE_Route53_CreateWildcardSAN"] -eq $True) {
            $LE_Route53_Certificate_SAN = $LE_Route53_CertificateDomain.Replace("*.","")
            $DomainList += $LE_Route53_Certificate_SAN
            # Include additional DnsPlugin of same type to surpress warning.
            $DnsPlugins += "Route53"
        }

        $Cert_Params = @{
            Domain = $DomainList
            AcceptTOS = $True;
            Contact = $OctopusParameters["LE_Route53_ContactEmailAddress"];
            DnsPlugin = $DnsPlugins;
            PluginArgs = $route53_params;
            PfxPass = $OctopusParameters["LE_Route53_PfxPassword"];
            Force = $True;
        }

        return New-PACertificate @Cert_Params
    }
    catch {
        Write-Host "Failed to Create Certificate. Error Message: $($_.Exception.Message). See Debug output for details."
        Write-Debug (Get-WebRequestErrorBody -RequestError $_)
        exit 1
    }
}

function Get-OctopusCertificates {
    Write-Debug "Entering: Get-OctopusCertificates"

    $octopus_uri = $OctopusParameters["Octopus.Web.ServerUri"]
    $octopus_space_id = $OctopusParameters["Octopus.Space.Id"]
    $octopus_headers = @{ "X-Octopus-ApiKey" = $OctopusParameters["LE_Route53_Octopus_APIKey"] }
    $octopus_certificates_uri = "$octopus_uri/api/$octopus_space_id/certificates?search=$($LE_Route53_CertificateDomain)"

    try {
        # Get a list of certificates that match our domain search criteria.
        $certificates_search = Invoke-WebRequest -Uri $octopus_certificates_uri -Method Get -Headers $octopus_headers -UseBasicParsing -ErrorAction Stop | ConvertFrom-Json | Select-Object -ExpandProperty Items

        # We don't want to confuse Production and Staging Lets Encrypt Certificates.
        $possible_issuers = $LE_Route53_Issuers
        if ($OctopusParameters["LE_Route53_Use_Staging"] -eq $True) {
            $possible_issuers = $LE_Route53_Fake_Issuers
        }

        return $certificates_search | Where-Object {
            $_.SubjectCommonName -eq $LE_Route53_CertificateDomain -and
            $possible_issuers -contains $_.IssuerCommonName -and
            $null -eq $_.ReplacedBy -and
            $null -eq $_.Archived
        }
    }
    catch {
        Write-Host "Could not retrieve certificates from Octopus Deploy. Error: $($_.Exception.Message). See Debug output for details."
        Write-Debug (Get-WebRequestErrorBody -RequestError $_)
        exit 1
    }
}

function Publish-OctopusCertificate {
    param (
        [string] $JsonBody
    )

    Write-Debug "Entering: Publish-OctopusCertificate"

    if (-not ($JsonBody)) {
        Write-Host "Existing Certificate Id and a replace Certificate are required."
        exit 1
    }

    $octopus_uri = $OctopusParameters["Octopus.Web.ServerUri"]
    $octopus_space_id = $OctopusParameters["Octopus.Space.Id"]
    $octopus_headers = @{ "X-Octopus-ApiKey" = $OctopusParameters["LE_Route53_Octopus_APIKey"] }
    $octopus_certificates_uri = "$octopus_uri/api/$octopus_space_id/certificates"

    try {
        Invoke-WebRequest -Uri $octopus_certificates_uri -Method Post -Headers $octopus_headers -Body $JsonBody -UseBasicParsing
        Write-Host "Published $($LE_Route53_CertificateDomain) certificate to the Octopus Deploy Certificate Store."
    }
    catch {
        Write-Host "Failed to publish $($LE_Route53_CertificateDomain) certificate. Error: $($_.Exception.Message). See Debug output for details."
        Write-Debug (Get-WebRequestErrorBody -RequestError $_)
        exit 1
    }
}

function Update-OctopusCertificate {
    param (
        [string]$Certificate_Id,
        [string]$JsonBody
    )

    Write-Debug "Entering: Update-OctopusCertificate"

    if (-not ($Certificate_Id -and $JsonBody)) {
        Write-Host "Existing Certificate Id and a replace Certificate are required."
        exit 1
    }

    $octopus_uri = $OctopusParameters["Octopus.Web.ServerUri"]
    $octopus_space_id = $OctopusParameters["Octopus.Space.Id"]
    $octopus_headers = @{ "X-Octopus-ApiKey" = $OctopusParameters["LE_Route53_Octopus_APIKey"] }
    $octopus_certificates_uri = "$octopus_uri/api/$octopus_space_id/certificates/$Certificate_Id/replace"

    try {
        Invoke-WebRequest -Uri $octopus_certificates_uri -Method Post -Headers $octopus_headers -Body $JsonBody -UseBasicParsing
        Write-Host "Replaced $($LE_Route53_CertificateDomain) certificate in the Octopus Deploy Certificate Store."
    }
    catch {
        Write-Error "Failed to replace $($LE_Route53_CertificateDomain) certificate. Error: $($_.Exception.Message). See Debug output for details."
        Write-Debug (Get-WebRequestErrorBody -RequestError $_)
        exit 1
    }
}

function Get-NewCertificatePFXAsJson {
    param (
        $Certificate
    )

    Write-Debug "Entering: Get-NewCertificatePFXAsJson"

    if (-not ($Certificate)) {
        Write-Host "Certificate is required."
        Exit 1
    }

    [Byte[]]$certificate_buffer = [System.IO.File]::ReadAllBytes($Certificate.PfxFullChain)
    $certificate_base64 = [convert]::ToBase64String($certificate_buffer)

    $certificate_body = @{
        Name = "$LE_Route53_CertificateName";
        Notes            = "";
        CertificateData  = @{
            HasValue = $true;
            NewValue = $certificate_base64;
        };
        Password         = @{
            HasValue = $true;
            NewValue = $OctopusParameters["LE_Route53_PfxPassword"];
        };
    }

    return $certificate_body | ConvertTo-Json
}

function Get-ReplaceCertificatePFXAsJson {
    param (
        $Certificate
    )

    Write-Debug "Entering: Get-ReplaceCertificatePFXAsJson"

    if (-not ($Certificate)) {
        Write-Host "Certificate is required."
        Exit 1
    }

    [Byte[]]$certificate_buffer = [System.IO.File]::ReadAllBytes($Certificate.PfxFullChain)
    $certificate_base64 = [convert]::ToBase64String($certificate_buffer)

    $certificate_body = @{
        CertificateData = $certificate_base64;
        Password        = $OctopusParameters["LE_Route53_PfxPassword"];
    }

    return $certificate_body | ConvertTo-Json
}

###############################################################################
# DO THE THING | MAIN |
###############################################################################
Write-Debug "Do the Thing"

Write-Host "Checking for existing Lets Encrypt Certificates in the Octopus Deploy Certificates Store."
$certificates = Get-OctopusCertificates

# Check for PFX & PEM
if ($certificates) {

    # Handle weird behavior between Powershell 5 and Powershell 6+
    $certificate_count = 1
    if ($certificates.Count -ge 1) {
        $certificate_count = $certificates.Count
    }

    Write-Host "Found $certificate_count for $($LE_Route53_CertificateDomain)."
    Write-Host "Checking to see if any expire within $($OctopusParameters["LE_Route53_ReplaceIfExpiresInDays"]) days."

    # Check Expiry Dates
    $expiring_certificates = $certificates | Where-Object { [DateTime]$_.NotAfter -lt (Get-Date).AddDays($OctopusParameters["LE_Route53_ReplaceIfExpiresInDays"]) }

    if ($expiring_certificates) {
        Write-Host "Found certificates that expire with $($OctopusParameters["LE_Route53_ReplaceIfExpiresInDays"]) days. Requesting new certificates for $($LE_Route53_CertificateDomain) from Lets Encrypt"
        $le_certificate = Get-LetsEncryptCertificate

        # PFX
        $existing_certificate = $certificates | Where-Object { $_.CertificateDataFormat -eq "Pkcs12" } | Select-Object -First 1
        $certificate_as_json = Get-ReplaceCertificatePFXAsJson -Certificate $le_certificate
        Update-OctopusCertificate -Certificate_Id $existing_certificate.Id -JsonBody $certificate_as_json
    }
    else {
        Write-Host "Nothing to do here..."
    }

    exit 0
}

# No existing Certificates - Lets get some new ones.
Write-Host "No existing certificates found for $($LE_Route53_CertificateDomain)."
Write-Host "Request New Certificate for $($LE_Route53_CertificateDomain) from Lets Encrypt"

# New Certificate..
$le_certificate = Get-LetsEncryptCertificate

Write-Host "Publishing: LetsEncrypt - $($LE_Route53_CertificateDomain) (PFX)"
$certificate_as_json = Get-NewCertificatePFXAsJson -Certificate $le_certificate
Publish-OctopusCertificate -JsonBody $certificate_as_json

Write-Host "GREAT SUCCESS"

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "55b654f4-3e01-424f-b299-f99beddf0501",
  "Name": "Lets Encrypt - Route53",
  "Description": "Request (or renew) a X.509 SSL Certificate from the [Let's Encrypt Certificate Authority](https://letsencrypt.org/). \n\n#### Features\n\n- ACME v2 protocol support which allows generating wildcard certificates (*.example.com)\n- [AWS Route53](https://aws.amazon.com/route53/) Challenge for TLD, CNAME and Wildcard domains. \n- Publishes/Updates SSL Certificates in the [Octopus Deploy Certificate Store](https://octopus.com/docs/deployment-examples/certificates). \n- Verified to work on both Windows (PowerShell 5+) and Linux (PowerShell 6+) deployment Targets or Workers.",
  "Version": 13,
  "ExportedAt": "2024-08-01T10:57:00.608Z",
  "ActionType": "Octopus.Script",
  "Author": "harrisonmeister",
  "Packages": [],
  "Parameters": [
    {
      "Id": "f1739a56-2603-42e0-b629-801dd71b0b0c",
      "Name": "LE_Route53_CertificateDomain",
      "Label": "Certificate Domain",
      "HelpText": "Domain (TLD, CNAME or Wildcard) to create a certificate for. ",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "c1f44b38-8025-4b12-b010-df48c02b3da4",
      "Name": "LE_Route53_PfxPassword",
      "Label": "PFX Password",
      "HelpText": "Password to use when converting to / from PFX. ",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "c258ee0e-e298-4fa1-9c66-5ac0749409c5",
      "Name": "LE_Route53_ReplaceIfExpiresInDays",
      "Label": "Replace expiring certificate before N days",
      "HelpText": "Replace the certificate if it expiries within N days",
      "DefaultValue": "30",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "2935998d-d030-4af6-ad42-39e8b85e2dce",
      "Name": "LE_Route53_AWSAccount",
      "Label": "AWS account",
      "HelpText": "An AWS Account that has API access to make Route53 changes. ",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "AmazonWebServicesAccount"
      }
    },
    {
      "Id": "85af482d-e577-40b8-94e5-626e545adab5",
      "Name": "LE_Route53_Octopus_APIKey",
      "Label": "Octopus Deploy API key",
      "HelpText": "A Octopus Deploy API key with access to change Certificates in the Certificate Store. ",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "00d513ed-3d75-48d2-954a-0a0165d85530",
      "Name": "LE_Route53_Use_Staging",
      "Label": "Use Lets Encrypt Staging",
      "HelpText": "Should the Certificate be generated using the Lets Encrypt Staging infrastructure?",
      "DefaultValue": "false",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "f1fd315d-9fc5-4b15-b53c-9381ecc5cb88",
      "Name": "LE_Route53_ContactEmailAddress",
      "Label": "Contact Email Address",
      "HelpText": "Email Address",
      "DefaultValue": "#{Octopus.Deployment.CreatedBy.EmailAddress}",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "2dc7d9bc-9eee-4ee0-a33c-ef9371ed69f1",
      "Name": "LE_Route53_CreateWildcardSAN",
      "Label": "Create Wildcard SAN",
      "HelpText": "Should the certificate have a Subject Alternative Name (SAN) excluding the wildcard?\n\ne.g. a certificate domain of `*.internal.example-domain.com` could also have a SAN of `internal.example-domain.com`",
      "DefaultValue": "false",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "Octopus.Action.Script.ScriptBody": "###############################################################################\n# TLS 1.2\n###############################################################################\n[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\n###############################################################################\n# Required Modules folder\n###############################################################################\nWrite-Host \"Checking for required powershell modules folder\"\n$ModulesFolder = \"$HOME\\Documents\\WindowsPowerShell\\Modules\"\nif ($PSEdition -eq \"Core\") {\n    if ($PSVersionTable.Platform -eq \"Unix\") {\n        $ModulesFolder = \"$HOME/.local/share/powershell/Modules\"\n    }\n    else {\n        $ModulesFolder = \"$HOME\\Documents\\PowerShell\\Modules\"\n    }\n}\n$PSModuleFolderExists = (Test-Path $ModulesFolder)\nif ($PSModuleFolderExists -eq $False) {\n\tWrite-Host \"Creating directory: $ModulesFolder\"\n\tNew-Item $ModulesFolder -ItemType Directory -Force\n    $env:PSModulePath = $ModulesFolder + [System.IO.Path]::PathSeparator + $env:PSModulePath\n}\n\n###############################################################################\n# Required Modules\n###############################################################################\nWrite-Host \"Checking for required modules.\"\n$required_posh_acme_version = 3.12.0\n$module_check = Get-Module -ListAvailable -Name Posh-Acme | Where-Object { $_.Version -ge $required_posh_acme_version }\n\nif (-not ($module_check)) {\n    Write-Host \"Ensuring NuGet provider is bootstrapped.\"\n    Get-PackageProvider NuGet -ForceBootstrap | Out-Null\n    Write-Host \"Installing Posh-ACME.\"\n    Install-Module -Name Posh-ACME -MinimumVersion 3.12.0 -Scope CurrentUser -Force\n}\n\nImport-Module Posh-ACME\n\n###############################################################################\n# Constants\n###############################################################################\n$LE_Route53_CertificateDomain = $OctopusParameters[\"LE_Route53_CertificateDomain\"]\n$LE_Route53_CertificateName = \"Lets Encrypt - $($LE_Route53_CertificateDomain)\"\n\n# Issuer used in a cert could be one of multiple, including ones no longer supported by Let's Encrypt\n$LE_Route53_Fake_Issuers = @(\"Fake LE Intermediate X1\", \"(STAGING) Artificial Apricot R3\", \"(STAGING) Ersatz Edamame E1\", \"(STAGING) Pseudo Plum E5\", \"(STAGING) False Fennel E6\", \"(STAGING) Puzzling Parsnip E7\", \"(STAGING) Mysterious Mulberry E8\", \"(STAGING) Fake Fig E9\", \"(STAGING) Counterfeit Cashew R10\", \"(STAGING) Wannabe Watercress R11\", \"(STAGING) Riddling Rhubarb R12\", \"(STAGING) Tenuous Tomato R13\", \"(STAGING) Not Nectarine R14\")\n$LE_Route53_Issuers = @(\"Let's Encrypt Authority X3\", \"E1\", \"E2\", \"R3\", \"R4\", \"R5\", \"R6\", \"R10\", \"R11\")\n\n###############################################################################\n# Helpers\n###############################################################################\nfunction Get-WebRequestErrorBody {\n    param (\n        $RequestError\n    )\n\n    # Powershell < 6 you can read the Exception\n    if ($PSVersionTable.PSVersion.Major -lt 6) {\n        if ($RequestError.Exception.Response) {\n            $reader = New-Object System.IO.StreamReader($RequestError.Exception.Response.GetResponseStream())\n            $reader.BaseStream.Position = 0\n            $reader.DiscardBufferedData()\n            $response = $reader.ReadToEnd()\n\n            return $response | ConvertFrom-Json\n        }\n    }\n    else {\n        return $RequestError.ErrorDetails.Message\n    }\n}\n\n###############################################################################\n# Functions\n###############################################################################\nfunction Get-LetsEncryptCertificate {\n    Write-Debug \"Entering: Get-LetsEncryptCertificate\"\n\n    if ($OctopusParameters[\"LE_Route53_Use_Staging\"] -eq $True) {\n        Write-Host \"Using Lets Encrypt Server: Staging\"\n        Set-PAServer LE_STAGE;\n    }\n    else {\n        Write-Host \"Using Lets Encrypt Server: Production\"\n        Set-PAServer LE_PROD;\n    }\n\n    # Clobber account if it exists.\n    $le_account = Get-PAAccount\n    if ($le_account) {\n        Remove-PAAccount $le_account.Id -Force\n    }\n\n    $aws_secret_key = ConvertTo-SecureString -String $OctopusParameters[\"LE_Route53_AWSAccount.SecretKey\"] -AsPlainText -Force\n    $route53_params = @{\n        R53AccessKey = $OctopusParameters[\"LE_Route53_AWSAccount.AccessKey\"];\n        R53SecretKey = $aws_secret_key\n    }\n\n    try {\n        $DnsPlugins = @(\"Route53\")\n        $DomainList = @($LE_Route53_CertificateDomain)\n        \n        # If domain is a wildcard e.g. *.example-domain.com, check if a SAN has been requested e.g. example-domain.com.\n        if ($LE_Route53_CertificateDomain -match \"\\*.\" -and $OctopusParameters[\"LE_Route53_CreateWildcardSAN\"] -eq $True) {\n            $LE_Route53_Certificate_SAN = $LE_Route53_CertificateDomain.Replace(\"*.\",\"\")\n            $DomainList += $LE_Route53_Certificate_SAN\n            # Include additional DnsPlugin of same type to surpress warning.\n            $DnsPlugins += \"Route53\"\n        }\n\n        $Cert_Params = @{\n            Domain = $DomainList\n            AcceptTOS = $True;\n            Contact = $OctopusParameters[\"LE_Route53_ContactEmailAddress\"];\n            DnsPlugin = $DnsPlugins;\n            PluginArgs = $route53_params;\n            PfxPass = $OctopusParameters[\"LE_Route53_PfxPassword\"];\n            Force = $True;\n        }\n\n        return New-PACertificate @Cert_Params\n    }\n    catch {\n        Write-Host \"Failed to Create Certificate. Error Message: $($_.Exception.Message). See Debug output for details.\"\n        Write-Debug (Get-WebRequestErrorBody -RequestError $_)\n        exit 1\n    }\n}\n\nfunction Get-OctopusCertificates {\n    Write-Debug \"Entering: Get-OctopusCertificates\"\n\n    $octopus_uri = $OctopusParameters[\"Octopus.Web.ServerUri\"]\n    $octopus_space_id = $OctopusParameters[\"Octopus.Space.Id\"]\n    $octopus_headers = @{ \"X-Octopus-ApiKey\" = $OctopusParameters[\"LE_Route53_Octopus_APIKey\"] }\n    $octopus_certificates_uri = \"$octopus_uri/api/$octopus_space_id/certificates?search=$($LE_Route53_CertificateDomain)\"\n\n    try {\n        # Get a list of certificates that match our domain search criteria.\n        $certificates_search = Invoke-WebRequest -Uri $octopus_certificates_uri -Method Get -Headers $octopus_headers -UseBasicParsing -ErrorAction Stop | ConvertFrom-Json | Select-Object -ExpandProperty Items\n\n        # We don't want to confuse Production and Staging Lets Encrypt Certificates.\n        $possible_issuers = $LE_Route53_Issuers\n        if ($OctopusParameters[\"LE_Route53_Use_Staging\"] -eq $True) {\n            $possible_issuers = $LE_Route53_Fake_Issuers\n        }\n\n        return $certificates_search | Where-Object {\n            $_.SubjectCommonName -eq $LE_Route53_CertificateDomain -and\n            $possible_issuers -contains $_.IssuerCommonName -and\n            $null -eq $_.ReplacedBy -and\n            $null -eq $_.Archived\n        }\n    }\n    catch {\n        Write-Host \"Could not retrieve certificates from Octopus Deploy. Error: $($_.Exception.Message). See Debug output for details.\"\n        Write-Debug (Get-WebRequestErrorBody -RequestError $_)\n        exit 1\n    }\n}\n\nfunction Publish-OctopusCertificate {\n    param (\n        [string] $JsonBody\n    )\n\n    Write-Debug \"Entering: Publish-OctopusCertificate\"\n\n    if (-not ($JsonBody)) {\n        Write-Host \"Existing Certificate Id and a replace Certificate are required.\"\n        exit 1\n    }\n\n    $octopus_uri = $OctopusParameters[\"Octopus.Web.ServerUri\"]\n    $octopus_space_id = $OctopusParameters[\"Octopus.Space.Id\"]\n    $octopus_headers = @{ \"X-Octopus-ApiKey\" = $OctopusParameters[\"LE_Route53_Octopus_APIKey\"] }\n    $octopus_certificates_uri = \"$octopus_uri/api/$octopus_space_id/certificates\"\n\n    try {\n        Invoke-WebRequest -Uri $octopus_certificates_uri -Method Post -Headers $octopus_headers -Body $JsonBody -UseBasicParsing\n        Write-Host \"Published $($LE_Route53_CertificateDomain) certificate to the Octopus Deploy Certificate Store.\"\n    }\n    catch {\n        Write-Host \"Failed to publish $($LE_Route53_CertificateDomain) certificate. Error: $($_.Exception.Message). See Debug output for details.\"\n        Write-Debug (Get-WebRequestErrorBody -RequestError $_)\n        exit 1\n    }\n}\n\nfunction Update-OctopusCertificate {\n    param (\n        [string]$Certificate_Id,\n        [string]$JsonBody\n    )\n\n    Write-Debug \"Entering: Update-OctopusCertificate\"\n\n    if (-not ($Certificate_Id -and $JsonBody)) {\n        Write-Host \"Existing Certificate Id and a replace Certificate are required.\"\n        exit 1\n    }\n\n    $octopus_uri = $OctopusParameters[\"Octopus.Web.ServerUri\"]\n    $octopus_space_id = $OctopusParameters[\"Octopus.Space.Id\"]\n    $octopus_headers = @{ \"X-Octopus-ApiKey\" = $OctopusParameters[\"LE_Route53_Octopus_APIKey\"] }\n    $octopus_certificates_uri = \"$octopus_uri/api/$octopus_space_id/certificates/$Certificate_Id/replace\"\n\n    try {\n        Invoke-WebRequest -Uri $octopus_certificates_uri -Method Post -Headers $octopus_headers -Body $JsonBody -UseBasicParsing\n        Write-Host \"Replaced $($LE_Route53_CertificateDomain) certificate in the Octopus Deploy Certificate Store.\"\n    }\n    catch {\n        Write-Error \"Failed to replace $($LE_Route53_CertificateDomain) certificate. Error: $($_.Exception.Message). See Debug output for details.\"\n        Write-Debug (Get-WebRequestErrorBody -RequestError $_)\n        exit 1\n    }\n}\n\nfunction Get-NewCertificatePFXAsJson {\n    param (\n        $Certificate\n    )\n\n    Write-Debug \"Entering: Get-NewCertificatePFXAsJson\"\n\n    if (-not ($Certificate)) {\n        Write-Host \"Certificate is required.\"\n        Exit 1\n    }\n\n    [Byte[]]$certificate_buffer = [System.IO.File]::ReadAllBytes($Certificate.PfxFullChain)\n    $certificate_base64 = [convert]::ToBase64String($certificate_buffer)\n\n    $certificate_body = @{\n        Name = \"$LE_Route53_CertificateName\";\n        Notes            = \"\";\n        CertificateData  = @{\n            HasValue = $true;\n            NewValue = $certificate_base64;\n        };\n        Password         = @{\n            HasValue = $true;\n            NewValue = $OctopusParameters[\"LE_Route53_PfxPassword\"];\n        };\n    }\n\n    return $certificate_body | ConvertTo-Json\n}\n\nfunction Get-ReplaceCertificatePFXAsJson {\n    param (\n        $Certificate\n    )\n\n    Write-Debug \"Entering: Get-ReplaceCertificatePFXAsJson\"\n\n    if (-not ($Certificate)) {\n        Write-Host \"Certificate is required.\"\n        Exit 1\n    }\n\n    [Byte[]]$certificate_buffer = [System.IO.File]::ReadAllBytes($Certificate.PfxFullChain)\n    $certificate_base64 = [convert]::ToBase64String($certificate_buffer)\n\n    $certificate_body = @{\n        CertificateData = $certificate_base64;\n        Password        = $OctopusParameters[\"LE_Route53_PfxPassword\"];\n    }\n\n    return $certificate_body | ConvertTo-Json\n}\n\n###############################################################################\n# DO THE THING | MAIN |\n###############################################################################\nWrite-Debug \"Do the Thing\"\n\nWrite-Host \"Checking for existing Lets Encrypt Certificates in the Octopus Deploy Certificates Store.\"\n$certificates = Get-OctopusCertificates\n\n# Check for PFX & PEM\nif ($certificates) {\n\n    # Handle weird behavior between Powershell 5 and Powershell 6+\n    $certificate_count = 1\n    if ($certificates.Count -ge 1) {\n        $certificate_count = $certificates.Count\n    }\n\n    Write-Host \"Found $certificate_count for $($LE_Route53_CertificateDomain).\"\n    Write-Host \"Checking to see if any expire within $($OctopusParameters[\"LE_Route53_ReplaceIfExpiresInDays\"]) days.\"\n\n    # Check Expiry Dates\n    $expiring_certificates = $certificates | Where-Object { [DateTime]$_.NotAfter -lt (Get-Date).AddDays($OctopusParameters[\"LE_Route53_ReplaceIfExpiresInDays\"]) }\n\n    if ($expiring_certificates) {\n        Write-Host \"Found certificates that expire with $($OctopusParameters[\"LE_Route53_ReplaceIfExpiresInDays\"]) days. Requesting new certificates for $($LE_Route53_CertificateDomain) from Lets Encrypt\"\n        $le_certificate = Get-LetsEncryptCertificate\n\n        # PFX\n        $existing_certificate = $certificates | Where-Object { $_.CertificateDataFormat -eq \"Pkcs12\" } | Select-Object -First 1\n        $certificate_as_json = Get-ReplaceCertificatePFXAsJson -Certificate $le_certificate\n        Update-OctopusCertificate -Certificate_Id $existing_certificate.Id -JsonBody $certificate_as_json\n    }\n    else {\n        Write-Host \"Nothing to do here...\"\n    }\n\n    exit 0\n}\n\n# No existing Certificates - Lets get some new ones.\nWrite-Host \"No existing certificates found for $($LE_Route53_CertificateDomain).\"\nWrite-Host \"Request New Certificate for $($LE_Route53_CertificateDomain) from Lets Encrypt\"\n\n# New Certificate..\n$le_certificate = Get-LetsEncryptCertificate\n\nWrite-Host \"Publishing: LetsEncrypt - $($LE_Route53_CertificateDomain) (PFX)\"\n$certificate_as_json = Get-NewCertificatePFXAsJson -Certificate $le_certificate\nPublish-OctopusCertificate -JsonBody $certificate_as_json\n\nWrite-Host \"GREAT SUCCESS\"\n",
    "Octopus.Action.SubstituteInFiles.Enabled": "True"
  },
  "Category": "Lets Encrypt",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/letsencrypt-route-53.json",
  "Website": "/step-templates/55b654f4-3e01-424f-b299-f99beddf0501",
  "Logo": "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",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Thursday, August 1, 2024