Skip to content

Commit 6674d6e

Browse files
authored
Merge pull request #1626 from OctopusDeploy/mh/bitwarden-secrets-manager
Add Bitwarden Secrets Manager - retrieve secrets steps
2 parents 4145373 + 7e85861 commit 6674d6e

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

gulpfile.babel.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ function humanize(categoryId) {
127127
return "Azure Site Extensions";
128128
case "azureFunctions":
129129
return "Azure Functions";
130+
case "bitwarden":
131+
return "Bitwarden";
130132
case "cassandra":
131133
return "Cassandra";
132134
case "chef":
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"Id": "740fb4a1-e863-4e81-ab54-ef28292334e4",
3+
"Name": "Bitwarden Secrets Manager - Retrieve Secrets",
4+
"Description": "This step retrieves one or more secrets from [Bitwarden Secrets Manager](https://bitwarden.com/products/secrets-manager/), and creates [sensitive output variables](https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables) for each value retrieved. These values can be used in other steps in your deployment or runbook process.\n\nYou can choose a custom output variable name for each secret, or one will be chosen for you.\n\n---\n\n**Required:** \n- PowerShell **5.1** or higher.\n- The Bitwarden Secrets Manager (`bws`) CLI installed on the target or worker. If the CLI can't be found, the step will fail.\n- A machine account [access token](https://bitwarden.com/help/access-tokens/) with permissions to retrieve secrets from the specified project.\n\nNotes:\n\n- Tested on Octopus **2025.4**.\n- Tested on both Windows Server 2022 and Ubuntu 24.04.",
5+
"ActionType": "Octopus.Script",
6+
"Version": 1,
7+
"CommunityActionTemplateId": null,
8+
"Packages": [],
9+
"GitDependencies": [],
10+
"Properties": {
11+
"Octopus.Action.Script.ScriptSource": "Inline",
12+
"Octopus.Action.Script.Syntax": "PowerShell",
13+
"Octopus.Action.Script.ScriptBody": "$ErrorActionPreference = 'Stop'\n\n# Variables\n$BwsServerUrl = $OctopusParameters[\"Bitwarden.SecretsManager.RetrieveSecrets.ServerUrl\"]\n$ProjectName = $OctopusParameters[\"Bitwarden.SecretsManager.RetrieveSecrets.ProjectName\"]\n$BwsAccessToken = $OctopusParameters[\"Bitwarden.SecretsManager.RetrieveSecrets.AccessToken\"]\n$SecretNames = $OctopusParameters[\"Bitwarden.SecretsManager.RetrieveSecrets.SecretNames\"]\n$PrintVariableNames = $OctopusParameters[\"Bitwarden.SecretsManager.RetrieveSecrets.PrintVariableNames\"]\n\nWrite-Output \"Verifying 'bws' command availability...\"\nif (-not (Get-Command bws -ErrorAction SilentlyContinue)) {\n throw \"The 'bws' (Bitwarden Secrets Manager CLI) command was not found. Please ensure it is installed and available in the system's PATH.\"\n}\nWrite-Output \"'bws' command found.\"\n\n# Validation\nif ([string]::IsNullOrWhiteSpace($ProjectName)) {\n throw \"Required parameter Bitwarden.SecretsManager.RetrieveSecrets.ProjectName not specified.\"\n}\nif ([string]::IsNullOrWhiteSpace($BwsServerUrl)) {\n throw \"Required parameter Bitwarden.SecretsManager.RetrieveSecrets.ServerURL not specified.\"\n}\nif ([string]::IsNullOrWhiteSpace($BwsAccessToken)) {\n throw \"Required parameter Bitwarden.SecretsManager.RetrieveSecrets.AccessToken not specified.\"\n}\nif ([string]::IsNullOrWhiteSpace($SecretNames)) {\n throw \"Required parameter Bitwarden.SecretsManager.RetrieveSecrets.SecretNames not specified.\"\n}\n\n# Functions\nfunction Save-OctopusVariable {\n Param(\n [string] $name,\n [string] $value\n )\n if ($script:storedVariables -icontains $name) {\n Write-Warning \"A variable with name '$name' has already been created. Check your secret name parameters as this will likely cause unexpected behavior and should be investigated.\"\n }\n Set-OctopusVariable -Name $name -Value $value -Sensitive\n $script:storedVariables += $name\n\n if ($PrintVariableNames -eq $True) {\n Write-Output \"Created output variable: ##{Octopus.Action[$StepName].Output.$name}\"\n }\n}\n\nfunction Get-BwsProjectIdByName {\n param(\n [Parameter(Mandatory = $true)]\n [string]$Name,\n [Parameter(Mandatory = $true)]\n [string]$AccessToken\n )\n \n # 1. API Call: Retrieve all projects in JSON format (1st API Call)\n $ProjectJson = bws project list `\n --access-token $AccessToken `\n --server-url $BwsServerUrl `\n --output json | Out-String\n\n # 2. Convert to PowerShell objects and filter by name\n $Projects = $ProjectJson | ConvertFrom-Json\n\n # 3. Find the ID of the matching project\n $ProjectObject = $Projects | Where-Object { $_.name -eq $Name }\n\n if (-not $ProjectObject) {\n throw \"Error: Project '$Name' not found.\"\n }\n\n # Handle the case where the project name might not be unique\n if ($ProjectObject.Count -gt 1) {\n Write-Warning \"Multiple projects found with name '$Name'. Using the first ID found.\"\n }\n\n # Return the ID\n return $ProjectObject.id\n}\n\n# End Functions\n\n$script:storedVariables = @()\n$StepName = $OctopusParameters[\"Octopus.Step.Name\"]\n$Secrets = @()\n\n# Extract secret names\n@(($SecretNames -Split \"`n\").Trim()) | ForEach-Object {\n if (![string]::IsNullOrWhiteSpace($_)) {\n Write-Verbose \"Working on: '$_'\"\n $secretDefinition = ($_ -Split \"\\|\")\n $secretName = $secretDefinition[0].Trim()\n \n if ([string]::IsNullOrWhiteSpace($secretName)) {\n throw \"Unable to establish secret name from: '$($_)'\"\n }\n $secret = [PsCustomObject]@{\n Name = $secretName\n VariableName = if ($secretDefinition.Count -gt 1 -and ![string]::IsNullOrWhiteSpace($secretDefinition[1])) { $secretDefinition[1].Trim() } else { $secretName } # If VariableName is blank, use SecretName\n }\n $Secrets += $secret\n }\n}\n\nWrite-Verbose \"Project Name: $ProjectName\"\nWrite-Verbose \"Secrets to retrieve: $($Secrets.Count)\"\nWrite-Verbose \"Print variables: $PrintVariableNames\"\n\ntry {\n\n # 1. Get the Project ID from the friendly name\n Write-Output \"Looking up project ID for '$ProjectName'\"\n $ProjectID = Get-BwsProjectIdByName -Name $ProjectName -AccessToken $BwsAccessToken\n \n Write-Output \"Project ID found: $ProjectID\"\n \n # 2. Retrieve all secrets from the found project (The single efficient call)\n Write-Output \"Fetching all secrets from project.\"\n $SecretNamesToQuery = @($Secrets | Select-Object -ExpandProperty Name)\n\n # Use the projectId to get all secrets in that project\n $SecretsJson = bws secret list $ProjectID `\n --access-token $BwsAccessToken `\n --server-url $BwsServerUrl `\n --output json | Out-String\n $AllSecrets = $SecretsJson | ConvertFrom-Json\n\n # 3. Filter the local objects to only include the desired secret names\n Write-Output \"Filtering for desired secrets: $($SecretNamesToQuery -join ', ').\"\n $FilteredSecrets = $AllSecrets | Where-Object { $_.key -in $SecretNamesToQuery } | Select-Object -Property key, value\n \n foreach ($secret in $FilteredSecrets) {\n # Find the VariableName associated with the secret key\n $variableName = ($Secrets | Where-Object { $_.Name -eq $secret.key }).VariableName \n \n # Save the secret value to the output variable\n Save-OctopusVariable -name $variableName -value $secret.value\n }\n}\ncatch {\n throw \"An error occurred while retrieving secrets: $($_.Exception.Message)\"\n}\n\nWrite-Output \"Created $($script:storedVariables.Count) output variables\""
14+
},
15+
"Parameters": [
16+
{
17+
"Id": "2a500677-eb3e-4e1c-93bd-0fa896aad9fd",
18+
"Name": "Bitwarden.SecretsManager.RetrieveSecrets.ServerUrl",
19+
"Label": "Server Url",
20+
"HelpText": "Provide the Server Url for retrieving secrets. Default: `https://vault.bitwarden.eu`",
21+
"DefaultValue": "https://vault.bitwarden.eu",
22+
"DisplaySettings": {
23+
"Octopus.ControlType": "SingleLineText"
24+
}
25+
},
26+
{
27+
"Id": "18b7321d-803e-4217-993d-6416dd6eb5f7",
28+
"Name": "Bitwarden.SecretsManager.RetrieveSecrets.ProjectName",
29+
"Label": "Project Name",
30+
"HelpText": "Provide the name of the project from which to retrieve secrets.",
31+
"DefaultValue": "",
32+
"DisplaySettings": {
33+
"Octopus.ControlType": "SingleLineText"
34+
}
35+
},
36+
{
37+
"Id": "0dada9ac-3a35-4215-b03f-9024486ee7a2",
38+
"Name": "Bitwarden.SecretsManager.RetrieveSecrets.AccessToken",
39+
"Label": "Machine Account Access Token",
40+
"HelpText": "Provide the machine account [access token](https://bitwarden.com/help/access-tokens/) used to authenticate to retrieve secrets.",
41+
"DefaultValue": "",
42+
"DisplaySettings": {
43+
"Octopus.ControlType": "Sensitive"
44+
}
45+
},
46+
{
47+
"Id": "b10f61d8-dedc-4a91-add3-451de0cfd47d",
48+
"Name": "Bitwarden.SecretsManager.RetrieveSecrets.SecretNames",
49+
"Label": "Secret names to retrieve",
50+
"HelpText": "Specify the names of the secrets to be returned from Secret Manager in Google Cloud, in the format:\n\n`SecretName | OutputVariableName` where:\n\n- `SecretName` is the name of the secret to retrieve.\n- `OutputVariableName` is the _optional_ Octopus [output variable](https://octopus.com/docs/projects/variables/output-variables) name to store the secret's value in. *If this value isn't specified, an output name will be generated dynamically*.\n\n**Note:** Multiple fields can be retrieved by entering each one on a new line.",
51+
"DefaultValue": "",
52+
"DisplaySettings": {
53+
"Octopus.ControlType": "MultiLineText"
54+
}
55+
},
56+
{
57+
"Id": "0a98e37e-7907-4e49-919a-5e50c7765469",
58+
"Name": "Bitwarden.SecretsManager.RetrieveSecrets.PrintVariableNames",
59+
"Label": "Print output variable names",
60+
"HelpText": "Write out the names of the Octopus [output variables](https://octopus.com/docs/projects/variables/output-variables) in the task log. Default: `False`.",
61+
"DefaultValue": "False",
62+
"DisplaySettings": {
63+
"Octopus.ControlType": "Checkbox"
64+
}
65+
}
66+
],
67+
"StepPackageId": "Octopus.Script",
68+
"$Meta": {
69+
"ExportedAt": "2025-10-09T16:22:38.417Z",
70+
"OctopusVersion": "2025.4.3435",
71+
"Type": "ActionTemplate"
72+
},
73+
"LastModifiedBy": "harrisonmeister",
74+
"Category": "bitwarden"
75+
}

step-templates/logos/bitwarden.png

10.3 KB
Loading

0 commit comments

Comments
 (0)