Venafi TPP - Import Certificate into Octopus

Octopus.Script exported 2021-08-23 by benjimac93 belongs to ‘Venafi’ category.

This step template will authenticate against a Venafi TPP instance using an existing OAuth access token, export a certificate by its Distinguished Name (DN), and then import the certificate into the Octopus certificate store.

The export is achieved using the VenafiPS PowerShell module’s Export-VenafiCertificate function.


Export options:

  • Provide the distinguished name (DN) path to the certificate.
  • Choose from the following export formats:
    • Base64
    • Base64 (PKCS #8)
    • DER
    • PKCS #12
  • Optional - Include the full certificate chain in the export.
  • Optional - Friendly name (Label or alias) to use. Permitted with Base64 and PKCS #12 formats.
  • Optional - Include the private key in the export. Not supported with DER format.
  • Optional - Provide a password to be used for the exported private key.
  • Optional - Also store the export certificate response in JSON format in an Octopus sensitive output variable. This output variable can then be used in additional deployment or runbook steps.
  • Optional - on successful completion, you can revoke the access token used.

Octopus Import options:

  • Octopus URL
  • Octopus API Key
  • Octopus Space name
  • Certificate name
  • Optional - replace the existing certificate if it already exists

Required:

  • The VenafiPS PowerShell module installed on the deployment target or worker. If the module can’t be found, the step will attempt to download a version from the PowerShell gallery.
  • PowerShell 5 or greater.
  • Octopus API key with permission to save the certificate.

Notes:

  • Tested on Octopus 2021.2.
  • Tested with VenafiPS 3.1.5.
  • Tested with both Windows PowerShell and PowerShell Core on Linux.

Parameters

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

Venafi TPP Server

Venafi.TPP.ImportCert.Server =

Required: The URL of the Venafi TPP instance you want to export the certificate from.

For example: https://mytppserver.example.com.

Venafi TPP Access Token

Venafi.TPP.ImportCert.AccessToken =

Required: The access token to authenticate against the TPP instance.

Venafi TPP Certificate Path

Venafi.TPP.ImportCert.DNPath =

Required: The Distinguished Name (DN) of the certificate you wish to export. This is the absolute path to the certificate in the TPP instance, separated by \.

Certificate Export Format

Venafi.TPP.ImportCert.Format =

Required: The certificate export format. Valid options are:

  • Base64
  • Base64 (PKCS #8)
  • DER
  • PKCS #12

Include certificate chain (Optional)

Venafi.TPP.ImportCert.IncludeChain = False

Optional: Include the certificate chain with the exported certificate. Not supported with DER format. Default: False.

Friendly Name (Optional)

Venafi.TPP.ImportCert.FriendlyName =

Optional: Label or alias to use. Permitted with Base64 and PKCS #12 formats.

Include Private Key (Optional)

Venafi.TPP.ImportCert.IncludePrivateKey = False

Optional: Include the private key in the certificate export. If this is selected, the Venafi.TPP.Export.PrivateKeyPassword must also be provided. Default: False.

Private Key password (Optional)

Venafi.TPP.ImportCert.PrivateKeyPassword =

Optional: The password required to include the private key. Not supported with DER format. You must adhere to the following rules:

  • Password is at least 12 characters.
  • Comprised of at least three of the following:
    • Uppercase alphabetic letters
    • Lowercase alphabetic letters
    • Numeric characters
    • Special characters

Certificate output variable name (Optional)

Venafi.TPP.ImportCert.OutputVariableName =

Optional: Create an output variable with the certificate details returned from the export call. The certificate details will be stored in JSON format.

Revoke access token on completion?

Venafi.TPP.ImportCert.RevokeTokenOnCompletion = False

Optional: Should the access token used be revoked once the step has been completed successfully? Default: False.

Octopus Server Url

Venafi.TPP.ImportCert.OctopusServerUri =

Required: Provide the base URL of your Octopus Server. There are two built-in Octopus variables you can use:

  • #{if Octopus.Web.ServerUri}#{Octopus.Web.ServerUri}#{else}#{Octopus.Web.BaseUrl}#{/if}
  • Octopus.Web.ServerUri

See our system variables page for further details.

Octopus API Key

Venafi.TPP.ImportCert.OctopusApiKey =

Required: Provide an Octopus API Key with appropriate permissions to save the certificate.

Octopus Space Name

Venafi.TPP.ImportCert.OctopusSpaceName = #{Octopus.Space.Name}

Required: Provide the Space name for the certificate to be saved in. The default is the current space the step is running within: #{Octopus.Space.Name}.

Octopus Certificate Name

Venafi.TPP.ImportCert.OctopusCertificateName =

Required: A short, memorable, unique name for the imported certificate.

Replace existing Octopus certificate?

Venafi.TPP.ImportCert.OctopusReplaceExistingCertificate = True

Optional: If a certificate exists in Octopus with the same name as the one to be imported, should the one stored in Octopus be replaced? Default: True.

Note: If multiple matches are found, the step template will not replace any, and will log a warning instead.

See replacing certificates for further information.

Script body

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

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ErrorActionPreference = 'Stop'

# TPP required variables
$Server = $OctopusParameters["Venafi.TPP.ImportCert.Server"]
$Token = $OctopusParameters["Venafi.TPP.ImportCert.AccessToken"]
$Path = $OctopusParameters["Venafi.TPP.ImportCert.DNPath"]
$Format = $OctopusParameters["Venafi.TPP.ImportCert.Format"]

# TPP optional variables
$IncludeChain = $OctopusParameters["Venafi.TPP.ImportCert.IncludeChain"]
$FriendlyName = $OctopusParameters["Venafi.TPP.ImportCert.FriendlyName"]
$IncludePrivateKey = $OctopusParameters["Venafi.TPP.ImportCert.IncludePrivateKey"]
$PrivateKeyPassword = $OctopusParameters["Venafi.TPP.ImportCert.PrivateKeyPassword"]
$OutputVariableName = $OctopusParameters["Venafi.TPP.ImportCert.OutputVariableName"]
$RevokeToken = $OctopusParameters["Venafi.TPP.ImportCert.RevokeTokenOnCompletion"]

# Octopus required variables
$OctopusServerUri = $OctopusParameters["Venafi.TPP.ImportCert.OctopusServerUri"]
$OctopusApiKey = $OctopusParameters["Venafi.TPP.ImportCert.OctopusApiKey"]
$OctopusSpaceName = $OctopusParameters["Venafi.TPP.ImportCert.OctopusSpaceName"]
$OctopusCertificateName = $OctopusParameters["Venafi.TPP.ImportCert.OctopusCertificateName"]
$OctopusReplaceExistingCertificate = $OctopusParameters["Venafi.TPP.ImportCert.OctopusReplaceExistingCertificate"]

# TPP validation
if ([string]::IsNullOrWhiteSpace($Server)) {
    throw "Required parameter Venafi.TPP.ImportCert.Server not specified"
}
if ([string]::IsNullOrWhiteSpace($Token)) {
    throw "Required parameter Venafi.TPP.ImportCert.AccessToken not specified"
}
if ([string]::IsNullOrWhiteSpace($Path)) {
    throw "Required parameter Venafi.TPP.ImportCert.DNPath not specified"
}
else {
    if ($Path.Contains("\") -eq $False) {
        throw "At least one '\' is required for the Venafi.TPP.ImportCert.DNPath value"
    }
}
if ([string]::IsNullOrWhiteSpace($Format)) {
    throw "Required parameter Venafi.TPP.ImportCert.Format not specified"
}

# TPP conditional validation
if ($IncludePrivateKey -eq $True) {
    if ([string]::IsNullOrWhiteSpace($PrivateKeyPassword)) {
        throw "IncludePrivateKey set to true, but parameter Venafi.TPP.ImportCert.PrivateKeyPassword not specified"
    }
}
else {
    $PrivateKeyPassword = $null
}

# Octopus validation
if ([string]::IsNullOrWhiteSpace($OctopusServerUri)) {
    throw "Required parameter Venafi.TPP.ImportCert.OctopusServerUri not specified"
}
if ([string]::IsNullOrWhiteSpace($OctopusApiKey)) {
    throw "Required parameter Venafi.TPP.ImportCert.OctopusApiKey not specified"
}
if ([string]::IsNullOrWhiteSpace($OctopusSpaceName)) {
    throw "Required parameter Venafi.TPP.ImportCert.OctopusSpaceName not specified"
}
if ([string]::IsNullOrWhiteSpace($OctopusCertificateName)) {
    throw "Required parameter Venafi.TPP.ImportCert.OctopusCertificateName not specified"
}
if ([string]::IsNullOrWhiteSpace($OctopusReplaceExistingCertificate)) {
    throw "Required parameter Venafi.TPP.ImportCert.OctopusReplaceExistingCertificate not specified"
}

# Helper functions
###############################################################################
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()
            $rawResponse = $reader.ReadToEnd()
            $response = ""
            try { $response = $rawResponse | ConvertFrom-Json } catch { $response = $rawResponse }
            return $response
        }
    }
    else {
        return $RequestError.ErrorDetails.Message
    }
}

function Get-MatchingOctopusCertificates {
    param (
        [string]$ServerUri,
        [string]$ApiKey,
        [string]$SpaceId,
        [string]$CertificateName
    )
    Write-Debug "Entering: Get-MatchingOctopusCertificates"

    try {

        $header = @{ "X-Octopus-ApiKey" = $ApiKey }

        # Get a list of certificates that match our domain search criteria.
        $partial_certificates = (Invoke-RestMethod -Uri "$ServerUri/api/$SpaceId/certificates?partialName=$([uri]::EscapeDataString($CertificateName))&skip=0&take=2000" -Headers $header) | Select-Object -ExpandProperty Items

        # return certs that arent archived and havent been replaced.
        return $partial_certificates | Where-Object {
            $null -eq $_.ReplacedBy -and
            $null -eq $_.Archived -and 
            $CertificateName -eq $_.Name
        }
    }
    catch {
        $Detail = (Get-WebRequestErrorBody -RequestError $_)
        Write-Error "Could not retrieve certificates from Octopus. Error: $($_.Exception.Message).`n`t$Detail"
    }
}

function Replace-OctopusCertificate {
    param (
        [string]$ServerUri,
        [string]$ApiKey,
        [string]$SpaceId,
        [string]$CertificateId,
        [string]$CertificateName,
        [string]$CertificateData,
        [string]$CertificatePwd
    )
    Write-Debug "Entering: Replace-OctopusCertificate"   
    try {

        $header = @{ "X-Octopus-ApiKey" = $ApiKey }

        $replacement_certificate = @{
            CertificateData = $CertificateData
        }

        if (![string]::IsNullOrWhiteSpace($CertificatePwd)) {
            $replacement_certificate.Password = $CertificatePwd
        }
        
        # Replace the cert
        $updated_certificate = Invoke-RestMethod -Method Post -Uri "$ServerUri/api/$SpaceId/certificates/$CertificateId/replace" -Headers $header -Body ($replacement_certificate | ConvertTo-Json -Depth 10)
        Write-Highlight "Replaced certificate in Octopus for '$($updated_certificate.Name)' ($($updated_certificate.Id))"
    }
    catch {
        $Detail = (Get-WebRequestErrorBody -RequestError $_)
        Write-Error "Could not replace certificate in Octopus. Error: $($_.Exception.Message).`n`t$Detail"
    }
}

function New-OctopusCertificate {
    param (
        [string]$ServerUri,
        [string]$ApiKey,
        [string]$SpaceId,
        [string]$CertificateName,
        [string]$CertificateData,
        [string]$CertificatePwd
    )
    Write-Debug "Entering: New-OctopusCertificate"   
    try {

        $header = @{ "X-Octopus-ApiKey" = $ApiKey }

        $certificate = @{
            Name            = $CertificateName;
            CertificateData = @{
                NewValue = $CertificateData;
                HasData  = $True;
            }
            Password        = @{
                HasValue = $False;
                NewValue = $null;
            }
        }

        if (![string]::IsNullOrWhiteSpace($CertificatePwd)) {
            $certificate.Password.NewValue = $CertificatePwd
            $certificate.Password.HasData = $True
        }
        
        # Create new certificate
        $new_certificate = Invoke-RestMethod -Method Post -Uri "$ServerUri/api/$SpaceId/certificates" -Headers $header -Body ($certificate | ConvertTo-Json -Depth 10)
        Write-Highlight "New certificate created in Octopus for '$($new_certificate.Name)' ($($new_certificate.Id))"
    }
    catch {
        $Detail = (Get-WebRequestErrorBody -RequestError $_)
        Write-Error "Could not create new certificate in Octopus. Error: $($_.Exception.Message).`n`t$Detail"
    }
}

function Clean-VenafiCertificateForOctopus {
    param (
        [string]$CertificateData
    )
    Write-Debug "Entering: Clean-VenafiCertificateForOctopus"   
    $PemHeaderFragment = "-----BEGIN *"
    $PemFooterFragment = "-----END *"

    $CertificateBytes = [Convert]::FromBase64String($CertificateData)
    $RawCert = [System.Text.Encoding]::UTF8.GetString($CertificateBytes)
    
    $CleanedCertLines = @()
    if (![string]::IsNullOrWhiteSpace($RawCert)) {
        $RawCertLines = ($RawCert -Split "`n")
        $currentLine = 0
        while ($currentLine -lt $RawCertLines.Length) {
            Write-Verbose "Working on line $currentLine"
            $headerPosition = [Array]::FindIndex($RawCertLines, $currentLine, [Predicate[string]] { $args[0] -like $PemHeaderFragment })
            if ($headerPosition -gt -1) {
                $footerPosition = [Array]::FindIndex($RawCertLines, $headerPosition, [Predicate[string]] { $args[0] -like $PemFooterFragment })
                if ($footerPosition -lt 0) {
                    throw "Unable to find a matching '-----END' PEM fragment!"
                }
                else {
                    Write-Verbose "Selecting PEM lines: $headerPosition-$footerPosition"
                    $pemLines = $RawCertLines[$headerPosition..$footerPosition]
                    $CleanedCertLines += $pemLines
                    $currentLine = $footerPosition
                }
            }
            else {
                $currentLine++
            }
        }
    }
    if ($CleanedCertLines.Length -le 0) {
        throw "Something went wrong extracting contents from file (no cleansed contents)"
    }

    $CleanedCert = $CleanedCertLines | Out-String
    $CleanedCertData = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($CleanedCert))
    
    return $CleanedCertData    
}
###############################################################################
# MAIN STEP TEMPLATE FLOW
###############################################################################

# TPP Access token
$SecureToken = ConvertTo-SecureString $Token -AsPlainText -Force
[PSCredential]$AccessToken = New-Object System.Management.Automation.PsCredential("token", $SecureToken)

# Clean-up
$Server = $Server.TrimEnd('/')
$OctopusServerUri = $OctopusServerUri.TrimEnd('/')
$OctopusSpaceName = $OctopusSpaceName.Trim(" ")
$OctopusCertificateName = $OctopusCertificateName.Trim(" ")

# Required Venafi Module
function Get-NugetPackageProviderNotInstalled {
    # See if the nuget package provider has been installed
    return ($null -eq (Get-PackageProvider -ListAvailable -Name Nuget -ErrorAction SilentlyContinue))
}

# Check to see if the package provider has been installed
if ((Get-NugetPackageProviderNotInstalled) -ne $false) {
    Write-Host "Nuget package provider not found, installing ..."    
    Install-PackageProvider -Name Nuget -Force -Scope CurrentUser
}

Write-Host "Checking for required VenafiPS module ..."
$required_venafips_version = 3.1.5
$module_available = Get-Module -ListAvailable -Name VenafiPS | Where-Object { $_.Version -ge $required_venafips_version }
if (-not ($module_available)) {
    Write-Host "Installing VenafiPS module ..."
    Install-Module -Name VenafiPS -MinimumVersion 3.1.5 -Scope CurrentUser -Force
}
else {
    $first_match = $module_available | Select-Object -First 1 
    Write-Host "Found version: $($first_match.Version)"
}

Write-Host "Importing VenafiPS module ..."
Import-Module VenafiPS

$StepName = $OctopusParameters["Octopus.Step.Name"]
$ExportFormatsIncompatibleWithOctopusCertificateStore = @("Base64", "Base64 (PKCS #8)")

Write-Verbose "Venafi.TPP.ImportCert.Server: $Server"
Write-Verbose "Venafi.TPP.ImportCert.AccessToken: ********"
Write-Verbose "Venafi.TPP.ImportCert.DNPath: $Path"
Write-Verbose "Venafi.TPP.ImportCert.Format: $Format"
Write-Verbose "Venafi.TPP.ImportCert.IncludeChain: $IncludeChain"
Write-Verbose "Venafi.TPP.ImportCert.FriendlyName: $FriendlyName"
Write-Verbose "Venafi.TPP.ImportCert.IncludePrivateKey: $IncludePrivateKey"
Write-Verbose "Venafi.TPP.ImportCert.PrivateKeyPassword: ********"
Write-Verbose "Venafi.TPP.ImportCert.CertDetails.OutputVariableName: $OutputVariableName"
Write-Verbose "Venafi.TPP.ImportCert.RevokeTokenOnCompletion: $RevokeTokenOnCompletion"
Write-Verbose "Venafi.TPP.ImportCert.OctopusServerUri: $OctopusServerUri"
Write-Verbose "Venafi.TPP.ImportCert.OctopusApiKey: ********"
Write-Verbose "Venafi.TPP.ImportCert.OctopusSpaceName: $OctopusSpaceName"
Write-Verbose "Venafi.TPP.ImportCert.OctopusCertificateName: $OctopusCertificateName"
Write-Verbose "Venafi.TPP.ImportCert.OctopusReplaceExistingCertificate: $OctopusReplaceExistingCertificate"
Write-Verbose "Step Name: $StepName"

Write-Host "Requesting new session from $Server"
New-VenafiSession -Server $Server -AccessToken $AccessToken

# Export certificate
$ExportCert_Params = @{
    CertificateId = $Path;
    Format        = $Format;
}

# Optional IncludeChain field
if ($IncludeChain -eq $True) {
    $ExportCert_Params.IncludeChain = $True
}

# Optional FriendlyName field
if (-not [string]::IsNullOrWhiteSpace($FriendlyName)) {
    $ExportCert_Params.FriendlyName = $FriendlyName
}

# Optional Private key field
if (-not [string]::IsNullOrWhiteSpace($PrivateKeyPassword) -and $IncludePrivateKey -eq $True) {
    $SecurePrivateKeyPassword = ConvertTo-SecureString $PrivateKeyPassword -AsPlainText -Force
    $ExportCert_Params.PrivateKeyPassword = $SecurePrivateKeyPassword    
    $ExportCert_Params.IncludePrivateKey = $True
}

# Do the export
$ExportCertificateResponse = ((Export-VenafiCertificate @ExportCert_Params) 6> $null)

if ($null -eq $ExportCertificateResponse -or $null -eq $ExportCertificateResponse.CertificateData) {
    Write-Warning "No certificate data returned for path: $Path`nCheck the path value represents a certificate, and not a folder."
}
else {
    Write-Host "Successfully retrieved certificate data to export for path: $Path"
        
    # Get octopus space Id
    $header = @{ "X-Octopus-ApiKey" = $OctopusApiKey }
    $spaces = Invoke-RestMethod -Uri "$OctopusServerUri/api/spaces?partialName=$([uri]::EscapeDataString($OctopusSpaceName))&skip=0&take=500" -Headers $header 
    $OctopusSpace = @($spaces.Items | Where-Object { $_.Name -eq $OctopusSpaceName }) | Select-Object -First 1

    if ($null -eq $OctopusSpace) {
        throw "Couldnt find Octopus space with name '$OctopusSpaceName'."
    }

    # Check for certificate based on name
    $CertificateMatches = @(Get-MatchingOctopusCertificates -ServerUri $OctopusServerUri -ApiKey $OctopusApiKey -SpaceId $($OctopusSpace.Id) -CertificateName $OctopusCertificateName) 
    Write-Host "Found $($CertificateMatches.Length) certificates matching '$OctopusCertificateName'"

    $FirstCertificateMatch = $CertificateMatches | Select-Object -First 1
    $CertificateData = $ExportCertificateResponse.CertificateData

    if ($ExportFormatsIncompatibleWithOctopusCertificateStore -icontains $Format) {
        Write-Host "Requested export format $Format needs to be cleaned before import to Octopus."
        $CertificateData = Clean-VenafiCertificateForOctopus -CertificateData $CertificateData
        if ([string]::IsNullOrWhiteSpace($CertificateData)) {
            throw "Cleaned certificate data empty!"
        }
    }

    switch ($CertificateMatches.Length) {
        0 {  
            # New cert
            Write-Host "Creating a new certificate '$OctopusCertificateName'"
            New-OctopusCertificate -ServerUri $OctopusServerUri -ApiKey $OctopusApiKey -SpaceId $($OctopusSpace.Id) -CertificateName $OctopusCertificateName -CertificateData $($CertificateData) -CertificatePwd $PrivateKeyPassword
        }
        1 {  
            # One cert to replace
            if ($OctopusReplaceExistingCertificate -eq $False) {
                Write-Host "Replace existing certificate set to False, nothing to do."
            }
            else {
                Write-Host "Replacing existing certificate '$OctopusCertificateName' ($($FirstCertificateMatch.Id))"
                Replace-OctopusCertificate -ServerUri $OctopusServerUri -ApiKey $OctopusApiKey -SpaceId $($OctopusSpace.Id) -CertificateId $($FirstCertificateMatch.Id) -CertificateName $OctopusCertificateName -CertificateData $($CertificateData) -CertificatePwd $PrivateKeyPassword
            }
        }
        default {
            Write-Warning "Multiple certs matching name '$OctopusCertificateName' found, nothing to do."
            return
        }
    }

    if ([string]::IsNullOrWhiteSpace($OutputVariableName) -eq $False) {
        $CertificateJson = $ExportCertificateResponse | ConvertTo-Json -Compress -Depth 10 
        Set-OctopusVariable -Name $OutputVariableName -Value $CertificateJson -Sensitive
        Write-Highlight "Created sensitive output variable: ##{Octopus.Action[$StepName].Output.$OutputVariableName}"
    }
}

if ($RevokeToken -eq $true) {
    # Revoke TPP access token
    Write-Host "Revoking access token with $Server"
    Revoke-TppToken -AuthServer $Server -AccessToken $AccessToken -Force
}

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": "e10820c2-ae6d-4030-8a8a-b73ed60a81fc",
  "Name": "Venafi TPP - Import Certificate into Octopus",
  "Description": "This step template will authenticate against a Venafi TPP instance using an existing OAuth access token, export a certificate by its Distinguished Name (DN), and then import the certificate into the Octopus certificate store.\n\nThe export is achieved using the VenafiPS PowerShell module's [Export-VenafiCertificate](https://venafips.readthedocs.io/en/latest/functions/Export-VenafiCertificate/) function.\n\n---\n\n**Export options:**\n\n- Provide the distinguished name (DN) path to the certificate.\n- Choose from the following export formats:\n  - `Base64`\n  - `Base64 (PKCS #8)`\n  - `DER`\n  - `PKCS #12` \n- *Optional* - Include the full certificate chain in the export.\n- *Optional* - Friendly name (Label or alias) to use. Permitted with `Base64` and `PKCS #12` formats. \n- *Optional* - Include the private key in the export. Not supported with `DER` format.\n- *Optional* - Provide a password to be used for the exported private key.\n- *Optional* - Also store the export certificate response in `JSON` format in an [Octopus sensitive output variable](https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables). This output variable can then be used in additional deployment or runbook steps.\n- *Optional* - on successful completion, you can revoke the access token used.\n\n---\n\n**Octopus Import options:**\n\n- Octopus URL\n- Octopus API Key\n- Octopus Space name\n- Certificate name\n- *Optional* - replace the existing certificate if it already exists\n\n---\n\n**Required:** \n- The `VenafiPS` PowerShell module installed on the deployment target or worker. If the module can't be found, the step will attempt to download a version from the [PowerShell gallery](https://www.powershellgallery.com/packages/VenafiPS).\n- PowerShell `5` or greater.\n- Octopus API key with permission to save the certificate.\n\nNotes:\n\n- Tested on Octopus `2021.2`.\n- Tested with VenafiPS `3.1.5`.\n- Tested with both Windows PowerShell and PowerShell Core on Linux.",
  "Version": 2,
  "ExportedAt": "2021-08-23T12:40:10.975Z",
  "ActionType": "Octopus.Script",
  "Author": "benjimac93",
  "Packages": [],
  "Parameters": [
    {
      "Id": "56ef4967-37f5-40a0-a66e-f3fa589b6467",
      "Name": "Venafi.TPP.ImportCert.Server",
      "Label": "Venafi TPP Server",
      "HelpText": "*Required*: The URL of the Venafi TPP instance you want to export the certificate from.\n\nFor example: `https://mytppserver.example.com`.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "49bcdbbb-3674-4901-8bf6-164e5e4bc395",
      "Name": "Venafi.TPP.ImportCert.AccessToken",
      "Label": "Venafi TPP Access Token",
      "HelpText": "*Required*: The access token to authenticate against the TPP instance.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "e3156852-4ba9-4dc0-8d39-5a93c52b1910",
      "Name": "Venafi.TPP.ImportCert.DNPath",
      "Label": "Venafi TPP Certificate Path",
      "HelpText": "*Required*: The Distinguished Name (DN) of the certificate you wish to export. This is the absolute path to the certificate in the TPP instance, separated by `\\`.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "4f9f4d4b-d686-4d00-aa93-af35b7df320b",
      "Name": "Venafi.TPP.ImportCert.Format",
      "Label": "Certificate Export Format",
      "HelpText": "*Required*: The certificate export format. Valid options are:\n\n- `Base64`\n- `Base64 (PKCS #8)`\n- `DER`\n- `PKCS #12`",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "Base64|Base64\nBase64 (PKCS #8)|Base64 (PKCS #8)\nDER|DER\nPKCS #12|PKCS #12"
      }
    },
    {
      "Id": "309d30de-79b6-4461-8a54-1698aedd5822",
      "Name": "Venafi.TPP.ImportCert.IncludeChain",
      "Label": "Include certificate chain (Optional)",
      "HelpText": "*Optional*: Include the certificate chain with the exported certificate. Not supported with `DER` format. Default: `False`.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "71fecac3-25c4-4161-9135-94815a485f03",
      "Name": "Venafi.TPP.ImportCert.FriendlyName",
      "Label": "Friendly Name (Optional)",
      "HelpText": "*Optional*: Label or alias to use. Permitted with `Base64` and `PKCS #12` formats.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "2aaedf1d-be93-4df4-856c-c69650db452a",
      "Name": "Venafi.TPP.ImportCert.IncludePrivateKey",
      "Label": "Include Private Key (Optional)",
      "HelpText": "*Optional*: Include the private key in the certificate export. If this is selected, the `Venafi.TPP.Export.PrivateKeyPassword` must also be provided. Default: `False`.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "2d168360-bcbf-4bdc-833d-a9c182e98a47",
      "Name": "Venafi.TPP.ImportCert.PrivateKeyPassword",
      "Label": "Private Key password (Optional)",
      "HelpText": "*Optional*: The password required to include the private key. Not supported with `DER` format.  You must adhere to the following rules: \n\n- Password is at least 12 characters. \n- Comprised of at least three of the following: \n  - Uppercase alphabetic letters \n  - Lowercase alphabetic letters \n  - Numeric characters \n  - Special characters",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "84f92dd5-064b-47e5-bb11-3dd0faacfeb4",
      "Name": "Venafi.TPP.ImportCert.OutputVariableName",
      "Label": "Certificate output variable name (Optional)",
      "HelpText": "*Optional*: Create an output variable with the certificate details returned from the export call. The certificate details will be stored in `JSON` format.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "606acdfe-641a-47f2-a4ea-56559477ea0c",
      "Name": "Venafi.TPP.ImportCert.RevokeTokenOnCompletion",
      "Label": "Revoke access token on completion?",
      "HelpText": "*Optional*: Should the access token used be revoked once the step has been completed successfully? Default: `False`.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "7813080c-d6d6-4bd1-8c38-d6d03921f541",
      "Name": "Venafi.TPP.ImportCert.OctopusServerUri",
      "Label": "Octopus Server Url",
      "HelpText": "*Required*: Provide the base URL of your Octopus Server. There are two built-in Octopus variables you can use:\n\n- `#{if Octopus.Web.ServerUri}#{Octopus.Web.ServerUri}#{else}#{Octopus.Web.BaseUrl}#{/if}`\n- `Octopus.Web.ServerUri`\n\nSee our [system variables](https://octopus.com/docs/projects/variables/system-variables#Systemvariables-Server) page for further details.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "5be3cf08-43ef-44a3-bd89-3ec3b4928b01",
      "Name": "Venafi.TPP.ImportCert.OctopusApiKey",
      "Label": "Octopus API Key",
      "HelpText": "*Required*: Provide an Octopus API Key with appropriate permissions to save the certificate.\n",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "d901f398-a035-4ba6-a546-553360eed283",
      "Name": "Venafi.TPP.ImportCert.OctopusSpaceName",
      "Label": "Octopus Space Name",
      "HelpText": "*Required*: Provide the Space name for the certificate to be saved in. The default is the current space the step is running within: `#{Octopus.Space.Name}`.",
      "DefaultValue": "#{Octopus.Space.Name}",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "56f8e1a8-fb6c-4b91-a216-eadc2f5cd673",
      "Name": "Venafi.TPP.ImportCert.OctopusCertificateName",
      "Label": "Octopus Certificate Name",
      "HelpText": "*Required*: A short, memorable, unique name for the imported certificate.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "3a85993f-8844-49aa-951a-804471f53b23",
      "Name": "Venafi.TPP.ImportCert.OctopusReplaceExistingCertificate",
      "Label": "Replace existing Octopus certificate?",
      "HelpText": "*Optional*: If a certificate exists in Octopus with the same name as the one to be imported, should the one stored in Octopus be replaced? Default: `True`.\n\n**Note**: If multiple matches are found, the step template will not replace any, and will log a warning instead.\n\nSee [replacing certificates](https://octopus.com/docs/deployments/certificates/replace-certificate) for further information.",
      "DefaultValue": "True",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n$ErrorActionPreference = 'Stop'\n\n# TPP required variables\n$Server = $OctopusParameters[\"Venafi.TPP.ImportCert.Server\"]\n$Token = $OctopusParameters[\"Venafi.TPP.ImportCert.AccessToken\"]\n$Path = $OctopusParameters[\"Venafi.TPP.ImportCert.DNPath\"]\n$Format = $OctopusParameters[\"Venafi.TPP.ImportCert.Format\"]\n\n# TPP optional variables\n$IncludeChain = $OctopusParameters[\"Venafi.TPP.ImportCert.IncludeChain\"]\n$FriendlyName = $OctopusParameters[\"Venafi.TPP.ImportCert.FriendlyName\"]\n$IncludePrivateKey = $OctopusParameters[\"Venafi.TPP.ImportCert.IncludePrivateKey\"]\n$PrivateKeyPassword = $OctopusParameters[\"Venafi.TPP.ImportCert.PrivateKeyPassword\"]\n$OutputVariableName = $OctopusParameters[\"Venafi.TPP.ImportCert.OutputVariableName\"]\n$RevokeToken = $OctopusParameters[\"Venafi.TPP.ImportCert.RevokeTokenOnCompletion\"]\n\n# Octopus required variables\n$OctopusServerUri = $OctopusParameters[\"Venafi.TPP.ImportCert.OctopusServerUri\"]\n$OctopusApiKey = $OctopusParameters[\"Venafi.TPP.ImportCert.OctopusApiKey\"]\n$OctopusSpaceName = $OctopusParameters[\"Venafi.TPP.ImportCert.OctopusSpaceName\"]\n$OctopusCertificateName = $OctopusParameters[\"Venafi.TPP.ImportCert.OctopusCertificateName\"]\n$OctopusReplaceExistingCertificate = $OctopusParameters[\"Venafi.TPP.ImportCert.OctopusReplaceExistingCertificate\"]\n\n# TPP validation\nif ([string]::IsNullOrWhiteSpace($Server)) {\n    throw \"Required parameter Venafi.TPP.ImportCert.Server not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($Token)) {\n    throw \"Required parameter Venafi.TPP.ImportCert.AccessToken not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($Path)) {\n    throw \"Required parameter Venafi.TPP.ImportCert.DNPath not specified\"\n}\nelse {\n    if ($Path.Contains(\"\\\") -eq $False) {\n        throw \"At least one '\\' is required for the Venafi.TPP.ImportCert.DNPath value\"\n    }\n}\nif ([string]::IsNullOrWhiteSpace($Format)) {\n    throw \"Required parameter Venafi.TPP.ImportCert.Format not specified\"\n}\n\n# TPP conditional validation\nif ($IncludePrivateKey -eq $True) {\n    if ([string]::IsNullOrWhiteSpace($PrivateKeyPassword)) {\n        throw \"IncludePrivateKey set to true, but parameter Venafi.TPP.ImportCert.PrivateKeyPassword not specified\"\n    }\n}\nelse {\n    $PrivateKeyPassword = $null\n}\n\n# Octopus validation\nif ([string]::IsNullOrWhiteSpace($OctopusServerUri)) {\n    throw \"Required parameter Venafi.TPP.ImportCert.OctopusServerUri not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($OctopusApiKey)) {\n    throw \"Required parameter Venafi.TPP.ImportCert.OctopusApiKey not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($OctopusSpaceName)) {\n    throw \"Required parameter Venafi.TPP.ImportCert.OctopusSpaceName not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($OctopusCertificateName)) {\n    throw \"Required parameter Venafi.TPP.ImportCert.OctopusCertificateName not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($OctopusReplaceExistingCertificate)) {\n    throw \"Required parameter Venafi.TPP.ImportCert.OctopusReplaceExistingCertificate not specified\"\n}\n\n# Helper functions\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            $rawResponse = $reader.ReadToEnd()\n            $response = \"\"\n            try { $response = $rawResponse | ConvertFrom-Json } catch { $response = $rawResponse }\n            return $response\n        }\n    }\n    else {\n        return $RequestError.ErrorDetails.Message\n    }\n}\n\nfunction Get-MatchingOctopusCertificates {\n    param (\n        [string]$ServerUri,\n        [string]$ApiKey,\n        [string]$SpaceId,\n        [string]$CertificateName\n    )\n    Write-Debug \"Entering: Get-MatchingOctopusCertificates\"\n\n    try {\n\n        $header = @{ \"X-Octopus-ApiKey\" = $ApiKey }\n\n        # Get a list of certificates that match our domain search criteria.\n        $partial_certificates = (Invoke-RestMethod -Uri \"$ServerUri/api/$SpaceId/certificates?partialName=$([uri]::EscapeDataString($CertificateName))&skip=0&take=2000\" -Headers $header) | Select-Object -ExpandProperty Items\n\n        # return certs that arent archived and havent been replaced.\n        return $partial_certificates | Where-Object {\n            $null -eq $_.ReplacedBy -and\n            $null -eq $_.Archived -and \n            $CertificateName -eq $_.Name\n        }\n    }\n    catch {\n        $Detail = (Get-WebRequestErrorBody -RequestError $_)\n        Write-Error \"Could not retrieve certificates from Octopus. Error: $($_.Exception.Message).`n`t$Detail\"\n    }\n}\n\nfunction Replace-OctopusCertificate {\n    param (\n        [string]$ServerUri,\n        [string]$ApiKey,\n        [string]$SpaceId,\n        [string]$CertificateId,\n        [string]$CertificateName,\n        [string]$CertificateData,\n        [string]$CertificatePwd\n    )\n    Write-Debug \"Entering: Replace-OctopusCertificate\"   \n    try {\n\n        $header = @{ \"X-Octopus-ApiKey\" = $ApiKey }\n\n        $replacement_certificate = @{\n            CertificateData = $CertificateData\n        }\n\n        if (![string]::IsNullOrWhiteSpace($CertificatePwd)) {\n            $replacement_certificate.Password = $CertificatePwd\n        }\n        \n        # Replace the cert\n        $updated_certificate = Invoke-RestMethod -Method Post -Uri \"$ServerUri/api/$SpaceId/certificates/$CertificateId/replace\" -Headers $header -Body ($replacement_certificate | ConvertTo-Json -Depth 10)\n        Write-Highlight \"Replaced certificate in Octopus for '$($updated_certificate.Name)' ($($updated_certificate.Id))\"\n    }\n    catch {\n        $Detail = (Get-WebRequestErrorBody -RequestError $_)\n        Write-Error \"Could not replace certificate in Octopus. Error: $($_.Exception.Message).`n`t$Detail\"\n    }\n}\n\nfunction New-OctopusCertificate {\n    param (\n        [string]$ServerUri,\n        [string]$ApiKey,\n        [string]$SpaceId,\n        [string]$CertificateName,\n        [string]$CertificateData,\n        [string]$CertificatePwd\n    )\n    Write-Debug \"Entering: New-OctopusCertificate\"   \n    try {\n\n        $header = @{ \"X-Octopus-ApiKey\" = $ApiKey }\n\n        $certificate = @{\n            Name            = $CertificateName;\n            CertificateData = @{\n                NewValue = $CertificateData;\n                HasData  = $True;\n            }\n            Password        = @{\n                HasValue = $False;\n                NewValue = $null;\n            }\n        }\n\n        if (![string]::IsNullOrWhiteSpace($CertificatePwd)) {\n            $certificate.Password.NewValue = $CertificatePwd\n            $certificate.Password.HasData = $True\n        }\n        \n        # Create new certificate\n        $new_certificate = Invoke-RestMethod -Method Post -Uri \"$ServerUri/api/$SpaceId/certificates\" -Headers $header -Body ($certificate | ConvertTo-Json -Depth 10)\n        Write-Highlight \"New certificate created in Octopus for '$($new_certificate.Name)' ($($new_certificate.Id))\"\n    }\n    catch {\n        $Detail = (Get-WebRequestErrorBody -RequestError $_)\n        Write-Error \"Could not create new certificate in Octopus. Error: $($_.Exception.Message).`n`t$Detail\"\n    }\n}\n\nfunction Clean-VenafiCertificateForOctopus {\n    param (\n        [string]$CertificateData\n    )\n    Write-Debug \"Entering: Clean-VenafiCertificateForOctopus\"   \n    $PemHeaderFragment = \"-----BEGIN *\"\n    $PemFooterFragment = \"-----END *\"\n\n    $CertificateBytes = [Convert]::FromBase64String($CertificateData)\n    $RawCert = [System.Text.Encoding]::UTF8.GetString($CertificateBytes)\n    \n    $CleanedCertLines = @()\n    if (![string]::IsNullOrWhiteSpace($RawCert)) {\n        $RawCertLines = ($RawCert -Split \"`n\")\n        $currentLine = 0\n        while ($currentLine -lt $RawCertLines.Length) {\n            Write-Verbose \"Working on line $currentLine\"\n            $headerPosition = [Array]::FindIndex($RawCertLines, $currentLine, [Predicate[string]] { $args[0] -like $PemHeaderFragment })\n            if ($headerPosition -gt -1) {\n                $footerPosition = [Array]::FindIndex($RawCertLines, $headerPosition, [Predicate[string]] { $args[0] -like $PemFooterFragment })\n                if ($footerPosition -lt 0) {\n                    throw \"Unable to find a matching '-----END' PEM fragment!\"\n                }\n                else {\n                    Write-Verbose \"Selecting PEM lines: $headerPosition-$footerPosition\"\n                    $pemLines = $RawCertLines[$headerPosition..$footerPosition]\n                    $CleanedCertLines += $pemLines\n                    $currentLine = $footerPosition\n                }\n            }\n            else {\n                $currentLine++\n            }\n        }\n    }\n    if ($CleanedCertLines.Length -le 0) {\n        throw \"Something went wrong extracting contents from file (no cleansed contents)\"\n    }\n\n    $CleanedCert = $CleanedCertLines | Out-String\n    $CleanedCertData = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($CleanedCert))\n    \n    return $CleanedCertData    \n}\n###############################################################################\n# MAIN STEP TEMPLATE FLOW\n###############################################################################\n\n# TPP Access token\n$SecureToken = ConvertTo-SecureString $Token -AsPlainText -Force\n[PSCredential]$AccessToken = New-Object System.Management.Automation.PsCredential(\"token\", $SecureToken)\n\n# Clean-up\n$Server = $Server.TrimEnd('/')\n$OctopusServerUri = $OctopusServerUri.TrimEnd('/')\n$OctopusSpaceName = $OctopusSpaceName.Trim(\" \")\n$OctopusCertificateName = $OctopusCertificateName.Trim(\" \")\n\n# Required Venafi Module\nfunction Get-NugetPackageProviderNotInstalled {\n    # See if the nuget package provider has been installed\n    return ($null -eq (Get-PackageProvider -ListAvailable -Name Nuget -ErrorAction SilentlyContinue))\n}\n\n# Check to see if the package provider has been installed\nif ((Get-NugetPackageProviderNotInstalled) -ne $false) {\n    Write-Host \"Nuget package provider not found, installing ...\"    \n    Install-PackageProvider -Name Nuget -Force -Scope CurrentUser\n}\n\nWrite-Host \"Checking for required VenafiPS module ...\"\n$required_venafips_version = 3.1.5\n$module_available = Get-Module -ListAvailable -Name VenafiPS | Where-Object { $_.Version -ge $required_venafips_version }\nif (-not ($module_available)) {\n    Write-Host \"Installing VenafiPS module ...\"\n    Install-Module -Name VenafiPS -MinimumVersion 3.1.5 -Scope CurrentUser -Force\n}\nelse {\n    $first_match = $module_available | Select-Object -First 1 \n    Write-Host \"Found version: $($first_match.Version)\"\n}\n\nWrite-Host \"Importing VenafiPS module ...\"\nImport-Module VenafiPS\n\n$StepName = $OctopusParameters[\"Octopus.Step.Name\"]\n$ExportFormatsIncompatibleWithOctopusCertificateStore = @(\"Base64\", \"Base64 (PKCS #8)\")\n\nWrite-Verbose \"Venafi.TPP.ImportCert.Server: $Server\"\nWrite-Verbose \"Venafi.TPP.ImportCert.AccessToken: ********\"\nWrite-Verbose \"Venafi.TPP.ImportCert.DNPath: $Path\"\nWrite-Verbose \"Venafi.TPP.ImportCert.Format: $Format\"\nWrite-Verbose \"Venafi.TPP.ImportCert.IncludeChain: $IncludeChain\"\nWrite-Verbose \"Venafi.TPP.ImportCert.FriendlyName: $FriendlyName\"\nWrite-Verbose \"Venafi.TPP.ImportCert.IncludePrivateKey: $IncludePrivateKey\"\nWrite-Verbose \"Venafi.TPP.ImportCert.PrivateKeyPassword: ********\"\nWrite-Verbose \"Venafi.TPP.ImportCert.CertDetails.OutputVariableName: $OutputVariableName\"\nWrite-Verbose \"Venafi.TPP.ImportCert.RevokeTokenOnCompletion: $RevokeTokenOnCompletion\"\nWrite-Verbose \"Venafi.TPP.ImportCert.OctopusServerUri: $OctopusServerUri\"\nWrite-Verbose \"Venafi.TPP.ImportCert.OctopusApiKey: ********\"\nWrite-Verbose \"Venafi.TPP.ImportCert.OctopusSpaceName: $OctopusSpaceName\"\nWrite-Verbose \"Venafi.TPP.ImportCert.OctopusCertificateName: $OctopusCertificateName\"\nWrite-Verbose \"Venafi.TPP.ImportCert.OctopusReplaceExistingCertificate: $OctopusReplaceExistingCertificate\"\nWrite-Verbose \"Step Name: $StepName\"\n\nWrite-Host \"Requesting new session from $Server\"\nNew-VenafiSession -Server $Server -AccessToken $AccessToken\n\n# Export certificate\n$ExportCert_Params = @{\n    CertificateId = $Path;\n    Format        = $Format;\n}\n\n# Optional IncludeChain field\nif ($IncludeChain -eq $True) {\n    $ExportCert_Params.IncludeChain = $True\n}\n\n# Optional FriendlyName field\nif (-not [string]::IsNullOrWhiteSpace($FriendlyName)) {\n    $ExportCert_Params.FriendlyName = $FriendlyName\n}\n\n# Optional Private key field\nif (-not [string]::IsNullOrWhiteSpace($PrivateKeyPassword) -and $IncludePrivateKey -eq $True) {\n    $SecurePrivateKeyPassword = ConvertTo-SecureString $PrivateKeyPassword -AsPlainText -Force\n    $ExportCert_Params.PrivateKeyPassword = $SecurePrivateKeyPassword    \n    $ExportCert_Params.IncludePrivateKey = $True\n}\n\n# Do the export\n$ExportCertificateResponse = ((Export-VenafiCertificate @ExportCert_Params) 6> $null)\n\nif ($null -eq $ExportCertificateResponse -or $null -eq $ExportCertificateResponse.CertificateData) {\n    Write-Warning \"No certificate data returned for path: $Path`nCheck the path value represents a certificate, and not a folder.\"\n}\nelse {\n    Write-Host \"Successfully retrieved certificate data to export for path: $Path\"\n        \n    # Get octopus space Id\n    $header = @{ \"X-Octopus-ApiKey\" = $OctopusApiKey }\n    $spaces = Invoke-RestMethod -Uri \"$OctopusServerUri/api/spaces?partialName=$([uri]::EscapeDataString($OctopusSpaceName))&skip=0&take=500\" -Headers $header \n    $OctopusSpace = @($spaces.Items | Where-Object { $_.Name -eq $OctopusSpaceName }) | Select-Object -First 1\n\n    if ($null -eq $OctopusSpace) {\n        throw \"Couldnt find Octopus space with name '$OctopusSpaceName'.\"\n    }\n\n    # Check for certificate based on name\n    $CertificateMatches = @(Get-MatchingOctopusCertificates -ServerUri $OctopusServerUri -ApiKey $OctopusApiKey -SpaceId $($OctopusSpace.Id) -CertificateName $OctopusCertificateName) \n    Write-Host \"Found $($CertificateMatches.Length) certificates matching '$OctopusCertificateName'\"\n\n    $FirstCertificateMatch = $CertificateMatches | Select-Object -First 1\n    $CertificateData = $ExportCertificateResponse.CertificateData\n\n    if ($ExportFormatsIncompatibleWithOctopusCertificateStore -icontains $Format) {\n        Write-Host \"Requested export format $Format needs to be cleaned before import to Octopus.\"\n        $CertificateData = Clean-VenafiCertificateForOctopus -CertificateData $CertificateData\n        if ([string]::IsNullOrWhiteSpace($CertificateData)) {\n            throw \"Cleaned certificate data empty!\"\n        }\n    }\n\n    switch ($CertificateMatches.Length) {\n        0 {  \n            # New cert\n            Write-Host \"Creating a new certificate '$OctopusCertificateName'\"\n            New-OctopusCertificate -ServerUri $OctopusServerUri -ApiKey $OctopusApiKey -SpaceId $($OctopusSpace.Id) -CertificateName $OctopusCertificateName -CertificateData $($CertificateData) -CertificatePwd $PrivateKeyPassword\n        }\n        1 {  \n            # One cert to replace\n            if ($OctopusReplaceExistingCertificate -eq $False) {\n                Write-Host \"Replace existing certificate set to False, nothing to do.\"\n            }\n            else {\n                Write-Host \"Replacing existing certificate '$OctopusCertificateName' ($($FirstCertificateMatch.Id))\"\n                Replace-OctopusCertificate -ServerUri $OctopusServerUri -ApiKey $OctopusApiKey -SpaceId $($OctopusSpace.Id) -CertificateId $($FirstCertificateMatch.Id) -CertificateName $OctopusCertificateName -CertificateData $($CertificateData) -CertificatePwd $PrivateKeyPassword\n            }\n        }\n        default {\n            Write-Warning \"Multiple certs matching name '$OctopusCertificateName' found, nothing to do.\"\n            return\n        }\n    }\n\n    if ([string]::IsNullOrWhiteSpace($OutputVariableName) -eq $False) {\n        $CertificateJson = $ExportCertificateResponse | ConvertTo-Json -Compress -Depth 10 \n        Set-OctopusVariable -Name $OutputVariableName -Value $CertificateJson -Sensitive\n        Write-Highlight \"Created sensitive output variable: ##{Octopus.Action[$StepName].Output.$OutputVariableName}\"\n    }\n}\n\nif ($RevokeToken -eq $true) {\n    # Revoke TPP access token\n    Write-Host \"Revoking access token with $Server\"\n    Revoke-TppToken -AuthServer $Server -AccessToken $AccessToken -Force\n}"
  },
  "Category": "Venafi",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/venafi-tpp-import-certificate-into-octopus.json",
  "Website": "/step-templates/e10820c2-ae6d-4030-8a8a-b73ed60a81fc",
  "Logo": "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",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Monday, August 23, 2021