Octopus.Script exported 2021-08-16 by harrisonmeister belongs to ‘Venafi’ category.
This step template will authenticate against a Venafi TPP instance using an existing OAuth access token, and find a matching certificate based on the certificate subject common name. This is achieved using a combination of two functions from the VenafiPS PowerShell module:
- Find-TppCertificate function.
- Get-VenafiCertificate function.
If multiple certificate matches are found, additional (optional) search criteria can be provided to further filter the results:
- Certificate serial number
- Full Issuer Distinguished Name (DN)
- Expires before
After any filtering is complete, if multiple matches are found, only the first certificate will be returned, and a warning will be logged that multiple matches were found.
You can also store the entire certificate result in JSON
format in an Octopus output variable
This output variable can then be used in additional deployment or runbook steps.
On successful completion, you can also optionally revoke the access token used.
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.
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.FindCert.Server =
Required: The URL of the Venafi TPP instance you want to find a certificate from.
For example: https://mytppserver.example.com
.
Venafi TPP Access Token
Venafi.TPP.FindCert.AccessToken =
Required: The access token to authenticate against the TPP instance.
Certificate Subject Common Name
Venafi.TPP.FindCert.SubjectCN =
Required: Enter the certificate subject common name (CN) to search for.
Certificate serial number (Optional)
Venafi.TPP.FindCert.SerialNumber =
Optional: In case of multiple certificates matched on the subject common name (CN), provide the certificate serial number to filter to a single certificate match.
Certificate Issuer Distinguished Name (Optional)
Venafi.TPP.FindCert.Issuer =
Optional: In case of multiple certificates matched on the subject common name (CN), provide the full Issuer distinguished name (DN) to filter to a single certificate match.
For example: CN=Example Root CA, O=Venafi,Inc., L=Salt Lake City, S=Utah, C=US
Certificate Expires Before (Optional)
Venafi.TPP.FindCert.ExpireBefore =
Optional: In case of multiple certificates matched on the subject common name (CN), provide the expires before date in the format yyyy-MM-dd
e.g. 2021-08-15
Certificate output variable name (Optional)
Venafi.TPP.FindCert.CertDetails.OutputVariableName =
Optional: Create an output variable with the certificate details found from the search. The certificate details will be stored in JSON
format.
Revoke access token on completion?
Venafi.TPP.FindCert.RevokeTokenOnCompletion = False
Should the access token used be revoked once the step has been completed successfully? Default: False
.
Script body
Steps based on this template will execute the following PowerShell script.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ErrorActionPreference = 'Stop'
# Variables
$Server = $OctopusParameters["Venafi.TPP.FindCert.Server"]
$Token = $OctopusParameters["Venafi.TPP.FindCert.AccessToken"]
$SubjectCommonName = $OctopusParameters["Venafi.TPP.FindCert.SubjectCN"]
# Optional
$CertSerialNumber = $OctopusParameters["Venafi.TPP.FindCert.SerialNumber"]
$Issuer = $OctopusParameters["Venafi.TPP.FindCert.Issuer"]
$ExpireBefore = $OctopusParameters["Venafi.TPP.FindCert.ExpireBefore"]
$OutputVariableName = $OctopusParameters["Venafi.TPP.FindCert.CertDetails.OutputVariableName"]
$RevokeTokenOnCompletion = $OctopusParameters["Venafi.TPP.FindCert.RevokeTokenOnCompletion"]
# Validation
if ([string]::IsNullOrWhiteSpace($Server)) {
throw "Required parameter Venafi.TPP.FindCert.Server not specified"
}
if ([string]::IsNullOrWhiteSpace($Token)) {
throw "Required parameter Venafi.TPP.FindCert.AccessToken not specified"
}
if ([string]::IsNullOrWhiteSpace($SubjectCommonName)) {
throw "Required parameter Venafi.TPP.FindCert.SubjectCN not specified"
}
$SecureToken = ConvertTo-SecureString $Token -AsPlainText -Force
[PSCredential]$AccessToken = New-Object System.Management.Automation.PsCredential("token", $SecureToken)
# Clean-up
$Server = $Server.TrimEnd('/')
# Required Modules
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"]
Write-Verbose "Venafi.TPP.FindCert.Server: $Server"
Write-Verbose "Venafi.TPP.FindCert.AccessToken: ********"
Write-Verbose "Venafi.TPP.FindCert.SubjectCN: $SubjectCommonName"
Write-Verbose "Venafi.TPP.FindCert.SerialNumber: $CertSerialNumber"
Write-Verbose "Venafi.TPP.FindCert.Issuer: $Issuer"
Write-Verbose "Venafi.TPP.FindCert.ExpireBefore: $ExpireBefore"
Write-Verbose "Venafi.TPP.FindCert.CertDetails.OutputVariableName: $OutputVariableName"
Write-Verbose "Venafi.TPP.FindCert.RevokeTokenOnCompletion: $RevokeTokenOnCompletion"
Write-Verbose "Step Name: $StepName"
Write-Host "Requesting new session from $Server"
New-VenafiSession -Server $Server -AccessToken $AccessToken
$FindCert_Params = @{
First = 5;
CommonName = $SubjectCommonName;
}
# Optional SerialNumber field
if ([string]::IsNullOrWhiteSpace($CertSerialNumber) -eq $False) {
$FindCert_Params += @{ SerialNumber = $CertSerialNumber }
}
# Optional Issuer field
if ([string]::IsNullOrWhiteSpace($Issuer) -eq $False) {
# Issuer DN should be the complete DN enclosed in double quotes. e.g. "CN=Example Root CA, O=Venafi,Inc., L=Salt Lake City, S=Utah, C=US"
# If a value DN already contains double quotes, the string should be enclosed in a second set of double quotes.
if ($Issuer.StartsWith("`"") -or $Issuer.EndsWith("`"")) {
Write-Verbose "Removing double quotes from start and end of Issuer DN."
$Issuer = $Issuer.Trim("`"")
}
$FindCert_Params += @{ Issuer = "`"$Issuer`"" }
}
# Optional ExpireBefore field
if ([string]::IsNullOrWhiteSpace($ExpireBefore) -eq $False) {
$FindCert_Params += @{ ExpireBefore = $ExpireBefore }
}
Write-Host "Searching for certificates matching Subject CN: $SubjectCommonName."
$MatchingCertificates = @(Find-TppCertificate @FindCert_Params)
$MatchingCount = $MatchingCertificates.Length
if ($null -eq $MatchingCertificates -or $MatchingCount -eq 0) {
Write-Warning "No matching certificates found for Subject CN: $SubjectCommonName. Check any additional search criteria and try again."
}
else {
$MatchingCertificate = $MatchingCertificates | Select-Object -First 1
if ($MatchingCount -gt 1) {
Write-Warning "Multiple matching certificates found ($MatchingCount) for Subject CN: $SubjectCommonName, retrieving details for first match."
}
Write-Highlight "Retrieving certificate details for Subject CN: $SubjectCommonName ($($MatchingCertificate.Path))"
$Certificate = Get-VenafiCertificate -CertificateId $MatchingCertificate.Path
if ($null -eq $Certificate) {
Write-Warning "No certificate details returned for Subject CN: $SubjectCommonName ($($MatchingCertificate.Path))"
}
else {
Write-Host "Retrieved certificate details for Subject CN: $SubjectCommonName ($($MatchingCertificate.Path))"
$Certificate | Format-List
if ([string]::IsNullOrWhiteSpace($OutputVariableName) -eq $False) {
$CertificateJson = $Certificate | ConvertTo-Json -Compress -Depth 10
Set-OctopusVariable -Name $OutputVariableName -Value $CertificateJson
Write-Highlight "Created output variable: ##{Octopus.Action[$StepName].Output.$OutputVariableName}"
}
}
}
if ($RevokeTokenOnCompletion -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.
To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
{
"Id": "91ea41ae-5b12-4854-8994-90c06ba4b7f1",
"Name": "Venafi TPP - Find Certificate details",
"Description": "This step template will authenticate against a Venafi TPP instance using an existing OAuth access token, and find a matching certificate based on the certificate subject common name. This is achieved using a combination of two functions from the VenafiPS PowerShell module:\n\n1. [Find-TppCertificate](https://venafips.readthedocs.io/en/latest/functions/Find-TppCertificate/) function.\n2. [Get-VenafiCertificate](https://venafips.readthedocs.io/en/latest/functions/Get-VenafiCertificate/) function.\n\nIf multiple certificate matches are found, additional (optional) search criteria can be provided to further filter the results:\n\n- Certificate serial number\n- Full Issuer Distinguished Name (DN)\n- Expires before\n\nAfter any filtering is complete, if multiple matches are found, only the first certificate will be returned, and a warning will be logged that multiple matches were found.\n\nYou can also store the entire certificate result in `JSON` format in an [Octopus output variable](https://octopus.com/docs/projects/variables/output-variables)\n\nThis output variable can then be used in additional deployment or runbook steps.\n\nOn successful completion, you can also *optionally* revoke the access token used.\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\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": 1,
"ExportedAt": "2021-08-16T09:51:20.800Z",
"ActionType": "Octopus.Script",
"Author": "harrisonmeister",
"Packages": [],
"Parameters": [
{
"Id": "cf0b3c21-9249-4aa9-bf19-1fffdaa58939",
"Name": "Venafi.TPP.FindCert.Server",
"Label": "Venafi TPP Server",
"HelpText": "*Required*: The URL of the Venafi TPP instance you want to find a certificate from.\n\nFor example: `https://mytppserver.example.com`.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "f0d9bb77-7980-45f5-97c6-eefe6ccb3398",
"Name": "Venafi.TPP.FindCert.AccessToken",
"Label": "Venafi TPP Access Token",
"HelpText": "*Required*: The access token to authenticate against the TPP instance.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "Sensitive"
}
},
{
"Id": "0b7565f5-1ca1-4738-b13f-4b8a4ab99cf0",
"Name": "Venafi.TPP.FindCert.SubjectCN",
"Label": "Certificate Subject Common Name",
"HelpText": "*Required*: Enter the certificate subject common name (CN) to search for.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "44bb491d-1249-4893-a9fb-903daf2df6b4",
"Name": "Venafi.TPP.FindCert.SerialNumber",
"Label": "Certificate serial number (Optional)",
"HelpText": "*Optional*: In case of multiple certificates matched on the subject common name (CN), provide the certificate serial number to filter to a single certificate match.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "fcac21e8-34b6-48c6-b40f-2448c0c598a5",
"Name": "Venafi.TPP.FindCert.Issuer",
"Label": "Certificate Issuer Distinguished Name (Optional)",
"HelpText": "*Optional*: In case of multiple certificates matched on the subject common name (CN), provide the full Issuer distinguished name (DN) to filter to a single certificate match. \n\nFor example: `CN=Example Root CA, O=Venafi,Inc., L=Salt Lake City, S=Utah, C=US`",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "c15020cd-b722-49c2-89c2-a2a2349d41eb",
"Name": "Venafi.TPP.FindCert.ExpireBefore",
"Label": "Certificate Expires Before (Optional)",
"HelpText": "*Optional*: In case of multiple certificates matched on the subject common name (CN), provide the expires before date in the format `yyyy-MM-dd` e.g. `2021-08-15`",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "19f60dd0-887e-4379-94b3-b3227b3073be",
"Name": "Venafi.TPP.FindCert.CertDetails.OutputVariableName",
"Label": "Certificate output variable name (Optional)",
"HelpText": "*Optional*: Create an output variable with the certificate details found from the search. The certificate details will be stored in `JSON` format.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "74dbacbe-6192-45e6-8a10-e714966c4150",
"Name": "Venafi.TPP.FindCert.RevokeTokenOnCompletion",
"Label": "Revoke access token on completion?",
"HelpText": "Should the access token used be revoked once the step has been completed successfully? Default: `False`.",
"DefaultValue": "False",
"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# Variables\n$Server = $OctopusParameters[\"Venafi.TPP.FindCert.Server\"]\n$Token = $OctopusParameters[\"Venafi.TPP.FindCert.AccessToken\"]\n$SubjectCommonName = $OctopusParameters[\"Venafi.TPP.FindCert.SubjectCN\"]\n\n# Optional \n$CertSerialNumber = $OctopusParameters[\"Venafi.TPP.FindCert.SerialNumber\"]\n$Issuer = $OctopusParameters[\"Venafi.TPP.FindCert.Issuer\"]\n$ExpireBefore = $OctopusParameters[\"Venafi.TPP.FindCert.ExpireBefore\"]\n$OutputVariableName = $OctopusParameters[\"Venafi.TPP.FindCert.CertDetails.OutputVariableName\"]\n$RevokeTokenOnCompletion = $OctopusParameters[\"Venafi.TPP.FindCert.RevokeTokenOnCompletion\"]\n\n# Validation\nif ([string]::IsNullOrWhiteSpace($Server)) {\n throw \"Required parameter Venafi.TPP.FindCert.Server not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($Token)) {\n throw \"Required parameter Venafi.TPP.FindCert.AccessToken not specified\"\n}\nif ([string]::IsNullOrWhiteSpace($SubjectCommonName)) {\n throw \"Required parameter Venafi.TPP.FindCert.SubjectCN not specified\"\n}\n\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\n# Required Modules\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\nWrite-Verbose \"Venafi.TPP.FindCert.Server: $Server\"\nWrite-Verbose \"Venafi.TPP.FindCert.AccessToken: ********\"\nWrite-Verbose \"Venafi.TPP.FindCert.SubjectCN: $SubjectCommonName\"\nWrite-Verbose \"Venafi.TPP.FindCert.SerialNumber: $CertSerialNumber\"\nWrite-Verbose \"Venafi.TPP.FindCert.Issuer: $Issuer\"\nWrite-Verbose \"Venafi.TPP.FindCert.ExpireBefore: $ExpireBefore\"\nWrite-Verbose \"Venafi.TPP.FindCert.CertDetails.OutputVariableName: $OutputVariableName\"\nWrite-Verbose \"Venafi.TPP.FindCert.RevokeTokenOnCompletion: $RevokeTokenOnCompletion\"\nWrite-Verbose \"Step Name: $StepName\"\n\nWrite-Host \"Requesting new session from $Server\"\nNew-VenafiSession -Server $Server -AccessToken $AccessToken\n\n$FindCert_Params = @{\n First = 5;\n CommonName = $SubjectCommonName;\n}\n\n# Optional SerialNumber field\nif ([string]::IsNullOrWhiteSpace($CertSerialNumber) -eq $False) {\n $FindCert_Params += @{ SerialNumber = $CertSerialNumber }\n}\n# Optional Issuer field\nif ([string]::IsNullOrWhiteSpace($Issuer) -eq $False) {\n # Issuer DN should be the complete DN enclosed in double quotes. e.g. \"CN=Example Root CA, O=Venafi,Inc., L=Salt Lake City, S=Utah, C=US\"\n # If a value DN already contains double quotes, the string should be enclosed in a second set of double quotes. \n if ($Issuer.StartsWith(\"`\"\") -or $Issuer.EndsWith(\"`\"\")) {\n Write-Verbose \"Removing double quotes from start and end of Issuer DN.\"\n $Issuer = $Issuer.Trim(\"`\"\")\n }\n $FindCert_Params += @{ Issuer = \"`\"$Issuer`\"\" }\n}\n# Optional ExpireBefore field\nif ([string]::IsNullOrWhiteSpace($ExpireBefore) -eq $False) {\n $FindCert_Params += @{ ExpireBefore = $ExpireBefore }\n}\n\nWrite-Host \"Searching for certificates matching Subject CN: $SubjectCommonName.\"\n$MatchingCertificates = @(Find-TppCertificate @FindCert_Params)\n$MatchingCount = $MatchingCertificates.Length\nif ($null -eq $MatchingCertificates -or $MatchingCount -eq 0) {\n Write-Warning \"No matching certificates found for Subject CN: $SubjectCommonName. Check any additional search criteria and try again.\"\n}\nelse {\n $MatchingCertificate = $MatchingCertificates | Select-Object -First 1\n if ($MatchingCount -gt 1) {\n Write-Warning \"Multiple matching certificates found ($MatchingCount) for Subject CN: $SubjectCommonName, retrieving details for first match.\"\n }\n \n Write-Highlight \"Retrieving certificate details for Subject CN: $SubjectCommonName ($($MatchingCertificate.Path))\"\n $Certificate = Get-VenafiCertificate -CertificateId $MatchingCertificate.Path\n if ($null -eq $Certificate) {\n Write-Warning \"No certificate details returned for Subject CN: $SubjectCommonName ($($MatchingCertificate.Path))\"\n }\n else {\n Write-Host \"Retrieved certificate details for Subject CN: $SubjectCommonName ($($MatchingCertificate.Path))\"\n $Certificate | Format-List\n\n if ([string]::IsNullOrWhiteSpace($OutputVariableName) -eq $False) {\n $CertificateJson = $Certificate | ConvertTo-Json -Compress -Depth 10 \n Set-OctopusVariable -Name $OutputVariableName -Value $CertificateJson\n Write-Highlight \"Created output variable: ##{Octopus.Action[$StepName].Output.$OutputVariableName}\"\n }\n }\n}\n\nif ($RevokeTokenOnCompletion -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-find-certificate-details.json",
"Website": "/step-templates/91ea41ae-5b12-4854-8994-90c06ba4b7f1",
"Logo": "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",
"$Meta": {
"Type": "ActionTemplate"
}
}
Page updated on Monday, August 16, 2021