Venafi TPP - Import Certificate into Octopus

Octopus.Script exported 08/23/2021 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.
Show 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
}

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

Show JSON
{
  "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/venafi-tpp-import-certificate-into-octopus.json",
  "Website": "/step-templates/e10820c2-ae6d-4030-8a8a-b73ed60a81fc",
  "Logo": "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",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History »