From 92c63a0deff1c2ad0992dc03ef7ee91fb61e5c01 Mon Sep 17 00:00:00 2001 From: baraka-akeyless Date: Thu, 11 Jun 2026 11:26:07 +0300 Subject: [PATCH 1/3] Add Akeyless step templates for secrets management Introduce community step templates that authenticate to Akeyless and retrieve static, dynamic, and rotated secrets as sensitive output variables. Co-authored-by: Cursor --- gulpfile.babel.js | 2 + step-templates/akeyless-access-key-login.json | 54 ++++++++ step-templates/akeyless-aws-iam-login.json | 74 +++++++++++ step-templates/akeyless-jwt-login.json | 65 ++++++++++ .../akeyless-retrieve-dynamic-secret.json | 104 ++++++++++++++++ .../akeyless-retrieve-rotated-secret.json | 104 ++++++++++++++++ .../akeyless-retrieve-static-secrets.json | 115 ++++++++++++++++++ step-templates/logos/akeyless.png | Bin 0 -> 2410 bytes 8 files changed, 518 insertions(+) create mode 100644 step-templates/akeyless-access-key-login.json create mode 100644 step-templates/akeyless-aws-iam-login.json create mode 100644 step-templates/akeyless-jwt-login.json create mode 100644 step-templates/akeyless-retrieve-dynamic-secret.json create mode 100644 step-templates/akeyless-retrieve-rotated-secret.json create mode 100644 step-templates/akeyless-retrieve-static-secrets.json create mode 100644 step-templates/logos/akeyless.png diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 43e1a1c97..05c116f2d 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -158,6 +158,8 @@ function humanize(categoryId) { return "1Password Connect"; case "amazon-chime": return "Amazon Chime"; + case "akeyless": + return "Akeyless"; case "ansible": return "Ansible"; case "apexsql": diff --git a/step-templates/akeyless-access-key-login.json b/step-templates/akeyless-access-key-login.json new file mode 100644 index 000000000..add96758e --- /dev/null +++ b/step-templates/akeyless-access-key-login.json @@ -0,0 +1,54 @@ +{ + "Id": "f8e3a1b2-4c5d-6e7f-8a9b-0c1d2e3f4a01", + "Name": "Akeyless - Access Key Login", + "Description": "This step authenticates to [Akeyless](https://www.akeyless.io) using an Access ID and Access Key.\n\nThe API token from the response is stored as a sensitive [output variable](https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables) named AkeylessAuthToken for use in other step templates.\n\nThis step template uses the [Akeyless REST API](https://docs.akeyless.io/reference/auth), so no other dependencies are needed.\n\n---\n\n**Required:**\n- Akeyless Access ID\n- Akeyless Access Key\n\n**Optional:**\n- Gateway/API URL (default: https://api.akeyless.io)\n\n**Notes:**\n- Tested on PowerShell Desktop and PowerShell Core.\n- Pair this step with **Akeyless - Retrieve Static Secrets** or **Akeyless - Retrieve Dynamic Secret**.", + "ActionType": "Octopus.Script", + "Version": 1, + "CommunityActionTemplateId": null, + "Packages": [], + "Properties": { + "Octopus.Action.Script.ScriptSource": "Inline", + "Octopus.Action.Script.Syntax": "PowerShell", + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Auth.AccessKey.GatewayUrl']\n$ACCESS_ID = $OctopusParameters['Akeyless.Auth.AccessKey.AccessId']\n$ACCESS_KEY = $OctopusParameters['Akeyless.Auth.AccessKey.AccessKey']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_ID)) {\n throw 'Required parameter Access ID not specified'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_KEY)) {\n throw 'Required parameter Access Key not specified'\n}\n\n$body = @{\n 'access-id' = $ACCESS_ID\n 'access-key' = $ACCESS_KEY\n 'access-type' = 'access_key'\n}\n\nComplete-AkeylessLogin -GatewayUrl $GATEWAY_URL -AuthBody $body -StepName $StepName" + }, + "Parameters": [ + { + "Name": "Akeyless.Auth.AccessKey.GatewayUrl", + "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10001000-0000-0000-0000-100010001001", + "DefaultValue": "https://api.akeyless.io", + "Label": "Gateway URL" + }, + { + "Name": "Akeyless.Auth.AccessKey.AccessId", + "HelpText": "The Akeyless Access ID used for authentication.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10001000-0000-0000-0000-100010001002", + "DefaultValue": "", + "Label": "Access ID" + }, + { + "Name": "Akeyless.Auth.AccessKey.AccessKey", + "HelpText": "The Akeyless Access Key used for authentication.", + "DisplaySettings": { + "Octopus.ControlType": "Sensitive" + }, + "Id": "10001000-0000-0000-0000-100010001003", + "DefaultValue": "", + "Label": "Access Key" + } + ], + "LastModifiedBy": "akeyless-community", + "LastModifiedAt": "2026-06-11T08:07:13.165Z", + "$Meta": { + "ExportedAt": "2026-06-11T08:07:13.165Z", + "OctopusVersion": "2024.4.0", + "Type": "ActionTemplate" + }, + "Category": "akeyless" +} diff --git a/step-templates/akeyless-aws-iam-login.json b/step-templates/akeyless-aws-iam-login.json new file mode 100644 index 000000000..fc763e0ff --- /dev/null +++ b/step-templates/akeyless-aws-iam-login.json @@ -0,0 +1,74 @@ +{ + "Id": "f8e3a1b2-4c5d-6e7f-8a9b-0c1d2e3f4a05", + "Name": "Akeyless - AWS IAM Login", + "Description": "This step authenticates to [Akeyless](https://www.akeyless.io) using an AWS IAM auth method.\n\nThe API token from the response is stored as a sensitive [output variable](https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables) named AkeylessAuthToken for use in other step templates.\n\n---\n\n**Cloud ID**\n\nIf **Cloud ID** is left blank, the step builds it automatically from:\n\n- AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and optional AWS_SESSION_TOKEN, or\n- the EC2 instance profile when running on an AWS worker or deployment target\n\nYou can also supply a pre-generated Cloud ID from \u0007keyless get-cloud-identity --cloud-provider aws_iam.\n\n---\n\n**Required:**\n- Akeyless Access ID for an AWS IAM auth method\n- AWS credentials or a supplied Cloud ID\n\n**Optional:**\n- AWS region used for STS signing (default us-east-1)\n- Custom STS endpoint URL", + "ActionType": "Octopus.Script", + "Version": 1, + "CommunityActionTemplateId": null, + "Packages": [], + "Properties": { + "Octopus.Action.Script.ScriptSource": "Inline", + "Octopus.Action.Script.Syntax": "PowerShell", + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Auth.AwsIam.GatewayUrl']\n$ACCESS_ID = $OctopusParameters['Akeyless.Auth.AwsIam.AccessId']\n$CLOUD_ID = $OctopusParameters['Akeyless.Auth.AwsIam.CloudId']\n$AWS_REGION = $OctopusParameters['Akeyless.Auth.AwsIam.AwsRegion']\n$STS_URL = $OctopusParameters['Akeyless.Auth.AwsIam.StsUrl']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_ID)) {\n throw 'Required parameter Access ID not specified'\n}\nif ([string]::IsNullOrWhiteSpace($AWS_REGION)) {\n $AWS_REGION = 'us-east-1'\n}\nif ([string]::IsNullOrWhiteSpace($STS_URL)) {\n $STS_URL = 'https://sts.amazonaws.com/'\n}\n\n$cloudId = Resolve-AwsIamCloudId -CloudId $CLOUD_ID -Region $AWS_REGION.Trim() -StsUrl $STS_URL.Trim()\n\n$body = @{\n 'access-id' = $ACCESS_ID\n 'access-type' = 'aws_iam'\n 'cloud-id' = $cloudId\n}\n\nComplete-AkeylessLogin -GatewayUrl $GATEWAY_URL -AuthBody $body -StepName $StepName" + }, + "Parameters": [ + { + "Name": "Akeyless.Auth.AwsIam.GatewayUrl", + "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10005000-0000-0000-0000-100050001001", + "DefaultValue": "https://api.akeyless.io", + "Label": "Gateway URL" + }, + { + "Name": "Akeyless.Auth.AwsIam.AccessId", + "HelpText": "The Akeyless Access ID for the AWS IAM auth method.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10005000-0000-0000-0000-100050001002", + "DefaultValue": "", + "Label": "Access ID" + }, + { + "Name": "Akeyless.Auth.AwsIam.CloudId", + "HelpText": "Optional pre-generated AWS Cloud ID. Leave blank to derive credentials from the worker environment.", + "DisplaySettings": { + "Octopus.ControlType": "Sensitive" + }, + "Id": "10005000-0000-0000-0000-100050001003", + "DefaultValue": "", + "Label": "Cloud ID (optional)" + }, + { + "Name": "Akeyless.Auth.AwsIam.AwsRegion", + "HelpText": "Region used when signing the STS GetCallerIdentity request.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10005000-0000-0000-0000-100050001004", + "DefaultValue": "us-east-1", + "Label": "AWS region" + }, + { + "Name": "Akeyless.Auth.AwsIam.StsUrl", + "HelpText": "STS endpoint used to build the Cloud ID payload.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10005000-0000-0000-0000-100050001005", + "DefaultValue": "https://sts.amazonaws.com/", + "Label": "STS URL" + } + ], + "LastModifiedBy": "akeyless-community", + "LastModifiedAt": "2026-06-11T08:07:13.231Z", + "$Meta": { + "ExportedAt": "2026-06-11T08:07:13.231Z", + "OctopusVersion": "2024.4.0", + "Type": "ActionTemplate" + }, + "Category": "akeyless" +} diff --git a/step-templates/akeyless-jwt-login.json b/step-templates/akeyless-jwt-login.json new file mode 100644 index 000000000..665cc6a6c --- /dev/null +++ b/step-templates/akeyless-jwt-login.json @@ -0,0 +1,65 @@ +{ + "Id": "f8e3a1b2-4c5d-6e7f-8a9b-0c1d2e3f4a04", + "Name": "Akeyless - JWT Login", + "Description": "This step authenticates to [Akeyless](https://www.akeyless.io) using a JSON Web Token (JWT) or OIDC token.\n\nThe API token from the response is stored as a sensitive [output variable](https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables) named AkeylessAuthToken for use in other step templates.\n\n---\n\n**Required:**\n- Akeyless Access ID configured for JWT or OIDC authentication\n- JWT or OIDC bearer token\n\n**Optional:**\n- Access type (jwt or oidc, default jwt)\n- Gateway/API URL (default: https://api.akeyless.io)\n\n**Notes:**\n- Bind the JWT from an Octopus OIDC account, a prior script step, or a sensitive project variable.", + "ActionType": "Octopus.Script", + "Version": 1, + "CommunityActionTemplateId": null, + "Packages": [], + "Properties": { + "Octopus.Action.Script.ScriptSource": "Inline", + "Octopus.Action.Script.Syntax": "PowerShell", + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Auth.Jwt.GatewayUrl']\n$ACCESS_ID = $OctopusParameters['Akeyless.Auth.Jwt.AccessId']\n$JWT = $OctopusParameters['Akeyless.Auth.Jwt.Jwt']\n$ACCESS_TYPE = $OctopusParameters['Akeyless.Auth.Jwt.AccessType']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_ID)) {\n throw 'Required parameter Access ID not specified'\n}\nif ([string]::IsNullOrWhiteSpace($JWT)) {\n throw 'Required parameter JWT not specified'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_TYPE)) {\n $ACCESS_TYPE = 'jwt'\n}\n\n$body = @{\n 'access-id' = $ACCESS_ID\n 'access-type' = $ACCESS_TYPE.Trim()\n jwt = $JWT\n}\n\nComplete-AkeylessLogin -GatewayUrl $GATEWAY_URL -AuthBody $body -StepName $StepName" + }, + "Parameters": [ + { + "Name": "Akeyless.Auth.Jwt.GatewayUrl", + "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10004000-0000-0000-0000-100040001001", + "DefaultValue": "https://api.akeyless.io", + "Label": "Gateway URL" + }, + { + "Name": "Akeyless.Auth.Jwt.AccessId", + "HelpText": "The Akeyless Access ID for the JWT/OIDC auth method.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10004000-0000-0000-0000-100040001002", + "DefaultValue": "", + "Label": "Access ID" + }, + { + "Name": "Akeyless.Auth.Jwt.Jwt", + "HelpText": "Bearer token used for authentication.", + "DisplaySettings": { + "Octopus.ControlType": "Sensitive" + }, + "Id": "10004000-0000-0000-0000-100040001003", + "DefaultValue": "", + "Label": "JWT / OIDC token" + }, + { + "Name": "Akeyless.Auth.Jwt.AccessType", + "HelpText": "Set to `oidc` when your Akeyless auth method requires OIDC instead of JWT.", + "DisplaySettings": { + "Octopus.SelectOptions": "jwt|JWT\noidc|OIDC", + "Octopus.ControlType": "Select" + }, + "Id": "10004000-0000-0000-0000-100040001004", + "DefaultValue": "jwt", + "Label": "Access type" + } + ], + "LastModifiedBy": "akeyless-community", + "LastModifiedAt": "2026-06-11T08:07:13.228Z", + "$Meta": { + "ExportedAt": "2026-06-11T08:07:13.228Z", + "OctopusVersion": "2024.4.0", + "Type": "ActionTemplate" + }, + "Category": "akeyless" +} diff --git a/step-templates/akeyless-retrieve-dynamic-secret.json b/step-templates/akeyless-retrieve-dynamic-secret.json new file mode 100644 index 000000000..dc5dfaacf --- /dev/null +++ b/step-templates/akeyless-retrieve-dynamic-secret.json @@ -0,0 +1,104 @@ +{ + "Id": "f8e3a1b2-4c5d-6e7f-8a9b-0c1d2e3f4a03", + "Name": "Akeyless - Retrieve Dynamic Secret", + "Description": "This step retrieves a **dynamic secret** from Akeyless and creates sensitive [output variables](https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables) for use in later deployment or runbook steps.\n\nThis step template uses the [Akeyless REST API](https://docs.akeyless.io/reference/getdynamicsecretvalue), so no other dependencies are needed.\n\n---\n\n**Authentication token**\n\nUse the **Akeyless - Access Key Login** step template first, then bind the Auth Token parameter to:\n\n#{Octopus.Action[].Output.AkeylessAuthToken}\n\n---\n\n**Dynamic secret arguments**\n\nOptional provisioning arguments can be supplied one per line.\n\n---\n\n**Required:**\n- Authentication token\n- Dynamic secret path", + "ActionType": "Octopus.Script", + "Version": 1, + "CommunityActionTemplateId": null, + "Packages": [], + "Properties": { + "Octopus.Action.Script.ScriptSource": "Inline", + "Octopus.Action.Script.Syntax": "PowerShell", + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Retrieve.Dynamic.GatewayUrl']\n$AUTH_TOKEN = $OctopusParameters['Akeyless.Retrieve.Dynamic.AuthToken']\n$SECRET_PATH = $OctopusParameters['Akeyless.Retrieve.Dynamic.SecretPath']\n$OUTPUT_VARIABLE_NAME = $OctopusParameters['Akeyless.Retrieve.Dynamic.OutputVariableName']\n$TIMEOUT_TEXT = $OctopusParameters['Akeyless.Retrieve.Dynamic.Timeout']\n$DYNAMIC_ARGS = $OctopusParameters['Akeyless.Retrieve.Dynamic.DynamicSecretArgs']\n$FIELD_VALUES = $OctopusParameters['Akeyless.Retrieve.Dynamic.FieldValues']\n$PRINT_VARIABLE_NAMES = $OctopusParameters['Akeyless.Retrieve.Dynamic.PrintVariableNames']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($AUTH_TOKEN)) {\n throw 'Required parameter Auth Token not specified'\n}\nif ([string]::IsNullOrWhiteSpace($SECRET_PATH)) {\n throw 'Required parameter Secret Path not specified'\n}\nif ([string]::IsNullOrWhiteSpace($PRINT_VARIABLE_NAMES)) {\n $PRINT_VARIABLE_NAMES = 'False'\n}\n\n$printNames = ($PRINT_VARIABLE_NAMES -eq 'True')\n$path = Normalize-AkeylessPath $SECRET_PATH\n$body = @{\n token = $AUTH_TOKEN\n name = $path\n}\n\nif (-not [string]::IsNullOrWhiteSpace($TIMEOUT_TEXT)) {\n $timeout = 0\n if (-not [int]::TryParse($TIMEOUT_TEXT, [ref]$timeout)) {\n throw \"Timeout must be an integer, got '$TIMEOUT_TEXT'\"\n }\n if ($timeout -gt 0) {\n $body.timeout = $timeout\n }\n}\n\n$argsList = @()\nif (-not [string]::IsNullOrWhiteSpace($DYNAMIC_ARGS)) {\n $argsList = @(($DYNAMIC_ARGS -Split \"`n\").Trim() | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })\n if ($argsList.Count -gt 0) {\n $body.args = $argsList\n }\n}\n\n$response = Invoke-AkeylessApi -GatewayUrl $GATEWAY_URL -Path 'get-dynamic-secret-value' -Body $body\nif ($null -eq $response) {\n throw \"Empty response retrieving dynamic secret '$path'\"\n}\n\n$fields = Parse-AkeylessFieldDefinitions -RawValue $FIELD_VALUES\n$created = Publish-AkeylessStructuredSecretResponse -Response $response -SecretPath $path -StepName $StepName -PrintVariableNames $printNames -OutputVariableName $OUTPUT_VARIABLE_NAME -Fields $fields\nWrite-Host \"Created $created output variables\"" + }, + "Parameters": [ + { + "Name": "Akeyless.Retrieve.Dynamic.GatewayUrl", + "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10003000-0000-0000-0000-100030001001", + "DefaultValue": "https://api.akeyless.io", + "Label": "Gateway URL" + }, + { + "Name": "Akeyless.Retrieve.Dynamic.AuthToken", + "HelpText": "Authentication token from a previous Akeyless login step.", + "DisplaySettings": { + "Octopus.ControlType": "Sensitive" + }, + "Id": "10003000-0000-0000-0000-100030001002", + "DefaultValue": "", + "Label": "Auth Token" + }, + { + "Name": "Akeyless.Retrieve.Dynamic.SecretPath", + "HelpText": "Full path to the dynamic secret, e.g. `/production/database/dynamic`", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10003000-0000-0000-0000-100030001003", + "DefaultValue": "", + "Label": "Secret path" + }, + { + "Name": "Akeyless.Retrieve.Dynamic.OutputVariableName", + "HelpText": "Optional output variable name when the dynamic secret returns a single value.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10003000-0000-0000-0000-100030001004", + "DefaultValue": "", + "Label": "Output variable name" + }, + { + "Name": "Akeyless.Retrieve.Dynamic.Timeout", + "HelpText": "Optional timeout for dynamic secret provisioning.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10003000-0000-0000-0000-100030001005", + "DefaultValue": "", + "Label": "Timeout (seconds)" + }, + { + "Name": "Akeyless.Retrieve.Dynamic.DynamicSecretArgs", + "HelpText": "Optional provisioning arguments, one per line.", + "DisplaySettings": { + "Octopus.ControlType": "MultiLineText" + }, + "Id": "10003000-0000-0000-0000-100030001006", + "DefaultValue": "", + "Label": "Dynamic secret args" + }, + { + "Name": "Akeyless.Retrieve.Dynamic.FieldValues", + "HelpText": "Choose specific response fields in the format FieldName | OutputVariableName.", + "DisplaySettings": { + "Octopus.ControlType": "MultiLineText" + }, + "Id": "10003000-0000-0000-0000-100030001007", + "DefaultValue": "", + "Label": "Field names" + }, + { + "Name": "Akeyless.Retrieve.Dynamic.PrintVariableNames", + "HelpText": "Write created output variable names to the task log.", + "DisplaySettings": { + "Octopus.ControlType": "Checkbox" + }, + "Id": "10003000-0000-0000-0000-100030001008", + "DefaultValue": "False", + "Label": "Print output variable names" + } + ], + "LastModifiedBy": "akeyless-community", + "LastModifiedAt": "2026-06-11T08:07:13.225Z", + "$Meta": { + "ExportedAt": "2026-06-11T08:07:13.225Z", + "OctopusVersion": "2024.4.0", + "Type": "ActionTemplate" + }, + "Category": "akeyless" +} diff --git a/step-templates/akeyless-retrieve-rotated-secret.json b/step-templates/akeyless-retrieve-rotated-secret.json new file mode 100644 index 000000000..6fb75ab45 --- /dev/null +++ b/step-templates/akeyless-retrieve-rotated-secret.json @@ -0,0 +1,104 @@ +{ + "Id": "f8e3a1b2-4c5d-6e7f-8a9b-0c1d2e3f4a06", + "Name": "Akeyless - Retrieve Rotated Secret", + "Description": "This step retrieves a **rotated secret** from Akeyless and creates sensitive [output variables](https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables) for use in later deployment or runbook steps.\n\nThis step template uses the [Akeyless REST API](https://docs.akeyless.io/reference/getrotatedsecretvalue), so no other dependencies are needed.\n\n---\n\n**Authentication token**\n\nUse any Akeyless login step template first, then bind the Auth Token parameter to:\n\n#{Octopus.Action[].Output.AkeylessAuthToken}\n\n---\n\n**Linked targets**\n\nFor rotated secrets associated with linked targets, set **Host** to the target host name.\n\n---\n\n**Required:**\n- Authentication token\n- Rotated secret path", + "ActionType": "Octopus.Script", + "Version": 1, + "CommunityActionTemplateId": null, + "Packages": [], + "Properties": { + "Octopus.Action.Script.ScriptSource": "Inline", + "Octopus.Action.Script.Syntax": "PowerShell", + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Retrieve.Rotated.GatewayUrl']\n$AUTH_TOKEN = $OctopusParameters['Akeyless.Retrieve.Rotated.AuthToken']\n$SECRET_PATH = $OctopusParameters['Akeyless.Retrieve.Rotated.SecretPath']\n$OUTPUT_VARIABLE_NAME = $OctopusParameters['Akeyless.Retrieve.Rotated.OutputVariableName']\n$HOST_NAME = $OctopusParameters['Akeyless.Retrieve.Rotated.Host']\n$VERSION_TEXT = $OctopusParameters['Akeyless.Retrieve.Rotated.Version']\n$FIELD_VALUES = $OctopusParameters['Akeyless.Retrieve.Rotated.FieldValues']\n$PRINT_VARIABLE_NAMES = $OctopusParameters['Akeyless.Retrieve.Rotated.PrintVariableNames']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($AUTH_TOKEN)) {\n throw 'Required parameter Auth Token not specified'\n}\nif ([string]::IsNullOrWhiteSpace($SECRET_PATH)) {\n throw 'Required parameter Secret Path not specified'\n}\nif ([string]::IsNullOrWhiteSpace($PRINT_VARIABLE_NAMES)) {\n $PRINT_VARIABLE_NAMES = 'False'\n}\n\n$printNames = ($PRINT_VARIABLE_NAMES -eq 'True')\n$path = Normalize-AkeylessPath $SECRET_PATH\n$body = @{\n token = $AUTH_TOKEN\n names = $path\n}\n\nif (-not [string]::IsNullOrWhiteSpace($HOST_NAME)) {\n $body.host = $HOST_NAME.Trim()\n}\n\nif (-not [string]::IsNullOrWhiteSpace($VERSION_TEXT)) {\n $version = 0\n if (-not [int]::TryParse($VERSION_TEXT, [ref]$version)) {\n throw \"Version must be an integer, got '$VERSION_TEXT'\"\n }\n if ($version -gt 0) {\n $body.version = $version\n }\n}\n\n$response = Invoke-AkeylessApi -GatewayUrl $GATEWAY_URL -Path 'get-rotated-secret-value' -Body $body\nif ($null -eq $response) {\n throw \"Empty response retrieving rotated secret '$path'\"\n}\n\n$fields = Parse-AkeylessFieldDefinitions -RawValue $FIELD_VALUES\n$created = Publish-AkeylessStructuredSecretResponse -Response $response -SecretPath $path -StepName $StepName -PrintVariableNames $printNames -OutputVariableName $OUTPUT_VARIABLE_NAME -Fields $fields\nWrite-Host \"Created $created output variables\"" + }, + "Parameters": [ + { + "Name": "Akeyless.Retrieve.Rotated.GatewayUrl", + "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10006000-0000-0000-0000-100060001001", + "DefaultValue": "https://api.akeyless.io", + "Label": "Gateway URL" + }, + { + "Name": "Akeyless.Retrieve.Rotated.AuthToken", + "HelpText": "Authentication token from a previous Akeyless login step.", + "DisplaySettings": { + "Octopus.ControlType": "Sensitive" + }, + "Id": "10006000-0000-0000-0000-100060001002", + "DefaultValue": "", + "Label": "Auth Token" + }, + { + "Name": "Akeyless.Retrieve.Rotated.SecretPath", + "HelpText": "Full path to the rotated secret, e.g. `/production/database/rotated`", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10006000-0000-0000-0000-100060001003", + "DefaultValue": "", + "Label": "Secret path" + }, + { + "Name": "Akeyless.Retrieve.Rotated.OutputVariableName", + "HelpText": "Optional output variable name when the rotated secret returns a single value.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10006000-0000-0000-0000-100060001004", + "DefaultValue": "", + "Label": "Output variable name" + }, + { + "Name": "Akeyless.Retrieve.Rotated.Host", + "HelpText": "Optional linked-target host name.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10006000-0000-0000-0000-100060001005", + "DefaultValue": "", + "Label": "Host" + }, + { + "Name": "Akeyless.Retrieve.Rotated.Version", + "HelpText": "Optional rotated secret version.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10006000-0000-0000-0000-100060001006", + "DefaultValue": "", + "Label": "Secret version" + }, + { + "Name": "Akeyless.Retrieve.Rotated.FieldValues", + "HelpText": "Choose specific response fields in the format FieldName | OutputVariableName.", + "DisplaySettings": { + "Octopus.ControlType": "MultiLineText" + }, + "Id": "10006000-0000-0000-0000-100060001007", + "DefaultValue": "", + "Label": "Field names" + }, + { + "Name": "Akeyless.Retrieve.Rotated.PrintVariableNames", + "HelpText": "Write created output variable names to the task log.", + "DisplaySettings": { + "Octopus.ControlType": "Checkbox" + }, + "Id": "10006000-0000-0000-0000-100060001008", + "DefaultValue": "False", + "Label": "Print output variable names" + } + ], + "LastModifiedBy": "akeyless-community", + "LastModifiedAt": "2026-06-11T08:07:13.241Z", + "$Meta": { + "ExportedAt": "2026-06-11T08:07:13.241Z", + "OctopusVersion": "2024.4.0", + "Type": "ActionTemplate" + }, + "Category": "akeyless" +} diff --git a/step-templates/akeyless-retrieve-static-secrets.json b/step-templates/akeyless-retrieve-static-secrets.json new file mode 100644 index 000000000..39d82d6c2 --- /dev/null +++ b/step-templates/akeyless-retrieve-static-secrets.json @@ -0,0 +1,115 @@ +{ + "Id": "f8e3a1b2-4c5d-6e7f-8a9b-0c1d2e3f4a02", + "Name": "Akeyless - Retrieve Static Secrets", + "Description": "This step retrieves one or more **static secrets** from Akeyless and creates sensitive [output variables](https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables) for use in later deployment or runbook steps.\n\nThis step template uses the [Akeyless REST API](https://docs.akeyless.io/reference/getsecretvalue), so no other dependencies are needed.\n\n---\n\n**Authentication token**\n\nUse the **Akeyless - Access Key Login** step template first, then bind the Auth Token parameter to:\n\n#{Octopus.Action[].Output.AkeylessAuthToken}\n\n---\n\n**Secret paths**\n\nSpecify secrets one per line in the format:\n\nSecretPath | OutputVariableName\n\nExamples:\n\n/production/database/password\n/production/database/password | DatabasePassword\n\nIf OutputVariableName is omitted, the variable name is derived from the secret path.\n\n---\n\n**Folder retrieval**\n\nChoose retrieval method **Folder** to list static secrets under an Akeyless folder. Enable **Recursive retrieval** to include subfolders.\n\n---\n\n**JSON secrets**\n\nFor generic JSON secrets, optionally choose specific fields in the format:\n\nFieldName | OutputVariableName\n\nIf field names are omitted, all top-level JSON fields are exported as separate output variables.\n\n---\n\n**Required:**\n- Authentication token\n- Secret paths, or a folder path when using folder retrieval", + "ActionType": "Octopus.Script", + "Version": 1, + "CommunityActionTemplateId": null, + "Packages": [], + "Properties": { + "Octopus.Action.Script.ScriptSource": "Inline", + "Octopus.Action.Script.Syntax": "PowerShell", + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Retrieve.Static.GatewayUrl']\n$AUTH_TOKEN = $OctopusParameters['Akeyless.Retrieve.Static.AuthToken']\n$SECRET_DEFINITIONS = $OctopusParameters['Akeyless.Retrieve.Static.Secrets']\n$FOLDER_PATH = $OctopusParameters['Akeyless.Retrieve.Static.FolderPath']\n$RETRIEVAL_METHOD = $OctopusParameters['Akeyless.Retrieve.Static.RetrievalMethod']\n$RECURSIVE_SEARCH = $OctopusParameters['Akeyless.Retrieve.Static.RecursiveSearch']\n$FIELD_VALUES = $OctopusParameters['Akeyless.Retrieve.Static.FieldValues']\n$PRINT_VARIABLE_NAMES = $OctopusParameters['Akeyless.Retrieve.Static.PrintVariableNames']\n$VERSION_TEXT = $OctopusParameters['Akeyless.Retrieve.Static.Version']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($AUTH_TOKEN)) {\n throw 'Required parameter Auth Token not specified'\n}\nif ([string]::IsNullOrWhiteSpace($RETRIEVAL_METHOD)) {\n $RETRIEVAL_METHOD = 'Single'\n}\nif ([string]::IsNullOrWhiteSpace($RECURSIVE_SEARCH)) {\n $RECURSIVE_SEARCH = 'False'\n}\nif ([string]::IsNullOrWhiteSpace($PRINT_VARIABLE_NAMES)) {\n $PRINT_VARIABLE_NAMES = 'False'\n}\n\n$version = 0\nif (-not [string]::IsNullOrWhiteSpace($VERSION_TEXT)) {\n if (-not [int]::TryParse($VERSION_TEXT, [ref]$version)) {\n throw \"Version must be a positive integer, got '$VERSION_TEXT'\"\n }\n}\n\n$printNames = ($PRINT_VARIABLE_NAMES -eq 'True')\n$recursive = ($RECURSIVE_SEARCH -eq 'True')\n$fields = Parse-AkeylessFieldDefinitions -RawValue $FIELD_VALUES\n$created = 0\n\nif ($RETRIEVAL_METHOD.ToUpper().Trim() -eq 'FOLDER') {\n if ([string]::IsNullOrWhiteSpace($FOLDER_PATH)) {\n throw 'Folder path is required when retrieval method is Folder'\n }\n\n $secretPaths = Get-AkeylessSecretsRecursively -GatewayUrl $GATEWAY_URL -Token $AUTH_TOKEN -FolderPath $FOLDER_PATH -Recursive $recursive\n foreach ($secretPath in $secretPaths) {\n $created += Publish-AkeylessSecretValue -GatewayUrl $GATEWAY_URL -Token $AUTH_TOKEN -SecretPath $secretPath -StepName $StepName -PrintVariableNames $printNames -Fields $fields -Version $version\n }\n}\nelse {\n $definitions = Parse-AkeylessSecretDefinitions -RawValue $SECRET_DEFINITIONS\n if ($definitions.Count -eq 0) {\n throw 'At least one secret path is required when retrieval method is Single or Multiple'\n }\n\n foreach ($definition in $definitions) {\n $created += Publish-AkeylessSecretValue -GatewayUrl $GATEWAY_URL -Token $AUTH_TOKEN -SecretPath $definition.Path -StepName $StepName -PrintVariableNames $printNames -OutputVariableName $definition.OutputVariableName -Fields $fields -Version $version\n }\n}\n\nWrite-Host \"Created $created output variables\"" + }, + "Parameters": [ + { + "Name": "Akeyless.Retrieve.Static.GatewayUrl", + "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10002000-0000-0000-0000-100020001001", + "DefaultValue": "https://api.akeyless.io", + "Label": "Gateway URL" + }, + { + "Name": "Akeyless.Retrieve.Static.AuthToken", + "HelpText": "Authentication token from a previous Akeyless login step.", + "DisplaySettings": { + "Octopus.ControlType": "Sensitive" + }, + "Id": "10002000-0000-0000-0000-100020001002", + "DefaultValue": "", + "Label": "Auth Token" + }, + { + "Name": "Akeyless.Retrieve.Static.Secrets", + "HelpText": "One secret per line in the format SecretPath | OutputVariableName.", + "DisplaySettings": { + "Octopus.ControlType": "MultiLineText" + }, + "Id": "10002000-0000-0000-0000-100020001003", + "DefaultValue": "", + "Label": "Secret paths" + }, + { + "Name": "Akeyless.Retrieve.Static.FolderPath", + "HelpText": "Akeyless folder to enumerate when retrieval method is Folder, e.g. `/production`", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10002000-0000-0000-0000-100020001004", + "DefaultValue": "", + "Label": "Folder path" + }, + { + "Name": "Akeyless.Retrieve.Static.RetrievalMethod", + "HelpText": "Retrieve explicit secret paths, or enumerate static secrets in a folder.", + "DisplaySettings": { + "Octopus.SelectOptions": "Single|Explicit secret paths\nFolder|Enumerate folder", + "Octopus.ControlType": "Select" + }, + "Id": "10002000-0000-0000-0000-100020001005", + "DefaultValue": "Single", + "Label": "Retrieval method" + }, + { + "Name": "Akeyless.Retrieve.Static.RecursiveSearch", + "HelpText": "When enumerating a folder, also retrieve secrets from subfolders.", + "DisplaySettings": { + "Octopus.ControlType": "Checkbox" + }, + "Id": "10002000-0000-0000-0000-100020001006", + "DefaultValue": "False", + "Label": "Recursive retrieval" + }, + { + "Name": "Akeyless.Retrieve.Static.FieldValues", + "HelpText": "For JSON secrets, choose fields in the format FieldName | OutputVariableName.", + "DisplaySettings": { + "Octopus.ControlType": "MultiLineText" + }, + "Id": "10002000-0000-0000-0000-100020001007", + "DefaultValue": "", + "Label": "Field names" + }, + { + "Name": "Akeyless.Retrieve.Static.Version", + "HelpText": "Optional static secret version to retrieve. Leave blank for latest.", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + }, + "Id": "10002000-0000-0000-0000-100020001008", + "DefaultValue": "", + "Label": "Secret version" + }, + { + "Name": "Akeyless.Retrieve.Static.PrintVariableNames", + "HelpText": "Write created output variable names to the task log.", + "DisplaySettings": { + "Octopus.ControlType": "Checkbox" + }, + "Id": "10002000-0000-0000-0000-100020001009", + "DefaultValue": "False", + "Label": "Print output variable names" + } + ], + "LastModifiedBy": "akeyless-community", + "LastModifiedAt": "2026-06-11T08:07:13.221Z", + "$Meta": { + "ExportedAt": "2026-06-11T08:07:13.221Z", + "OctopusVersion": "2024.4.0", + "Type": "ActionTemplate" + }, + "Category": "akeyless" +} diff --git a/step-templates/logos/akeyless.png b/step-templates/logos/akeyless.png new file mode 100644 index 0000000000000000000000000000000000000000..49192870ff0337928a0166a964c174a6c377f5a8 GIT binary patch literal 2410 zcmeAS@N?(olHy`uVBq!ia0vp^r$Cs44M;voy}trTF%}28J29*~C-V}>u}F>ZO!M_+ z&;qhK7#Q0#8CXC{fLIEM85o!rFu_GmEnr5lL6TzmcQ}CB9(cMqhEy=VofDfel~v~0 z|L1Q`7hOI$X=3Ev62S-A^9CJ*5yLRws0+PhD- z^jzUl?b;)tq*}82WyrIbr#A0O?loJ#E1i9MPQTsp`-~s=ygzsD^Pc_f^6z%cP5smw zEs|IL?OpHI?E9U$_w)O=ZQJ(bisk)Rud?r(rVb4y(+*ebI|JB$rGELWN^ieu> zY`yu>&-!~LVq;^SuN5C``uXvSuy)&dKk z&v!eoSnqsbnnUWRr(x&!tGF+}BP2UH@74FDw)ekGU+w?7@ci*zt5-idu%`Oala5=D zj<1U5JF>#xT)Vq}D*0==1+B}L%1v8;;@H0B@}0@T z$CjP@US9CZ{_3ya0?!o^oD&iPckO;W%lv-7;O%<9SRcFDy?_1B^(Rlx_nZ5if$6Ew zg8D$NNj-J14@>NPdi=`P=bL9{#2M;a*YOaIV6rYD*Jw*2XulPEvfAPQD zvMaOCryE_|RN^Ny*G#c=9y3P_AB$FXe9GfpUkk6Q$Gd(#czCbB`=_%aWQ zE6SD)4wXV4ok1V3+fV#s`T3gZGQ(TT?^iv`>{BebDd182()2>;^*8yw$4ck(en~v% zyxXF5dXOzP7KkPswqJb2R?rShDX+*4)~BnVs2D9}9V<&5o})^m^*O?Z^1d zT-NRD(?4A0{P16@qrz8xuXPLUcD2Vx{wdz@@|}fAa+N&C;_!Nrt=5*_mq<>x(i(bn@%*%-@#iKhC_ z_Po1cd-`_C?spN1Yvw6A-TOv6)VDZ0=J%~x8Ifk{pF*bwey;X@d0CjVomV={>KPwP z3}?!=oV(NF_dNN?eX0KAqSlQyH!pF2x?`isGKFQ+$$W3mI>93W_tpGoNqCFtOTAkD z^ru2WAcsgjxA&w!KgFe$D*LNHeY^X4r)qrO*?Ct(rYu>t>Q&9WuJf%2RGk;7c=<1W zV*Bpo?ehQUt%WCxzXK+0pNHuY;GVVV8@C#$&Ex6n=d#Wzp$P!b85x!U literal 0 HcmV?d00001 From 294f970dc91ff66254d5d3a02d91f70651d7b1d7 Mon Sep 17 00:00:00 2001 From: baraka-akeyless Date: Thu, 11 Jun 2026 11:35:31 +0300 Subject: [PATCH 2/3] Use official Akeyless brand icon for library logo Replace the placeholder icon with the teal Akeyless mark cropped from the official horizontal logo at 202x202 for the community library UI. Co-authored-by: Cursor --- step-templates/logos/akeyless.png | Bin 2410 -> 17181 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/step-templates/logos/akeyless.png b/step-templates/logos/akeyless.png index 49192870ff0337928a0166a964c174a6c377f5a8..4e4a91c8dd435f6af068e42c5bd303d1885c7d67 100644 GIT binary patch literal 17181 zcmV*DKy1H>P)Rj{E3h4dO=HIviIbc+=j1*2e1Ln$c)y>Faeu@`{EVCr zcib;`j5E-eb4Kn--ZYNn*ru^1Lkuz?45l$)z%)Y5ZmQOrcg(e__TIvdV;gK#TlFNu zNU()=Rn0ZKXFij_Odw)>^4@YCo9))bKZA+iL>Ts?v!eq_0hJ2C9D$($DCB5G3}%gv z4sxH(R&IjuznJ*H z82l4hehGxNfLaWN5}4(63?ljge@^-LEVR}rmw*Q|3`Iv7cnhEt&~#O~A6{;>t#)&O zK7`>ilu9M{gIWvs;l3GuxF0-=@Hbd~69Ci$#wwJ$pTgm7;Cq}a{z<36AtlL+0 z&)LC)3ZD*_P@`wu^&#|vs0^Y2m?zgn?xFulN+4bg*yV`grvmq-@sTo=UJk_@P%f;4 zq8?Oh8pc={E9aiPl#)~N3BQwhhV%eQ4-`TH;FVx(hNk0hViUSy9z@(hV5jXlny2Gr%T@msZ*xhH8 z0IVZIO&$(qQ8i(B7euwjYu2w<*gtM?2yKe>3af8Ph%TpxKMbzCvsi2Nb8KE=*c||E z0%$q3>Hu>g@v`1fe(>jvn&xSY7z%;yQetcbcqvHD1mMFOXJ`p5KLg^;=;~O7LIHR% z4bT|>qqfO|mHfyhb`q=_H>sQoS{ zAJ!Nj3UT|pUU^P}EP_KY#A3(1{seTj-%h1^YXq9;3#4SI`+|79r3B8hBQ)>mS zJD}BKkXjZ%6}v$w!OCfX3Z`d)QZb3u1> zs8-vp;l-Osm5wZXJaDegRBA~XBW4sK|ZV>eYv;u)r4bLlH*oCh1F#nbN z+F~>cQlxS15$92Sbt^ZJuZQy;(M1N%njriPOKmfe7fekt z_K#=ZScZlacI^r=Jgi%LLglmJa;>7Bis`3p`85!4g;vW^E;^U%!W(OoXRYxu-{6Z1 z@}U$dRAw!hCC-wYE|#sJ=9zWR+n$LWHSSs|DCJL}nTmY%Dp-v}(KsSp$5Y*NJ<7m< z1qQQkD1tR>7y~2CoaE#mJL`xyS;aqLr~xQ?0gM$;O4<#QwEKL{C;l4p49@q*P0~&T zS`0<97fJb_^pBJc*raI^XoA)Sm%O{mS9}lPn_!GV(+RM?fYR!_Q0t_7pL%N7l|)v- zOui2^&N%SDOR3MaRJWEv(K;sH1;#EY^n=wZgqmEaMRFj6okx*NFz*dSen@|-a*NSU)5Rk;?OXQmO>7=aE?p{x8k)~=O)iu(@0L248LTInp= z0^!B30KfjV2DdyjOG`_2KtEQ%&zboR5J^LEIZ8zz(979kjPzEd)HVOTh=7{mSI*n| zw&9lCV5JF)qyVCw5lqi8;^;D>`WTcNfeFSCnyXm7bGBg+Lqi#OGQJTQ50+c(+!-bG zC&G~50M+nKSO%*`5PU(hoe{s z(9xlxwS4`akbq$AQB>>4pm7qN>I&BEb;(Kt`}b$ngTOcA4huWO_k+Yp(yvdv^%R!G zFTq5wg769yZH3Y+Q3$mQTiAJD`Iu8h${KP*`8u?Q2d?0O7)l$*TyWu5E#`Xt0yDqO zr2hi&2e^Cp>Kr3!Hqo-q?b5)w7)tY^ocYTV&B60T{Bu$?0PuDw>O-O6|Bi8_T505Y z8q|C%0e=g4u2+t)Ml0o82Xd$sT#reL_-3bf0g_d%@Zx!O| zb5ME@v)uzlTcFem1c6Gt2*Wwx7Nkc;Mv;eJ|GYqGoi_-y=O)e0g6q+zZ0vhX_zQyI zI2IQ#gIdBZ@g^?JIv}sjF8NW6WRVa!TM7|h99yIjZY8aLM#TF-c;2yq#Yh~fFqZ0; z?=AUo%RgfoHWEQ@C=bIkNF{7wM~n+~0iiTXz^F&lu>1+4_(Md|F@)iV*tGUil9Lb) zCkgMsfWNL;oM7}UT5lM?`<=nYU)6BnfWhc!h);(X<8HK-Ier?LYj$PT{3lNC{WL{>)uY$?bQiS6sUZhD&5wwkgq{s~=MQiEO z$J0Wy_69UFBY%DwM8~1jd!XPXY7dJ{fPe z3alGj$vMb5?YGx2lGk*gD= zDOV8fh=rY_%wYlBCM~6-1lKF6*DD;Ea|{eK^Is904^b>##k!X2mT;c1-vTYfz$}Ja z=l>95Y;5kqIgVvIHZLinpE2PlP--ib>O-mI=Wo3Dh`)$!Y$Sq(Z72`ZQbY~J^+_Bp z1&GaM)aqlh&O3)w+{L0SQa71p3+j%`jpU86mPO@_cGy)gazC} zONkP;f|YD8rKPkj7G~%*r%&Qazl0zN;ChPgq`U{_8!-GnOmqyL<&#*q`ZCrhoONl>Ws1as<3sE%c!mZY3qI>mzi;)p|z_Mv5((5_jB?qO( z!GzT;I5)CHQ{e?Av=m?fz&1xqDJ8U&1`9a*w3KgyU_pml(kBr)CK(=*JY}{T!`MqO z^%Ed^2P!;)Ww)HMj{R=v z3ZxJlPO4jGAsNWRGnCeFi=;Y58pWbQ5GG$YlVDte*q9i*!M?;u1TXfj!jxSPGJ8R^3&2)rozPN@r=>`k>KVZcBHUWop_ikj zkO*j4H<>8Zl-XJ4dVGv%+s+Lwom%PgZA4M~;d;#_71Y&ap z2*%JAe(J=vMIj`mh&N~{nVe2+(o)99V{F{$X(`etnK-rxwdt*FZ9+@YVkE1eR0w0O z6Q;95OG#Lce&P=c4Z(t;r4S+)YZt-*g`y<#(ovpfqO$-#0^@H`IDzicRjhuk`p_vX zzh=!inh;UC@o|mAhmA9k1m%h1iaO73W=*fK6(hlyX({eq^L$lG`N7cMhLS$pu4|kW zG_@8TF2VyBRb7J8$DqO^sMSw87VzqwE#a1{25}N{Wt)~F!&I$PmN$pH>i26~EYuqS z@AA4^SngIINM$QdBx2i`mhx@TzJ}sFNy1{4>XxD+w7gxChu{I&ehy>D5ZGe~gA*vM zy^J+U2{>&eW}22FZi03)BFSDY(VD5(w`W$!5SGVR;?Q?I^rpbJ3pM;!lvUUl)9E&rvGMy;2lu-~3 zL#g*MRk-w(w3I`Rl9F9oiYQUmrx_S%dc@Ju4v^hsEx*b{Zvg5ACvz&)9orD6OK2%h zn6CYv4=i+#l9EYWpF~zwDCKD>302<2_hHRVussgL$Dq|obQCXRb26FOF0>ROIJ#Vf z7~7KNMPmHhRxAh2tOE&6UT7(*A4DsB(^b4kLQ6^2QgTl+2kmiUxSQG{PM6YBM3rB( zfNQln*W>dH`YVIpf;FS)?tWNOs7Xtau5~6c=*8*AhXM??&{AGpxkM{|QL*_sSqwl- zNctp&(B~hnPhzADF5&6?6U?3m2-<4s%7t56Zoc$NbV5rph|MLydVVs^mdHCR^y-U3o9Q7rg8%vA-GJS@ZFbUy^z!=s2NkP4S1pJ6Dy z5@2%5vQ`(t{2nZh1NuV*U!maWI@I%IBkgy^IJ8^hv!R7v^U*uKy2jfqVGk7cfYI+* zz+u5t>+MYd1Vl$IiiyVikEOxdDZufW=ij+SEh2y|f-UC(~%jKr5d zl{~XhFi|gia7XsSMCVmBMUxgx3a9ri~OA0L|ioFN+*Dn>K ze$ww~UL$)RrJ=p(xJU9KUPShkXh*q-jSO8~1o#w8{0<_VME(9%Y)NS;GOu(vc`9ya z%Q)uXumZ;9V3;dT_grxW9@IAi(QX!R0vX@cD;zDwL91-Cnl%X2Wo8_O#W4iI3C98!f@1>*h02lkM>2ej_7TqCQaCwe=j=?Z*0)+~ zUx(7WTr*K6;&e?~O0wodylC?Nb0cXpL-7Q?G~AM3bwW>(Ff0Q2HsV-Fjvs)+4^dow z8JpKt{eF(a8be>ArDW?7X=oZhv@>z^w;?*#FG7rsOn4cjc0-X2U5PlIlWBXHr$99oJ^_IgmpX(?jt^)>98qOCg9 zcbLrYOe7^WT~AAqVY2a&KCoTS5$}d3ua?5X0(Lpc+?|OzE#;ArmD9z~xl8aM#v-1j z9*dz&y7dc8@3S#~Gi=9d6yIo8f%Yfjbk0cHYiHtxBYtP1)^9S=>tLBDQOi**sDH3C zF%P-g-lL?u$R%`;oFt0nl$->_NH9tvz0YCc?^yfBjm9fnr+SE;W>t2LM8MeaFH!o-u8(*hA8O?`(#D43D>Q!Fm4sSz!!7Y7=RkCf z6pjV~pQ=Cm*PD&k9u&cBS%8z#K8YK;I;;{V^IRiwI};0Q!PpBx_*+{X3s{68T(@0F zp~-j2!>u+plxFiw9v1_0Vi0~>PZD>NdJ;a)z+0^159@`+_gtc$zKRYp2Bid?RTi*= zMy29{sKyn!dbC$7z1_xUx1-B5d2u>vCi+k-p`u8~KBuL$x1q>m;r2B-La!gX5@mT& z1CbG4;1lT_5YcfC=p;70dCk4f(K%9)>7B7RoI@G)CW8GjewFS_WR2@%MLd|<%tWt% zAheWZXQEWMzG`P;o|3e$p-5BAb=&1eyP-tCqr?vuNhNCn#vWCnepfSpT>p`6<|GYF zW!HLe(kluJ$`rBk*k#XPHz2m!J)vA>0ua%_=~Yg;YhHIS8X z%P;Y9I};_`a($9?^+G~V$?z4Jr%>~#Pa@;HPEg1_1da&|2_^2iK8fYyWcfG{eT3)t z+;oe4920mn#J~Mp2zDVy23f#NzWCr^KiE+-L|tF;fnbRMAhnD}p82+ts{T%Ys$kts%ZhGkBrnYe3LfZ^e0bxUX| zef@p)THMc!ml^abAOuIf+*P-v^4I1&6XzR(`OzV90(H_T^GT>wx2&B6=mH31%HmxU znWNZ*JD_CD^{}I**f|q@S%ipw|KSh7Knn*=b|%i4&DP>|Czvg&^6Ab*-zQ1`X5J%d zdqYVD*8F-nVF43Doir20lkhnJ?-TgAW;$h_f3w*qk+5WHL<0k|{KmAw&P4wnWaw(C zS&DnSo&);|SmxY?1uTiXQqKE&aXNXqr}N~|RvR0NHxa+>Ds3i8d@Uh`OWf%^*dAdm zswv^e^$s;R+}d&7U}U2cCqh=kEup2zIyl)InW7KNbg}Rv8}l+VWZTtNkXr84Qe1V* zF>w;{2bZ`%(YLXX@MH`nIZGR~6uIGEd~FhhPrw)>Qb#%D53ugF&*ylO9lh4?KTjHr zS%eTA*Z(@*ndoulNEuVnMr(NwK+<*J3P8l^q)+0je=ccHd0V-Ww6CG~#CXX2IjR${ig4kQw%w97RUMJ+{Ec{_>IT%2xU zl!u##pQoiLvJDon>yxA`U>swnzt~W{TNi=4Ip2dUZg)m=Bto->Kimi~zIE=-#B1;O z>Pq|q5&4~oB2FhOswLb)i#Xi^+nKm9Jxb~WQd){rO;J+alEi(oK}&%?#&vT7TLwOJ ze_#3wgM+e44K7S++3k@@loKDu}2`xoFJS|18 zSw6hm)`sG$TQXHB`G^u9=EWequ9O(bIj}vdK<{a7$LptWH)$z@gA&gRhbx=mR;v3e z`$-<&nRt4|Qz%61U}TRw$&C9JD79iPE#;x!P)BQ3tJ;mz+ zcq6J8|>{ zyX!6NZq|Wpy4vp4Qe@~#j3eEd=vC#P^beooAp{7lM%`!>0zVbUkCT=neG;9{aLZ)k z1Q;I>;TRXfQ`oRCrKJ!?M*~bvY2a`KxI86$M$#1fmY+Swp`HFcjE;u*;)_*Qp`R^w zqY!QfvuFX!Vo|ATrQ&qHuaXZ~h(9o9dHxnMF0wT6QP8ZPiU$ z3eJP@9s_@}COQt9LrY=oa7rmL29y1XnKY6S?bk>QNq*6M@cXC_3Y(evC4gQ)7_LUK zSoGp_MoQ#zNTq5iPx_tY_zDDKDp2x(yRLc4yTW%`;*-YKeiW5F?Sefm1$tCbe8dz= z$Fcs6TP+V#fT5w39GJz#y#=>*Gj{2=Yea z{6>IEZ5CaHTB3?ht`R66!9Vz-=#iXu3H;KTbYrX+SMvi?(jk$7D?$(gu}eGd!wM~h zj+5r2+SpTO%MsHH&}I(`g&hE|1yFQ{ zi;?nHqLkvCamsakV(%r#*C2pdG6{;NK_m-yV%Hqh%I^js#6D(jU00SY!gR*csBTF? zK*n(cw~1E>jz)I5^92A$neZ0J(MR>gJ-6rl>jMMEd6RTqb=Dw1^+>W~MKr{$6Y7(P zUt!{{<*-UrQeG$E4Fthj5SF-5v|96rO*+DxtIE%N1a3{+>EQ=ZdIoTc@KS{-H2NQ$ zc!__6R0Bb2*-;`2j@B88B2P=vIL0;@L;t|_7KzXh>(`4H%H}*tD{em=Zm|!yOxTZr zb~)`WYhQp?W%uV%Rr0_HmIFuS$;-eQYB&~pCSj*#4rIo4g+*wtvjc8ns3hcWG}n2U z=Js9rklu((+GQ*iz|4&N@h!lf7nxI~^fAqLyuSI>W}ifo-L9@|2N|+N6jJ&flnF&U zN1bxw=n5BZDf1eF;!cp>gn-KK8j4`HTq&2lCufiM&QI*ENQ|Xkm*n&=n9qRpJFL~8jSkB+;bzz1bmz< zr~HqfenFY&7i`VX!Po?)i!M2LAs9;uQ`RLkM_8ryFY^*%$gswu9#1DM;4#HGW_pXq zu`)^AQ*pYXi?Z!1%A8V46aKw>Ss5NNHBN82jV5Z&-~ZPO_BkMkrbWs8Zrhr1~Y>bH&4WwRDv}C{zb?6>KMA z=?aQ^!o++LD|X*%{44TMjgA6+eKy2+wk3&r9F*r3bV zmG2|@%Au=dQ8l-iO_h-kKqR;BRMi7Q}g{m@!gRKEeDZO}mp z90Sv`%g9MMCNih#&cy6qf`y8vp?K9r37@9Hd>bdWx+Fc0V_z~30UawSNlb*l zRf}P!tEkh|+=Ze^xHUE=v=nw_U}qp1>^e8XtU<=UrtX&Slf1PYk=~ACup26T8EiM9 zT=L;8+AXfsGDq?OV8HSouwMijDZ0GNOj;?!GaX+|%O zc3@Fw0mdrKO5fHsYV%o=whX;ovO1tJep{hmG;qJge7ZYvM-;|nd5`2H7p;4^vYL;i z;gM z)H^|HBUI4gC_Iw5OL*pVfjyHnZz#4O@?<_m>*zpgG<>>%i3fdv*#hD>$nwunE^a^= z^dL5&XGvRjf)L5%T0U3Oo_O+WALb95z4Fwv+(H2sOF(ulz_NXJGYW-X$C9qp45BD< zIfu6)oPd!MMdV+!Y0$7xi@KR;!22yck+h3uJhOIh6rw5K;Fb*>!kkK9nc(N#)9EvH&TURgfJfn%#Lcm#ouYkGY zw3G{BvOO&0w4EH^7}F^3dxag<-8&^7BdPa9lC|1fo9ze9CDHNEjv`BBtt9YVt6wO_6()uY%|Z__3>& zD^>fUF{qky(#0C_*Xi2$G_6XW2IN&vLW{^3dz+!9X>go%iVd@qH8Olg#E%47>y2GdIB1 z)d_^oxkO-&8nohkF;H6|_b6WnKg9KAjpQ})6`YN$P=N$!eW=z}qEzTiie6smZq6pM z99c(hDE|zN#YrO?BM2}sQcuJJujAav--+pICM-r2Zv@-rC>BDOv52syu#Elp$US$) zk>9{?YAi#y8Qt9m)Ux+J;;WYUZD#(T9GfGE%olF3tD~GWBuT&MciDja*JAIjw~V%y}k{U@?qG> zh{o8M7*6DxlBZ9fN99@VC*v}m$cg{Q?GNfvr(4G`pv(2?vg3whVo63 zdJ>R@Nu!J*&r!Y`e1p*~ zEIlY9-h?0TyAQheci8INsMe1nHfJzBJL`-@dMtqop|$U=$m;Ceqht!Xp*$K0V^8Tu zs<-Vx9bJ_EZtoS<&xwz#xFY+Ysvd8SwU_n-vz0Fw`eZ!xGwYb z&1T7(0~J&&XBqStX8eh3(OJ}Lv0ET34N1QzCiZ&GIkJMh>h-9Q&`V@x`}+xfeT03k z#{zshyciXJ0nGnOTK^~XbUzJH8MPXr9toFBs4fz=xiw_@otWRqJ<1~^(OurZALyQF z9z5&z-3Ro9gkMZ}hvWDxqG-kqeRXyy1c4ZdgkdpQN@IB#HlLAKy&g|BOctOLcJw}U zb!YCvmOlbfhD z=mcujV~C?M#PLPM=7F<{LZ}ghfioHj!DInZZY%Q=xuHB(&oVBag)ubbS4YT0zhc`o z>gEdS@rP`9n2Cltj?W>EXB^$7RFp7GrXwY{Y0_PC%q!%E@;D(pFfkZ05=rh;Y}+@D zzNHt@sgHqZ81>qRh~jIg)jTIm%P*49=et$1VJLF0HJfNZ;KxCgge+gZ7A~tu-uD9LDjDlbyJKZ;MiDfSCsn7Pi3J zH3*alRT$J|IZ-5Ah=miDlPTv3+UNA1ui(QP7$;!RG*)n*9bKk_+VgB`dzkfJfOjC! z%TX#2>PASE>0yx@$~?jR%u$+QS(_O0fc&B`bl6|-O*`%uih6>SIsy}q!tw<$9yq44 zsJaLz%n5-z^?a|QpJQJ1QJQPno;3V4p@J}_1_r*}8t-|D{mrmze{wJp*~;m|x1f3} zAl*TVmVj*sYW3$ps@v^cabcTlL|Ry@^GewKFqGyn(%63s5Ueerduf15-MfBk7~L@g zi-D=>M?2-ayV!k?9srY45RDn7(4bSrxKIF%7@jE;ubu|rPf>?irR z`z!_qd>21GoC5W^)^^OsG;1waq20ow4Y0OH@_8s1e#+pN{Ox@6@RRi@1pJr(QpQZY z5~Q{uEWAL>>jBj(GkE}2!H8Vk?6j`FBV_td9(HX5u+zPg!3@#Mx=qSnrm8t(F1-z` zePgb%v;+LWmv|CCi_+h(ao~WFvT^k3@Fxi94yIr!pwM;pqj)_)J{?j67!bptnj`NiN40pAI3U%?3X<<5UZM){9Y9?s zwi7nMDb#raWqjs|g)I=TC!O!Cdlq>gyLN>rmra9hEEpfzpaR;<*6s!2Wq>v!3_D#2 zR6)KfpCrB+Pzlgb!GLNAlD5t9Y*H8L+ zl7~DfOToOH6+KIaH;|%Js*WS^l_R5Av$x`3$%ONN5QRBU^~f^B*hfYT1_tKNq*S`D z*{<4g*y=2aCt)f>4lJPLuA3t3&Mmfn0w$M1`hg84?C3;g_8BPk0#xv06bcH}s^2c< zCMA?Spnfge<3d{UBr1SW0(c3ao(1zpCVI+>*O;o@m2Iv4UNy{Po?hL!h1yT zNe;5iv-ru!ojYeSJp2ikMKh#|U97Me)@>zz%FVKfODA>#Yi8ejMiJo1kuoMLOAw$B z%uiu)Pl$2}n4SH;1>C%6a-~ou#6X}}(5T0KsLXC>!w;a;1DySvhOJ!#(H*zvJ@qa_ zL#i#h_yZ#fmn0M&CMT@tL$Ry+BCs^VCVzQql$M=N*LEs-&Io&03x7_TlNQs4=@PU+w8%qiw zFWgC*lK=b-*W|s8$1*VO@WC3F?S0p|Khx}X4{3BlVXZT~tUoLk8~#Pu3C(KQQv>(R zskcT^R9}GU0f~rS9CslMI(!9D4l{SJsHH&C4u{6PLV#EqgjP;*w}`N@vf#^?`un-~ zuBz6$B0+K$BfibLd{xBbEI$ik>ElOWe2&xqeiPNpQy_|5fT=Z<#d$)Nwcl9 zq@mPPAZ!EMJqSv>(7pAUMu@WSP~3R9cv!qs)=yM&M&k92ldtTHq4=C)I}C;2gYW^Y zz2k}^a*ZUQmC!Z$U5>{KDLaY>@r^{O(LYHvevWi4TuRmy`ilx+?bAfG3nYJNO6KCR z8F$(yuf~oNVf-Rxz9H$~vB8cVbHBRb^SkC!-!YirHcV6}OJ(O>;<&p-qxE5rn)O3t zUSR&q6^RPG^r5X)P}B`3X*o(|p^j>G3LpPD#+FqQhDnd5-bm^nUek~@q(8S*`UVE1 zG#)1ljP8R+Kf;B2515vNxdX;{s*5P)i;dKyWRGOa=C2%WYAEhOPm)w)J@q9FG>lye zV>iMgCJn1>u@p?)`?v;NY&>8h6@+n(k=wQ{DEoc4E&5yLT4-m4Gymf(V^J?iN$H~q z=B?=L0HR0?MXHvPcgp?svn+Y~(x!%zhG7D0Jh#b(U|Kn2A=)Z4<(6zYR#5ot&y#ri z=hD3g_K*A1c%Tn$(PtUt=u4jQ^AoqQa`zZ?S;Np0#Bm>L^}gha5md|d%Z;Rs4W$** zoRpHO%*KcTi5#i5Vg)2dWDQp^t!HrN-80Y+?tp$(Yb&}-`g4n?+`3hx|6>C>5QB$5 z!vx9%TE;}3sMnuI5UfP8SO!q=&DVL8>2wz*;UwQB=c6?>xZsKe^XjfCYr|Z@ENR>v9GuWBb>c>35b>@FVb#OQm$7Xg0;D!r0`l<3IEJR z6bgk<*aXXbcxJ6-o`FUkel!*kjW}7*6UDu-X0v!5 zB2!d>?J<}>4&bxK>t$Abd|Y0i%_Tmrq|sn8e3{>6_=+vsVOV;A*xVuJeNgI0C=~%) zODlk$5$?WUUJYwcLuutP`3J=yioD?nl|#Y}MO&qxZ>8^IYg4!|I)y7!Gw#$CnvSTT zv(GhjZ0OCz(W)+OXx0hlESf*wZ&QdAJN3indS?Q54y@%>14GRQ>j$^5!p486 z*d7IF6xQ5u*QdLSP%cT>B}7=+Bk36oxxuudM@d0WNzqS!Ss9j*bqPvJ$AFD{0keg) z&~s2yT6de6rDS~7v6TnYQ`m8-G8%?8a;!v$U`I0y6Lps>M=sl`=!i3rD3Q2TexKHQSSJ@kX?`Br!bR-qzr<=xK-6cc~Q_%x(|VtrsN(A`x5H$Iyd&5 zdLhTky_ig&XWpv-LkIjE%WyKA^GxjlOyL}ukE3qJzqKoBO)T7O$)iSppFNz73s)S(@f=Q0& zo054otj!Fi!Iw?NFIYyH{Zyt6q;w2KW{K0sQurK^odQ)AMn^9=#S`}e5qTNNwns_l zSY*G=(BbBVkr0fBZ{w``2!VuLQ4KQqWqC0cb@`QeuE!!=y5xK1M$(3c@>OshXc!nH z94JZdrD3of(AyBzW5=ouC+nD)xCA1}TNpSbr>YcR{eX?Q)cV1lNIvu#Cwq<&peF2VGPQ*AwAY0>6&H zynw=ur@*7FT4iz?D~yb2w@@jIkasR?@%)zJmalKCL#?`wdi)WreVf65Wn(@<9Eolk zoD5PJ`bI9CUP5*ciSG&R=TXuyOceNC`ICWiSxTZ3g7reHI#8Sx*>PN+0ZyO3=DPPj zKOr1Wc9>+DZG7lZ5<(4m5ktYojWwJ;{2AgElE2guMWWH#<)opCoq>$-q2@eLR`ay) z71@3$z6z-(nLH@IO(3jeb|S7_9SUKv4yD3Q#QX*-(GJ*qPlG3l$*bAL_3n3#U6$GO zE&?f12;k=O>@J{7Zh7 z_c-zU8$fZK6jrgqQl|)N`DrK$Ku!Wjys=;7?T?rRkM=W^6rOPG4Mha@6h(;Q0G(x7 z(6h{iVPOz~)HF=ZpsUsl!^J0E@u%KtbK7Zy!OHQXnLBYNy~%+v@XJ!r3eFZAL4~vuE*k? zWVQE#^)2mfC~5J=kHrSADJjAWk?})KVc0{k&jYrXwTNMpfxW@t(a+pmx^!e9)g#BF z%5E4YV+E3{?cAB@3HU8*)zhbM0(WPi3Ne8@U?Hk5n^3DQbGvYy4h}hUNxW-rC~XZd z3@)BQ8j?nm7BC0s2sBi<7^FH8NC;+U-Gg!Jo{%O#10AR*3YmVEC97ziy}5T!;qZ{g zem93NVL|4uCSr+Iz?}cM1%`i+j8-7osD8oL`gXj$?{Td2|?XY*B!o=^r zv84UGp+*>%O&mBN3s-#zd3b*^%R?3$daj^j>L{4r2J`!1o{-@$=Yh$s=t#r~Gdvmk z2x#vd<*VRB2pJ!ikis~+xi$g9P@`1rL}WIgpe7NUI&6gy6)kx9S|UEzRCP&zpS=es zDf=>^>$L!<_iGG|$1W!malMb<9#_nZtj|g0qvWYBBHQY8q0>-Kl6~)=7XTKXq4>G` z7Mi}u=n35=0wg3a7fYnnM#M8^SQHq#j!Welm~SOrYlZdeCHzV!47007S-xw~zlza6 z$gZ0&xM^>jRq6y7GhiE%QoV@dP6S$p+}si4E}&eqG=Iyohy8|Fc!n}343pB1)Uu?( zR*zA&8l4?d2JHjf)-MaC?jbTWxOj{)JbWk71h$!fKw9@miuV{D4O}x)My8oBWBlkO zR86kIDp>A7r6PMIg&P+%4w)wBxb3lUa+I$^x+c%jU92RobwBu}2;CjBJLX3SgIB=O zUQ}xv(H(cX2Q2+a^A*QB&alzQ&%Si2cH02lXJoN$?_Hp9iWq+b<2V>s+#yjeksG;g zFs#Mk+TUIlrbl_$vrx;mFosaAdIJiB9%6bCi~@lpyT~nEoou{*|9SpV+J8){E1Y(q)mZ?F%; zV#A1ISy-geQI>~5_OcW>(02j4j!SP>@I|NRh4};+ZPI5+)_EUP`1P+fcI*(Ep!sBbeXwA{}myC6#SWdn|2hFu6xb z(Y(hpH||$+i@KtzrVY1eDMlv<%a~LFF=UkZlg8_R{S;wGfA${~p}PzYGPWIRSu?S( z8ee{A97f#*+xsp-VeW!4OT30n3wbitIL>y?_9f(o^0joA*sCjQ9iUj~fYxge$0d~1 z1K9W;P7ecx%2jOqRSi3T(+tD1>`_|xNXja2*;2gWUu3XNw4CqZlVL@cmN3wTT75f6 zFLB$@nne+z?2BwG+6j5+^-a8t7u)k;SP(2nA$$p-{|JpYQ7vr3>BEIDUDuTAl4YaW zxRIUDq?SHW6_=E@?-JvGfz(lezi`(@)_HqRA_qp>jT7e{%9nc zp^?@%u7K}QwnG?}8_GWq!@N2PTLV%tKn3aEqqC!gO64VJ`T~Y#aOSNLGc#8~B)Lm- zoH)x!Jqa`6{CV#+NoI9klWN#GOuW+z@H1faAd1&RscsaM2&zf-P$(}Zp}1uGI^>4( zjo~|!l!R3B5csv#D=5&r5Zeyopx1`wzkv^b3`!;>o8o%SQ#q@2mj@5NJU`!2IL|ZH z5qEuzd7T(PMo*VW9|6;|7Hp-=E}eWo^p)XgEcYnim>VYhCE)o^)3croSSW~M{04yf zm{3F^n8CT8Y4E|zAgLPdXN(Lo2KHsN4>@Q4KK|0zSO5MVF8+s6L^TUGMOa#j+3I?? z4b2NjDX=?WGHoP++)%zL(x$Tu!#Dz*B76x&*GO1~di@1J-GQ+dAHUv#lYbO}y&3l! zY1s3S_7bicwHR{7;+1K}q#-%Qo>6`1Ce9uC2ytaGpnDM2BLr%tOphWA3(mVlbJlrh zG7QTN<(p#8iAXvL!+^RB36e06R}ttn#8EG?S!6Z61?IE<0R!QDSAY#7*^p54v#1_V z&RAsj-eO0~I`65%Ep(gX9K{g`cPNT~0>eA7sLKz>MUnJbM6N>0$et&bF%Od)%D08P z(J9>}mJfOOHOgff@Y)DcOF2}M#NNZXzg00jeAS)2?0OL`%YWSev-PwzjitHT&l`RO z=1+0ztr=KVfvI-@Y8j?y*Psx{pDU0fh-G%2cWx+;r0(LzVr2qQ?tJJK^m!@Rz5vDp z)M^E+&_el`m}?|s0fyv69?GUzMG9HOgb^=pL)S;=fubJWX2vl%W1q5xu%iFL;6EdZKSjOHZqH_*Eyz^>WuzgYx@6-r zgIt#|OEFLm4d-w?a5!bN5I} zm?cTur#Sh?8JQvmV+9B+5yvb19!Y6TTATEz{P)Q~exG}kM@91{rxh*#@W=_OXeHRb z0)t(DJ zvZ&rDC^zsWeHO{_n(Xa)irk|-)-a3p!#87cu@4h zq{1chDWBug`;6HcY1no^s}98R1_VJ5v??Vc>Rvj^&$7%bx=XgJE%Ozgrr~e0wGwW4 z?;_(ZLC^uvX0rTCQuMF7KriD$Vac4=dnL+w^N@Tu_55U{IDH>t^lM+a4XVkOuXh;y z78^T?sQ$U@K@~#T;+*be@qLzggxpZRUo`s*@-S2?Ugd?9tX0?vqMut}4{UusMt|E} zyxP~7T_X{sx@Q8v6EigygVX=_Euel1DtsHY`cc$s7h!qY>k??;&?O!F`E?0W4P75! zX4=)ybFd`oZqTyG_MXF>1Undj zG<*ogN%$ofU9#7-oZJP*a|G5!N&F=o|#*TEL|i(KH31)^`h6Tu-|=VuE#r$5>Ii4(1!Udw;vLs#@LCFXZM)QkDKpZGP~5|Aykqp8mfk2CDVg zX@gn%0%WHUbk3!;d@Pg@CrZ&iAgO3c0QwuoK~~Aw0dO(8h0VQ_xO=xkppX?pRH`q) zm}O3hQVwl7<%I$x52MQ;lJEH_S19R@B)mL-pTtNASHQRh@Klg!P)*^?@TX8Xis|WY z5G|IWBgBzZACg^t?#*PorvIctZn95w!TdFhhJt<2A%s4x-Lv4@Tr^1+My)8_IWwpX@V6 zGrNOF`XyJv_6#%5+XAj)X1Y3OiR#FRZ1F|g;^IY7beWsO?B4yrTsZnMjJ*#VT?X(Y zzzxCm5Ll{&t~~3Al)ie1M|&=loCvJhSOsfqU|a_5S%m5`Te{c$=EK7QR>VEva18>r zSpn;qY_Io4s_G`uT%RR3l<$5&#GklOHW2!stWbc$ryz9}y?s})?7z=A;k;~a8-8@O zzVCFQQK_;D8tb9V8i1A)&<&C@ZQ7J{)i7TVd!&C`e3Tf9D{#$#%|$4jMOe6k=Zg1| zP->98vLS%e!(FiPc2z8F633r}UKNmvV2yBba*y&TH$w_I^(e4R21u}F>ZO!M_+ z&;qhK7#Q0#8CXC{fLIEM85o!rFu_GmEnr5lL6TzmcQ}CB9(cMqhEy=VofDfel~v~0 z|L1Q`7hOI$X=3Ev62S-A^9CJ*5yLRws0+PhD- z^jzUl?b;)tq*}82WyrIbr#A0O?loJ#E1i9MPQTsp`-~s=ygzsD^Pc_f^6z%cP5smw zEs|IL?OpHI?E9U$_w)O=ZQJ(bisk)Rud?r(rVb4y(+*ebI|JB$rGELWN^ieu> zY`yu>&-!~LVq;^SuN5C``uXvSuy)&dKk z&v!eoSnqsbnnUWRr(x&!tGF+}BP2UH@74FDw)ekGU+w?7@ci*zt5-idu%`Oala5=D zj<1U5JF>#xT)Vq}D*0==1+B}L%1v8;;@H0B@}0@T z$CjP@US9CZ{_3ya0?!o^oD&iPckO;W%lv-7;O%<9SRcFDy?_1B^(Rlx_nZ5if$6Ew zg8D$NNj-J14@>NPdi=`P=bL9{#2M;a*YOaIV6rYD*Jw*2XulPEvfAPQD zvMaOCryE_|RN^Ny*G#c=9y3P_AB$FXe9GfpUkk6Q$Gd(#czCbB`=_%aWQ zE6SD)4wXV4ok1V3+fV#s`T3gZGQ(TT?^iv`>{BebDd182()2>;^*8yw$4ck(en~v% zyxXF5dXOzP7KkPswqJb2R?rShDX+*4)~BnVs2D9}9V<&5o})^m^*O?Z^1d zT-NRD(?4A0{P16@qrz8xuXPLUcD2Vx{wdz@@|}fAa+N&C;_!Nrt=5*_mq<>x(i(bn@%*%-@#iKhC_ z_Po1cd-`_C?spN1Yvw6A-TOv6)VDZ0=J%~x8Ifk{pF*bwey;X@d0CjVomV={>KPwP z3}?!=oV(NF_dNN?eX0KAqSlQyH!pF2x?`isGKFQ+$$W3mI>93W_tpGoNqCFtOTAkD z^ru2WAcsgjxA&w!KgFe$D*LNHeY^X4r)qrO*?Ct(rYu>t>Q&9WuJf%2RGk;7c=<1W zV*Bpo?ehQUt%WCxzXK+0pNHuY;GVVV8@C#$&Ex6n=d#Wzp$P!b85x!U From 98680beadfb0d272a0d494c37b3ece0356c8d430 Mon Sep 17 00:00:00 2001 From: baraka-akeyless Date: Wed, 17 Jun 2026 07:32:05 +0300 Subject: [PATCH 3/3] Fix Akeyless API URI construction in Invoke-AkeylessApi PowerShell string interpolation does not evaluate method calls on variables unless wrapped in a subexpression. Normalize the gateway URL and API path before building the request URI. Co-authored-by: Cursor --- step-templates/akeyless-access-key-login.json | 30 +++---- step-templates/akeyless-aws-iam-login.json | 46 +++++------ step-templates/akeyless-jwt-login.json | 38 ++++----- .../akeyless-retrieve-dynamic-secret.json | 70 ++++++++--------- .../akeyless-retrieve-rotated-secret.json | 70 ++++++++--------- .../akeyless-retrieve-static-secrets.json | 78 +++++++++---------- 6 files changed, 166 insertions(+), 166 deletions(-) diff --git a/step-templates/akeyless-access-key-login.json b/step-templates/akeyless-access-key-login.json index add96758e..ff22e899a 100644 --- a/step-templates/akeyless-access-key-login.json +++ b/step-templates/akeyless-access-key-login.json @@ -9,44 +9,44 @@ "Properties": { "Octopus.Action.Script.ScriptSource": "Inline", "Octopus.Action.Script.Syntax": "PowerShell", - "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Auth.AccessKey.GatewayUrl']\n$ACCESS_ID = $OctopusParameters['Akeyless.Auth.AccessKey.AccessId']\n$ACCESS_KEY = $OctopusParameters['Akeyless.Auth.AccessKey.AccessKey']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_ID)) {\n throw 'Required parameter Access ID not specified'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_KEY)) {\n throw 'Required parameter Access Key not specified'\n}\n\n$body = @{\n 'access-id' = $ACCESS_ID\n 'access-key' = $ACCESS_KEY\n 'access-type' = 'access_key'\n}\n\nComplete-AkeylessLogin -GatewayUrl $GATEWAY_URL -AuthBody $body -StepName $StepName" + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $apiPath = $Path.TrimStart('/')\n $uri = \"$base/$apiPath\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Auth.AccessKey.GatewayUrl']\n$ACCESS_ID = $OctopusParameters['Akeyless.Auth.AccessKey.AccessId']\n$ACCESS_KEY = $OctopusParameters['Akeyless.Auth.AccessKey.AccessKey']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_ID)) {\n throw 'Required parameter Access ID not specified'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_KEY)) {\n throw 'Required parameter Access Key not specified'\n}\n\n$body = @{\n 'access-id' = $ACCESS_ID\n 'access-key' = $ACCESS_KEY\n 'access-type' = 'access_key'\n}\n\nComplete-AkeylessLogin -GatewayUrl $GATEWAY_URL -AuthBody $body -StepName $StepName" }, "Parameters": [ { - "Name": "Akeyless.Auth.AccessKey.GatewayUrl", "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "Id": "10001000-0000-0000-0000-100010001001", + "Label": "Gateway URL", + "DefaultValue": "https://api.akeyless.io", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10001000-0000-0000-0000-100010001001", - "DefaultValue": "https://api.akeyless.io", - "Label": "Gateway URL" + "Name": "Akeyless.Auth.AccessKey.GatewayUrl" }, { - "Name": "Akeyless.Auth.AccessKey.AccessId", "HelpText": "The Akeyless Access ID used for authentication.", + "Id": "10001000-0000-0000-0000-100010001002", + "Label": "Access ID", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10001000-0000-0000-0000-100010001002", - "DefaultValue": "", - "Label": "Access ID" + "Name": "Akeyless.Auth.AccessKey.AccessId" }, { - "Name": "Akeyless.Auth.AccessKey.AccessKey", "HelpText": "The Akeyless Access Key used for authentication.", + "Id": "10001000-0000-0000-0000-100010001003", + "Label": "Access Key", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "Sensitive" }, - "Id": "10001000-0000-0000-0000-100010001003", - "DefaultValue": "", - "Label": "Access Key" + "Name": "Akeyless.Auth.AccessKey.AccessKey" } ], "LastModifiedBy": "akeyless-community", - "LastModifiedAt": "2026-06-11T08:07:13.165Z", + "LastModifiedAt": "2026-06-17T04:32:01.568Z", "$Meta": { - "ExportedAt": "2026-06-11T08:07:13.165Z", + "ExportedAt": "2026-06-17T04:32:01.568Z", "OctopusVersion": "2024.4.0", "Type": "ActionTemplate" }, diff --git a/step-templates/akeyless-aws-iam-login.json b/step-templates/akeyless-aws-iam-login.json index fc763e0ff..5d137743a 100644 --- a/step-templates/akeyless-aws-iam-login.json +++ b/step-templates/akeyless-aws-iam-login.json @@ -9,64 +9,64 @@ "Properties": { "Octopus.Action.Script.ScriptSource": "Inline", "Octopus.Action.Script.Syntax": "PowerShell", - "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Auth.AwsIam.GatewayUrl']\n$ACCESS_ID = $OctopusParameters['Akeyless.Auth.AwsIam.AccessId']\n$CLOUD_ID = $OctopusParameters['Akeyless.Auth.AwsIam.CloudId']\n$AWS_REGION = $OctopusParameters['Akeyless.Auth.AwsIam.AwsRegion']\n$STS_URL = $OctopusParameters['Akeyless.Auth.AwsIam.StsUrl']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_ID)) {\n throw 'Required parameter Access ID not specified'\n}\nif ([string]::IsNullOrWhiteSpace($AWS_REGION)) {\n $AWS_REGION = 'us-east-1'\n}\nif ([string]::IsNullOrWhiteSpace($STS_URL)) {\n $STS_URL = 'https://sts.amazonaws.com/'\n}\n\n$cloudId = Resolve-AwsIamCloudId -CloudId $CLOUD_ID -Region $AWS_REGION.Trim() -StsUrl $STS_URL.Trim()\n\n$body = @{\n 'access-id' = $ACCESS_ID\n 'access-type' = 'aws_iam'\n 'cloud-id' = $cloudId\n}\n\nComplete-AkeylessLogin -GatewayUrl $GATEWAY_URL -AuthBody $body -StepName $StepName" + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $apiPath = $Path.TrimStart('/')\n $uri = \"$base/$apiPath\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Auth.AwsIam.GatewayUrl']\n$ACCESS_ID = $OctopusParameters['Akeyless.Auth.AwsIam.AccessId']\n$CLOUD_ID = $OctopusParameters['Akeyless.Auth.AwsIam.CloudId']\n$AWS_REGION = $OctopusParameters['Akeyless.Auth.AwsIam.AwsRegion']\n$STS_URL = $OctopusParameters['Akeyless.Auth.AwsIam.StsUrl']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_ID)) {\n throw 'Required parameter Access ID not specified'\n}\nif ([string]::IsNullOrWhiteSpace($AWS_REGION)) {\n $AWS_REGION = 'us-east-1'\n}\nif ([string]::IsNullOrWhiteSpace($STS_URL)) {\n $STS_URL = 'https://sts.amazonaws.com/'\n}\n\n$cloudId = Resolve-AwsIamCloudId -CloudId $CLOUD_ID -Region $AWS_REGION.Trim() -StsUrl $STS_URL.Trim()\n\n$body = @{\n 'access-id' = $ACCESS_ID\n 'access-type' = 'aws_iam'\n 'cloud-id' = $cloudId\n}\n\nComplete-AkeylessLogin -GatewayUrl $GATEWAY_URL -AuthBody $body -StepName $StepName" }, "Parameters": [ { - "Name": "Akeyless.Auth.AwsIam.GatewayUrl", "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "Id": "10005000-0000-0000-0000-100050001001", + "Label": "Gateway URL", + "DefaultValue": "https://api.akeyless.io", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10005000-0000-0000-0000-100050001001", - "DefaultValue": "https://api.akeyless.io", - "Label": "Gateway URL" + "Name": "Akeyless.Auth.AwsIam.GatewayUrl" }, { - "Name": "Akeyless.Auth.AwsIam.AccessId", "HelpText": "The Akeyless Access ID for the AWS IAM auth method.", + "Id": "10005000-0000-0000-0000-100050001002", + "Label": "Access ID", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10005000-0000-0000-0000-100050001002", - "DefaultValue": "", - "Label": "Access ID" + "Name": "Akeyless.Auth.AwsIam.AccessId" }, { - "Name": "Akeyless.Auth.AwsIam.CloudId", "HelpText": "Optional pre-generated AWS Cloud ID. Leave blank to derive credentials from the worker environment.", + "Id": "10005000-0000-0000-0000-100050001003", + "Label": "Cloud ID (optional)", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "Sensitive" }, - "Id": "10005000-0000-0000-0000-100050001003", - "DefaultValue": "", - "Label": "Cloud ID (optional)" + "Name": "Akeyless.Auth.AwsIam.CloudId" }, { - "Name": "Akeyless.Auth.AwsIam.AwsRegion", "HelpText": "Region used when signing the STS GetCallerIdentity request.", + "Id": "10005000-0000-0000-0000-100050001004", + "Label": "AWS region", + "DefaultValue": "us-east-1", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10005000-0000-0000-0000-100050001004", - "DefaultValue": "us-east-1", - "Label": "AWS region" + "Name": "Akeyless.Auth.AwsIam.AwsRegion" }, { - "Name": "Akeyless.Auth.AwsIam.StsUrl", "HelpText": "STS endpoint used to build the Cloud ID payload.", + "Id": "10005000-0000-0000-0000-100050001005", + "Label": "STS URL", + "DefaultValue": "https://sts.amazonaws.com/", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10005000-0000-0000-0000-100050001005", - "DefaultValue": "https://sts.amazonaws.com/", - "Label": "STS URL" + "Name": "Akeyless.Auth.AwsIam.StsUrl" } ], "LastModifiedBy": "akeyless-community", - "LastModifiedAt": "2026-06-11T08:07:13.231Z", + "LastModifiedAt": "2026-06-17T04:32:01.625Z", "$Meta": { - "ExportedAt": "2026-06-11T08:07:13.231Z", + "ExportedAt": "2026-06-17T04:32:01.625Z", "OctopusVersion": "2024.4.0", "Type": "ActionTemplate" }, diff --git a/step-templates/akeyless-jwt-login.json b/step-templates/akeyless-jwt-login.json index 665cc6a6c..757495a3a 100644 --- a/step-templates/akeyless-jwt-login.json +++ b/step-templates/akeyless-jwt-login.json @@ -9,55 +9,55 @@ "Properties": { "Octopus.Action.Script.ScriptSource": "Inline", "Octopus.Action.Script.Syntax": "PowerShell", - "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Auth.Jwt.GatewayUrl']\n$ACCESS_ID = $OctopusParameters['Akeyless.Auth.Jwt.AccessId']\n$JWT = $OctopusParameters['Akeyless.Auth.Jwt.Jwt']\n$ACCESS_TYPE = $OctopusParameters['Akeyless.Auth.Jwt.AccessType']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_ID)) {\n throw 'Required parameter Access ID not specified'\n}\nif ([string]::IsNullOrWhiteSpace($JWT)) {\n throw 'Required parameter JWT not specified'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_TYPE)) {\n $ACCESS_TYPE = 'jwt'\n}\n\n$body = @{\n 'access-id' = $ACCESS_ID\n 'access-type' = $ACCESS_TYPE.Trim()\n jwt = $JWT\n}\n\nComplete-AkeylessLogin -GatewayUrl $GATEWAY_URL -AuthBody $body -StepName $StepName" + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $apiPath = $Path.TrimStart('/')\n $uri = \"$base/$apiPath\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Auth.Jwt.GatewayUrl']\n$ACCESS_ID = $OctopusParameters['Akeyless.Auth.Jwt.AccessId']\n$JWT = $OctopusParameters['Akeyless.Auth.Jwt.Jwt']\n$ACCESS_TYPE = $OctopusParameters['Akeyless.Auth.Jwt.AccessType']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_ID)) {\n throw 'Required parameter Access ID not specified'\n}\nif ([string]::IsNullOrWhiteSpace($JWT)) {\n throw 'Required parameter JWT not specified'\n}\nif ([string]::IsNullOrWhiteSpace($ACCESS_TYPE)) {\n $ACCESS_TYPE = 'jwt'\n}\n\n$body = @{\n 'access-id' = $ACCESS_ID\n 'access-type' = $ACCESS_TYPE.Trim()\n jwt = $JWT\n}\n\nComplete-AkeylessLogin -GatewayUrl $GATEWAY_URL -AuthBody $body -StepName $StepName" }, "Parameters": [ { - "Name": "Akeyless.Auth.Jwt.GatewayUrl", "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "Id": "10004000-0000-0000-0000-100040001001", + "Label": "Gateway URL", + "DefaultValue": "https://api.akeyless.io", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10004000-0000-0000-0000-100040001001", - "DefaultValue": "https://api.akeyless.io", - "Label": "Gateway URL" + "Name": "Akeyless.Auth.Jwt.GatewayUrl" }, { - "Name": "Akeyless.Auth.Jwt.AccessId", "HelpText": "The Akeyless Access ID for the JWT/OIDC auth method.", + "Id": "10004000-0000-0000-0000-100040001002", + "Label": "Access ID", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10004000-0000-0000-0000-100040001002", - "DefaultValue": "", - "Label": "Access ID" + "Name": "Akeyless.Auth.Jwt.AccessId" }, { - "Name": "Akeyless.Auth.Jwt.Jwt", "HelpText": "Bearer token used for authentication.", + "Id": "10004000-0000-0000-0000-100040001003", + "Label": "JWT / OIDC token", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "Sensitive" }, - "Id": "10004000-0000-0000-0000-100040001003", - "DefaultValue": "", - "Label": "JWT / OIDC token" + "Name": "Akeyless.Auth.Jwt.Jwt" }, { - "Name": "Akeyless.Auth.Jwt.AccessType", "HelpText": "Set to `oidc` when your Akeyless auth method requires OIDC instead of JWT.", + "Id": "10004000-0000-0000-0000-100040001004", + "Label": "Access type", + "DefaultValue": "jwt", "DisplaySettings": { "Octopus.SelectOptions": "jwt|JWT\noidc|OIDC", "Octopus.ControlType": "Select" }, - "Id": "10004000-0000-0000-0000-100040001004", - "DefaultValue": "jwt", - "Label": "Access type" + "Name": "Akeyless.Auth.Jwt.AccessType" } ], "LastModifiedBy": "akeyless-community", - "LastModifiedAt": "2026-06-11T08:07:13.228Z", + "LastModifiedAt": "2026-06-17T04:32:01.624Z", "$Meta": { - "ExportedAt": "2026-06-11T08:07:13.228Z", + "ExportedAt": "2026-06-17T04:32:01.624Z", "OctopusVersion": "2024.4.0", "Type": "ActionTemplate" }, diff --git a/step-templates/akeyless-retrieve-dynamic-secret.json b/step-templates/akeyless-retrieve-dynamic-secret.json index dc5dfaacf..f8498ec59 100644 --- a/step-templates/akeyless-retrieve-dynamic-secret.json +++ b/step-templates/akeyless-retrieve-dynamic-secret.json @@ -9,94 +9,94 @@ "Properties": { "Octopus.Action.Script.ScriptSource": "Inline", "Octopus.Action.Script.Syntax": "PowerShell", - "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Retrieve.Dynamic.GatewayUrl']\n$AUTH_TOKEN = $OctopusParameters['Akeyless.Retrieve.Dynamic.AuthToken']\n$SECRET_PATH = $OctopusParameters['Akeyless.Retrieve.Dynamic.SecretPath']\n$OUTPUT_VARIABLE_NAME = $OctopusParameters['Akeyless.Retrieve.Dynamic.OutputVariableName']\n$TIMEOUT_TEXT = $OctopusParameters['Akeyless.Retrieve.Dynamic.Timeout']\n$DYNAMIC_ARGS = $OctopusParameters['Akeyless.Retrieve.Dynamic.DynamicSecretArgs']\n$FIELD_VALUES = $OctopusParameters['Akeyless.Retrieve.Dynamic.FieldValues']\n$PRINT_VARIABLE_NAMES = $OctopusParameters['Akeyless.Retrieve.Dynamic.PrintVariableNames']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($AUTH_TOKEN)) {\n throw 'Required parameter Auth Token not specified'\n}\nif ([string]::IsNullOrWhiteSpace($SECRET_PATH)) {\n throw 'Required parameter Secret Path not specified'\n}\nif ([string]::IsNullOrWhiteSpace($PRINT_VARIABLE_NAMES)) {\n $PRINT_VARIABLE_NAMES = 'False'\n}\n\n$printNames = ($PRINT_VARIABLE_NAMES -eq 'True')\n$path = Normalize-AkeylessPath $SECRET_PATH\n$body = @{\n token = $AUTH_TOKEN\n name = $path\n}\n\nif (-not [string]::IsNullOrWhiteSpace($TIMEOUT_TEXT)) {\n $timeout = 0\n if (-not [int]::TryParse($TIMEOUT_TEXT, [ref]$timeout)) {\n throw \"Timeout must be an integer, got '$TIMEOUT_TEXT'\"\n }\n if ($timeout -gt 0) {\n $body.timeout = $timeout\n }\n}\n\n$argsList = @()\nif (-not [string]::IsNullOrWhiteSpace($DYNAMIC_ARGS)) {\n $argsList = @(($DYNAMIC_ARGS -Split \"`n\").Trim() | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })\n if ($argsList.Count -gt 0) {\n $body.args = $argsList\n }\n}\n\n$response = Invoke-AkeylessApi -GatewayUrl $GATEWAY_URL -Path 'get-dynamic-secret-value' -Body $body\nif ($null -eq $response) {\n throw \"Empty response retrieving dynamic secret '$path'\"\n}\n\n$fields = Parse-AkeylessFieldDefinitions -RawValue $FIELD_VALUES\n$created = Publish-AkeylessStructuredSecretResponse -Response $response -SecretPath $path -StepName $StepName -PrintVariableNames $printNames -OutputVariableName $OUTPUT_VARIABLE_NAME -Fields $fields\nWrite-Host \"Created $created output variables\"" + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $apiPath = $Path.TrimStart('/')\n $uri = \"$base/$apiPath\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Retrieve.Dynamic.GatewayUrl']\n$AUTH_TOKEN = $OctopusParameters['Akeyless.Retrieve.Dynamic.AuthToken']\n$SECRET_PATH = $OctopusParameters['Akeyless.Retrieve.Dynamic.SecretPath']\n$OUTPUT_VARIABLE_NAME = $OctopusParameters['Akeyless.Retrieve.Dynamic.OutputVariableName']\n$TIMEOUT_TEXT = $OctopusParameters['Akeyless.Retrieve.Dynamic.Timeout']\n$DYNAMIC_ARGS = $OctopusParameters['Akeyless.Retrieve.Dynamic.DynamicSecretArgs']\n$FIELD_VALUES = $OctopusParameters['Akeyless.Retrieve.Dynamic.FieldValues']\n$PRINT_VARIABLE_NAMES = $OctopusParameters['Akeyless.Retrieve.Dynamic.PrintVariableNames']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($AUTH_TOKEN)) {\n throw 'Required parameter Auth Token not specified'\n}\nif ([string]::IsNullOrWhiteSpace($SECRET_PATH)) {\n throw 'Required parameter Secret Path not specified'\n}\nif ([string]::IsNullOrWhiteSpace($PRINT_VARIABLE_NAMES)) {\n $PRINT_VARIABLE_NAMES = 'False'\n}\n\n$printNames = ($PRINT_VARIABLE_NAMES -eq 'True')\n$path = Normalize-AkeylessPath $SECRET_PATH\n$body = @{\n token = $AUTH_TOKEN\n name = $path\n}\n\nif (-not [string]::IsNullOrWhiteSpace($TIMEOUT_TEXT)) {\n $timeout = 0\n if (-not [int]::TryParse($TIMEOUT_TEXT, [ref]$timeout)) {\n throw \"Timeout must be an integer, got '$TIMEOUT_TEXT'\"\n }\n if ($timeout -gt 0) {\n $body.timeout = $timeout\n }\n}\n\n$argsList = @()\nif (-not [string]::IsNullOrWhiteSpace($DYNAMIC_ARGS)) {\n $argsList = @(($DYNAMIC_ARGS -Split \"`n\").Trim() | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })\n if ($argsList.Count -gt 0) {\n $body.args = $argsList\n }\n}\n\n$response = Invoke-AkeylessApi -GatewayUrl $GATEWAY_URL -Path 'get-dynamic-secret-value' -Body $body\nif ($null -eq $response) {\n throw \"Empty response retrieving dynamic secret '$path'\"\n}\n\n$fields = Parse-AkeylessFieldDefinitions -RawValue $FIELD_VALUES\n$created = Publish-AkeylessStructuredSecretResponse -Response $response -SecretPath $path -StepName $StepName -PrintVariableNames $printNames -OutputVariableName $OUTPUT_VARIABLE_NAME -Fields $fields\nWrite-Host \"Created $created output variables\"" }, "Parameters": [ { - "Name": "Akeyless.Retrieve.Dynamic.GatewayUrl", "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "Id": "10003000-0000-0000-0000-100030001001", + "Label": "Gateway URL", + "DefaultValue": "https://api.akeyless.io", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10003000-0000-0000-0000-100030001001", - "DefaultValue": "https://api.akeyless.io", - "Label": "Gateway URL" + "Name": "Akeyless.Retrieve.Dynamic.GatewayUrl" }, { - "Name": "Akeyless.Retrieve.Dynamic.AuthToken", "HelpText": "Authentication token from a previous Akeyless login step.", + "Id": "10003000-0000-0000-0000-100030001002", + "Label": "Auth Token", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "Sensitive" }, - "Id": "10003000-0000-0000-0000-100030001002", - "DefaultValue": "", - "Label": "Auth Token" + "Name": "Akeyless.Retrieve.Dynamic.AuthToken" }, { - "Name": "Akeyless.Retrieve.Dynamic.SecretPath", "HelpText": "Full path to the dynamic secret, e.g. `/production/database/dynamic`", + "Id": "10003000-0000-0000-0000-100030001003", + "Label": "Secret path", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10003000-0000-0000-0000-100030001003", - "DefaultValue": "", - "Label": "Secret path" + "Name": "Akeyless.Retrieve.Dynamic.SecretPath" }, { - "Name": "Akeyless.Retrieve.Dynamic.OutputVariableName", "HelpText": "Optional output variable name when the dynamic secret returns a single value.", + "Id": "10003000-0000-0000-0000-100030001004", + "Label": "Output variable name", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10003000-0000-0000-0000-100030001004", - "DefaultValue": "", - "Label": "Output variable name" + "Name": "Akeyless.Retrieve.Dynamic.OutputVariableName" }, { - "Name": "Akeyless.Retrieve.Dynamic.Timeout", "HelpText": "Optional timeout for dynamic secret provisioning.", + "Id": "10003000-0000-0000-0000-100030001005", + "Label": "Timeout (seconds)", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10003000-0000-0000-0000-100030001005", - "DefaultValue": "", - "Label": "Timeout (seconds)" + "Name": "Akeyless.Retrieve.Dynamic.Timeout" }, { - "Name": "Akeyless.Retrieve.Dynamic.DynamicSecretArgs", "HelpText": "Optional provisioning arguments, one per line.", + "Id": "10003000-0000-0000-0000-100030001006", + "Label": "Dynamic secret args", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "MultiLineText" }, - "Id": "10003000-0000-0000-0000-100030001006", - "DefaultValue": "", - "Label": "Dynamic secret args" + "Name": "Akeyless.Retrieve.Dynamic.DynamicSecretArgs" }, { - "Name": "Akeyless.Retrieve.Dynamic.FieldValues", "HelpText": "Choose specific response fields in the format FieldName | OutputVariableName.", + "Id": "10003000-0000-0000-0000-100030001007", + "Label": "Field names", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "MultiLineText" }, - "Id": "10003000-0000-0000-0000-100030001007", - "DefaultValue": "", - "Label": "Field names" + "Name": "Akeyless.Retrieve.Dynamic.FieldValues" }, { - "Name": "Akeyless.Retrieve.Dynamic.PrintVariableNames", "HelpText": "Write created output variable names to the task log.", + "Id": "10003000-0000-0000-0000-100030001008", + "Label": "Print output variable names", + "DefaultValue": "False", "DisplaySettings": { "Octopus.ControlType": "Checkbox" }, - "Id": "10003000-0000-0000-0000-100030001008", - "DefaultValue": "False", - "Label": "Print output variable names" + "Name": "Akeyless.Retrieve.Dynamic.PrintVariableNames" } ], "LastModifiedBy": "akeyless-community", - "LastModifiedAt": "2026-06-11T08:07:13.225Z", + "LastModifiedAt": "2026-06-17T04:32:01.621Z", "$Meta": { - "ExportedAt": "2026-06-11T08:07:13.225Z", + "ExportedAt": "2026-06-17T04:32:01.621Z", "OctopusVersion": "2024.4.0", "Type": "ActionTemplate" }, diff --git a/step-templates/akeyless-retrieve-rotated-secret.json b/step-templates/akeyless-retrieve-rotated-secret.json index 6fb75ab45..0e283f9aa 100644 --- a/step-templates/akeyless-retrieve-rotated-secret.json +++ b/step-templates/akeyless-retrieve-rotated-secret.json @@ -9,94 +9,94 @@ "Properties": { "Octopus.Action.Script.ScriptSource": "Inline", "Octopus.Action.Script.Syntax": "PowerShell", - "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Retrieve.Rotated.GatewayUrl']\n$AUTH_TOKEN = $OctopusParameters['Akeyless.Retrieve.Rotated.AuthToken']\n$SECRET_PATH = $OctopusParameters['Akeyless.Retrieve.Rotated.SecretPath']\n$OUTPUT_VARIABLE_NAME = $OctopusParameters['Akeyless.Retrieve.Rotated.OutputVariableName']\n$HOST_NAME = $OctopusParameters['Akeyless.Retrieve.Rotated.Host']\n$VERSION_TEXT = $OctopusParameters['Akeyless.Retrieve.Rotated.Version']\n$FIELD_VALUES = $OctopusParameters['Akeyless.Retrieve.Rotated.FieldValues']\n$PRINT_VARIABLE_NAMES = $OctopusParameters['Akeyless.Retrieve.Rotated.PrintVariableNames']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($AUTH_TOKEN)) {\n throw 'Required parameter Auth Token not specified'\n}\nif ([string]::IsNullOrWhiteSpace($SECRET_PATH)) {\n throw 'Required parameter Secret Path not specified'\n}\nif ([string]::IsNullOrWhiteSpace($PRINT_VARIABLE_NAMES)) {\n $PRINT_VARIABLE_NAMES = 'False'\n}\n\n$printNames = ($PRINT_VARIABLE_NAMES -eq 'True')\n$path = Normalize-AkeylessPath $SECRET_PATH\n$body = @{\n token = $AUTH_TOKEN\n names = $path\n}\n\nif (-not [string]::IsNullOrWhiteSpace($HOST_NAME)) {\n $body.host = $HOST_NAME.Trim()\n}\n\nif (-not [string]::IsNullOrWhiteSpace($VERSION_TEXT)) {\n $version = 0\n if (-not [int]::TryParse($VERSION_TEXT, [ref]$version)) {\n throw \"Version must be an integer, got '$VERSION_TEXT'\"\n }\n if ($version -gt 0) {\n $body.version = $version\n }\n}\n\n$response = Invoke-AkeylessApi -GatewayUrl $GATEWAY_URL -Path 'get-rotated-secret-value' -Body $body\nif ($null -eq $response) {\n throw \"Empty response retrieving rotated secret '$path'\"\n}\n\n$fields = Parse-AkeylessFieldDefinitions -RawValue $FIELD_VALUES\n$created = Publish-AkeylessStructuredSecretResponse -Response $response -SecretPath $path -StepName $StepName -PrintVariableNames $printNames -OutputVariableName $OUTPUT_VARIABLE_NAME -Fields $fields\nWrite-Host \"Created $created output variables\"" + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $apiPath = $Path.TrimStart('/')\n $uri = \"$base/$apiPath\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Retrieve.Rotated.GatewayUrl']\n$AUTH_TOKEN = $OctopusParameters['Akeyless.Retrieve.Rotated.AuthToken']\n$SECRET_PATH = $OctopusParameters['Akeyless.Retrieve.Rotated.SecretPath']\n$OUTPUT_VARIABLE_NAME = $OctopusParameters['Akeyless.Retrieve.Rotated.OutputVariableName']\n$HOST_NAME = $OctopusParameters['Akeyless.Retrieve.Rotated.Host']\n$VERSION_TEXT = $OctopusParameters['Akeyless.Retrieve.Rotated.Version']\n$FIELD_VALUES = $OctopusParameters['Akeyless.Retrieve.Rotated.FieldValues']\n$PRINT_VARIABLE_NAMES = $OctopusParameters['Akeyless.Retrieve.Rotated.PrintVariableNames']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($AUTH_TOKEN)) {\n throw 'Required parameter Auth Token not specified'\n}\nif ([string]::IsNullOrWhiteSpace($SECRET_PATH)) {\n throw 'Required parameter Secret Path not specified'\n}\nif ([string]::IsNullOrWhiteSpace($PRINT_VARIABLE_NAMES)) {\n $PRINT_VARIABLE_NAMES = 'False'\n}\n\n$printNames = ($PRINT_VARIABLE_NAMES -eq 'True')\n$path = Normalize-AkeylessPath $SECRET_PATH\n$body = @{\n token = $AUTH_TOKEN\n names = $path\n}\n\nif (-not [string]::IsNullOrWhiteSpace($HOST_NAME)) {\n $body.host = $HOST_NAME.Trim()\n}\n\nif (-not [string]::IsNullOrWhiteSpace($VERSION_TEXT)) {\n $version = 0\n if (-not [int]::TryParse($VERSION_TEXT, [ref]$version)) {\n throw \"Version must be an integer, got '$VERSION_TEXT'\"\n }\n if ($version -gt 0) {\n $body.version = $version\n }\n}\n\n$response = Invoke-AkeylessApi -GatewayUrl $GATEWAY_URL -Path 'get-rotated-secret-value' -Body $body\nif ($null -eq $response) {\n throw \"Empty response retrieving rotated secret '$path'\"\n}\n\n$fields = Parse-AkeylessFieldDefinitions -RawValue $FIELD_VALUES\n$created = Publish-AkeylessStructuredSecretResponse -Response $response -SecretPath $path -StepName $StepName -PrintVariableNames $printNames -OutputVariableName $OUTPUT_VARIABLE_NAME -Fields $fields\nWrite-Host \"Created $created output variables\"" }, "Parameters": [ { - "Name": "Akeyless.Retrieve.Rotated.GatewayUrl", "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "Id": "10006000-0000-0000-0000-100060001001", + "Label": "Gateway URL", + "DefaultValue": "https://api.akeyless.io", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10006000-0000-0000-0000-100060001001", - "DefaultValue": "https://api.akeyless.io", - "Label": "Gateway URL" + "Name": "Akeyless.Retrieve.Rotated.GatewayUrl" }, { - "Name": "Akeyless.Retrieve.Rotated.AuthToken", "HelpText": "Authentication token from a previous Akeyless login step.", + "Id": "10006000-0000-0000-0000-100060001002", + "Label": "Auth Token", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "Sensitive" }, - "Id": "10006000-0000-0000-0000-100060001002", - "DefaultValue": "", - "Label": "Auth Token" + "Name": "Akeyless.Retrieve.Rotated.AuthToken" }, { - "Name": "Akeyless.Retrieve.Rotated.SecretPath", "HelpText": "Full path to the rotated secret, e.g. `/production/database/rotated`", + "Id": "10006000-0000-0000-0000-100060001003", + "Label": "Secret path", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10006000-0000-0000-0000-100060001003", - "DefaultValue": "", - "Label": "Secret path" + "Name": "Akeyless.Retrieve.Rotated.SecretPath" }, { - "Name": "Akeyless.Retrieve.Rotated.OutputVariableName", "HelpText": "Optional output variable name when the rotated secret returns a single value.", + "Id": "10006000-0000-0000-0000-100060001004", + "Label": "Output variable name", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10006000-0000-0000-0000-100060001004", - "DefaultValue": "", - "Label": "Output variable name" + "Name": "Akeyless.Retrieve.Rotated.OutputVariableName" }, { - "Name": "Akeyless.Retrieve.Rotated.Host", "HelpText": "Optional linked-target host name.", + "Id": "10006000-0000-0000-0000-100060001005", + "Label": "Host", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10006000-0000-0000-0000-100060001005", - "DefaultValue": "", - "Label": "Host" + "Name": "Akeyless.Retrieve.Rotated.Host" }, { - "Name": "Akeyless.Retrieve.Rotated.Version", "HelpText": "Optional rotated secret version.", + "Id": "10006000-0000-0000-0000-100060001006", + "Label": "Secret version", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10006000-0000-0000-0000-100060001006", - "DefaultValue": "", - "Label": "Secret version" + "Name": "Akeyless.Retrieve.Rotated.Version" }, { - "Name": "Akeyless.Retrieve.Rotated.FieldValues", "HelpText": "Choose specific response fields in the format FieldName | OutputVariableName.", + "Id": "10006000-0000-0000-0000-100060001007", + "Label": "Field names", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "MultiLineText" }, - "Id": "10006000-0000-0000-0000-100060001007", - "DefaultValue": "", - "Label": "Field names" + "Name": "Akeyless.Retrieve.Rotated.FieldValues" }, { - "Name": "Akeyless.Retrieve.Rotated.PrintVariableNames", "HelpText": "Write created output variable names to the task log.", + "Id": "10006000-0000-0000-0000-100060001008", + "Label": "Print output variable names", + "DefaultValue": "False", "DisplaySettings": { "Octopus.ControlType": "Checkbox" }, - "Id": "10006000-0000-0000-0000-100060001008", - "DefaultValue": "False", - "Label": "Print output variable names" + "Name": "Akeyless.Retrieve.Rotated.PrintVariableNames" } ], "LastModifiedBy": "akeyless-community", - "LastModifiedAt": "2026-06-11T08:07:13.241Z", + "LastModifiedAt": "2026-06-17T04:32:01.636Z", "$Meta": { - "ExportedAt": "2026-06-11T08:07:13.241Z", + "ExportedAt": "2026-06-17T04:32:01.636Z", "OctopusVersion": "2024.4.0", "Type": "ActionTemplate" }, diff --git a/step-templates/akeyless-retrieve-static-secrets.json b/step-templates/akeyless-retrieve-static-secrets.json index 39d82d6c2..d3d0dbd91 100644 --- a/step-templates/akeyless-retrieve-static-secrets.json +++ b/step-templates/akeyless-retrieve-static-secrets.json @@ -9,105 +9,105 @@ "Properties": { "Octopus.Action.Script.ScriptSource": "Inline", "Octopus.Action.Script.Syntax": "PowerShell", - "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $uri = \"$base/$Path.TrimStart('/')\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Retrieve.Static.GatewayUrl']\n$AUTH_TOKEN = $OctopusParameters['Akeyless.Retrieve.Static.AuthToken']\n$SECRET_DEFINITIONS = $OctopusParameters['Akeyless.Retrieve.Static.Secrets']\n$FOLDER_PATH = $OctopusParameters['Akeyless.Retrieve.Static.FolderPath']\n$RETRIEVAL_METHOD = $OctopusParameters['Akeyless.Retrieve.Static.RetrievalMethod']\n$RECURSIVE_SEARCH = $OctopusParameters['Akeyless.Retrieve.Static.RecursiveSearch']\n$FIELD_VALUES = $OctopusParameters['Akeyless.Retrieve.Static.FieldValues']\n$PRINT_VARIABLE_NAMES = $OctopusParameters['Akeyless.Retrieve.Static.PrintVariableNames']\n$VERSION_TEXT = $OctopusParameters['Akeyless.Retrieve.Static.Version']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($AUTH_TOKEN)) {\n throw 'Required parameter Auth Token not specified'\n}\nif ([string]::IsNullOrWhiteSpace($RETRIEVAL_METHOD)) {\n $RETRIEVAL_METHOD = 'Single'\n}\nif ([string]::IsNullOrWhiteSpace($RECURSIVE_SEARCH)) {\n $RECURSIVE_SEARCH = 'False'\n}\nif ([string]::IsNullOrWhiteSpace($PRINT_VARIABLE_NAMES)) {\n $PRINT_VARIABLE_NAMES = 'False'\n}\n\n$version = 0\nif (-not [string]::IsNullOrWhiteSpace($VERSION_TEXT)) {\n if (-not [int]::TryParse($VERSION_TEXT, [ref]$version)) {\n throw \"Version must be a positive integer, got '$VERSION_TEXT'\"\n }\n}\n\n$printNames = ($PRINT_VARIABLE_NAMES -eq 'True')\n$recursive = ($RECURSIVE_SEARCH -eq 'True')\n$fields = Parse-AkeylessFieldDefinitions -RawValue $FIELD_VALUES\n$created = 0\n\nif ($RETRIEVAL_METHOD.ToUpper().Trim() -eq 'FOLDER') {\n if ([string]::IsNullOrWhiteSpace($FOLDER_PATH)) {\n throw 'Folder path is required when retrieval method is Folder'\n }\n\n $secretPaths = Get-AkeylessSecretsRecursively -GatewayUrl $GATEWAY_URL -Token $AUTH_TOKEN -FolderPath $FOLDER_PATH -Recursive $recursive\n foreach ($secretPath in $secretPaths) {\n $created += Publish-AkeylessSecretValue -GatewayUrl $GATEWAY_URL -Token $AUTH_TOKEN -SecretPath $secretPath -StepName $StepName -PrintVariableNames $printNames -Fields $fields -Version $version\n }\n}\nelse {\n $definitions = Parse-AkeylessSecretDefinitions -RawValue $SECRET_DEFINITIONS\n if ($definitions.Count -eq 0) {\n throw 'At least one secret path is required when retrieval method is Single or Multiple'\n }\n\n foreach ($definition in $definitions) {\n $created += Publish-AkeylessSecretValue -GatewayUrl $GATEWAY_URL -Token $AUTH_TOKEN -SecretPath $definition.Path -StepName $StepName -PrintVariableNames $printNames -OutputVariableName $definition.OutputVariableName -Fields $fields -Version $version\n }\n}\n\nWrite-Host \"Created $created output variables\"" + "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\nfunction Get-AkeylessApiErrorBody {\n param ($RequestError)\n\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 try { return ($rawResponse | ConvertFrom-Json) } catch { return $rawResponse }\n }\n return $null\n }\n\n return $RequestError.ErrorDetails.Message\n}\n\nfunction Format-AkeylessApiError {\n param (\n [string]$Action,\n [System.Management.Automation.ErrorRecord]$ErrorRecord\n )\n\n $message = \"An error occurred during $Action`: $($ErrorRecord.Exception.Message)\"\n $body = Get-AkeylessApiErrorBody -RequestError $ErrorRecord\n if ($null -ne $body) {\n if ($body.error) {\n $message += \"`n`tDetail: $($body.error)\"\n }\n elseif ($body.PSObject.Properties.Name -contains 'errors') {\n $message += \"`n`tDetail: $($body.errors -Join ',')\"\n }\n elseif ($body -is [string] -and -not [string]::IsNullOrWhiteSpace($body)) {\n $message += \"`n`tDetail: $body\"\n }\n }\n\n return $message\n}\n\nfunction Invoke-AkeylessApi {\n param (\n [Parameter(Mandatory = $true)][string]$GatewayUrl,\n [Parameter(Mandatory = $true)][string]$Path,\n [Parameter(Mandatory = $true)][hashtable]$Body\n )\n\n $base = $GatewayUrl.TrimEnd('/')\n $apiPath = $Path.TrimStart('/')\n $uri = \"$base/$apiPath\"\n $json = $Body | ConvertTo-Json -Depth 20 -Compress:$false\n\n try {\n return Invoke-RestMethod -Method Post -Uri $uri -Body $json -ContentType 'application/json'\n }\n catch {\n $detail = Format-AkeylessApiError -Action \"POST $Path\" -ErrorRecord $_\n Write-Error $detail -Category ConnectionError\n }\n}\n\nfunction Normalize-AkeylessPath {\n param ([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) {\n return $Path\n }\n\n $normalized = $Path.Trim()\n if (-not $normalized.StartsWith('/')) {\n $normalized = \"/$normalized\"\n }\n\n return $normalized\n}\n\nfunction ConvertTo-AkeylessOutputVariableName {\n param (\n [string]$SecretPath,\n [string]$FieldName = '',\n [string]$OverrideName = ''\n )\n\n if (-not [string]::IsNullOrWhiteSpace($OverrideName)) {\n return $OverrideName.Trim()\n }\n\n $base = (Normalize-AkeylessPath $SecretPath).Trim('/').Replace('/', '.')\n if ([string]::IsNullOrWhiteSpace($FieldName)) {\n return $base\n }\n\n return \"$base.$($FieldName.Trim())\"\n}\n\nfunction Get-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [int]$Version = 0\n )\n\n $path = Normalize-AkeylessPath $SecretPath\n $body = @{\n token = $Token\n names = @($path)\n }\n\n if ($Version -gt 0) {\n $body.version = $Version\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'get-secret-value' -Body $body\n if ($null -eq $response) {\n throw \"Empty response retrieving secret '$path'\"\n }\n\n if ($response.PSObject.Properties.Name -contains $path) {\n return $response.$path\n }\n\n foreach ($property in $response.PSObject.Properties) {\n if (-not [string]::IsNullOrWhiteSpace([string]$property.Value)) {\n return $property.Value\n }\n }\n\n throw \"Secret '$path' was not found in the API response\"\n}\n\nfunction Set-AkeylessSensitiveOutput {\n param (\n [string]$Name,\n [string]$Value,\n [string]$StepName,\n [bool]$PrintVariableNames\n )\n\n if ([string]::IsNullOrWhiteSpace($Name)) {\n throw 'Output variable name cannot be empty'\n }\n\n Set-OctopusVariable -Name $Name -Value $Value -Sensitive\n if ($PrintVariableNames) {\n Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$Name}\"\n }\n}\n\nfunction Publish-AkeylessSecretValue {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @(),\n [int]$Version = 0\n )\n\n $raw = Get-AkeylessSecretValue -GatewayUrl $GatewayUrl -Token $Token -SecretPath $SecretPath -Version $Version\n $created = 0\n $fieldsSpecified = ($Fields.Count -gt 0)\n\n if ($raw -is [string]) {\n $trimmed = $raw.Trim()\n if ($fieldsSpecified) {\n $parsed = $null\n try { $parsed = $trimmed | ConvertFrom-Json } catch {}\n if ($null -eq $parsed) {\n throw \"Secret '$SecretPath' is not JSON but field names were specified\"\n }\n $raw = $parsed\n }\n }\n\n if ($raw -is [pscustomobject] -or $raw -is [hashtable]) {\n $properties = if ($raw -is [hashtable]) { $raw.Keys } else { $raw.PSObject.Properties.Name }\n\n if ($fieldsSpecified) {\n foreach ($field in $Fields) {\n $fieldName = $field.Name\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n foreach ($fieldName in $properties) {\n $fieldValue = if ($raw -is [hashtable]) { $raw[$fieldName] } else { $raw.$fieldName }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n\n return $created\n }\n\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$raw) -StepName $StepName -PrintVariableNames $PrintVariableNames\n return 1\n}\n\nfunction Invoke-AkeylessListItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @()\n )\n\n $path = Normalize-AkeylessPath $FolderPath\n $items = @()\n $folders = @()\n $paginationToken = ''\n\n do {\n $body = @{\n token = $Token\n path = $path\n 'current-folder' = $true\n }\n\n if ($Types.Count -gt 0) {\n $body.type = $Types\n }\n if (-not [string]::IsNullOrWhiteSpace($paginationToken)) {\n $body.'pagination-token' = $paginationToken\n }\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'list-items' -Body $body\n if ($null -eq $response) {\n break\n }\n\n if ($null -ne $response.items) {\n $items += @($response.items | ForEach-Object { $_.item_name })\n }\n if ($null -ne $response.folders) {\n $folders += @($response.folders)\n }\n\n $paginationToken = ''\n if ($response.PSObject.Properties.Name -contains 'next_page') {\n $paginationToken = [string]$response.next_page\n }\n } while (-not [string]::IsNullOrWhiteSpace($paginationToken))\n\n return [pscustomobject]@{\n Items = $items\n Folders = $folders\n }\n}\n\nfunction Get-AkeylessFolderItems {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [string[]]$Types = @('static-secret')\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath -Types $Types\n return @($listing.Items)\n}\n\nfunction Get-AkeylessFolderChildren {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath\n )\n\n $listing = Invoke-AkeylessListItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $FolderPath\n return @($listing.Folders)\n}\n\nfunction Parse-AkeylessFieldDefinitions {\n param ([string]$RawValue)\n\n $fields = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $fields\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $name = $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($name)) {\n throw \"Unable to establish field name from: '$_'\"\n }\n $fields += [pscustomobject]@{\n Name = $name\n VariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $fields\n}\n\nfunction Parse-AkeylessSecretDefinitions {\n param ([string]$RawValue)\n\n $secrets = @()\n if ([string]::IsNullOrWhiteSpace($RawValue)) {\n return $secrets\n }\n\n @(($RawValue -Split \"`n\").Trim()) | ForEach-Object {\n if ([string]::IsNullOrWhiteSpace($_)) { return }\n $parts = ($_ -Split '\\|', 2)\n $path = Normalize-AkeylessPath $parts[0].Trim()\n if ([string]::IsNullOrWhiteSpace($path)) {\n throw \"Unable to establish secret path from: '$_'\"\n }\n $secrets += [pscustomobject]@{\n Path = $path\n OutputVariableName = if ($parts.Count -gt 1) { $parts[1].Trim() } else { '' }\n }\n }\n\n return $secrets\n}\n\nfunction Get-AkeylessSecretsRecursively {\n param (\n [string]$GatewayUrl,\n [string]$Token,\n [string]$FolderPath,\n [bool]$Recursive\n )\n\n $results = @()\n $folder = Normalize-AkeylessPath $FolderPath\n\n $items = Get-AkeylessFolderItems -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($item in $items) {\n $results += $item\n }\n\n if ($Recursive) {\n $children = Get-AkeylessFolderChildren -GatewayUrl $GatewayUrl -Token $Token -FolderPath $folder\n foreach ($child in $children) {\n $results += Get-AkeylessSecretsRecursively -GatewayUrl $GatewayUrl -Token $Token -FolderPath $child -Recursive $true\n }\n }\n\n return $results\n}\n\nfunction Complete-AkeylessLogin {\n param (\n [string]$GatewayUrl,\n [hashtable]$AuthBody,\n [string]$StepName\n )\n\n $response = Invoke-AkeylessApi -GatewayUrl $GatewayUrl -Path 'auth' -Body $AuthBody\n if ($null -eq $response -or [string]::IsNullOrWhiteSpace($response.token)) {\n throw 'Authentication succeeded but no token was returned'\n }\n\n Set-AkeylessSensitiveOutput -Name 'AkeylessAuthToken' -Value $response.token -StepName $StepName -PrintVariableNames $true\n Write-Host 'Authenticated to Akeyless successfully'\n}\n\nfunction Publish-AkeylessStructuredSecretResponse {\n param (\n [object]$Response,\n [string]$SecretPath,\n [string]$StepName,\n [bool]$PrintVariableNames,\n [string]$OutputVariableName = '',\n [array]$Fields = @()\n )\n\n $created = 0\n if ($Response -is [pscustomobject] -or $Response -is [hashtable]) {\n $properties = if ($Response -is [hashtable]) { @($Response.Keys) } else { @($Response.PSObject.Properties.Name) }\n if ($Fields.Count -gt 0) {\n foreach ($field in $Fields) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$field.Name] } else { $Response.$($field.Name) }\n if ($null -ne $fieldValue) {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $field.Name -OverrideName $field.VariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n else {\n $useSingleOutputName = (-not [string]::IsNullOrWhiteSpace($OutputVariableName)) -and ($properties.Count -eq 1)\n foreach ($fieldName in $properties) {\n $fieldValue = if ($Response -is [hashtable]) { $Response[$fieldName] } else { $Response.$fieldName }\n if ($null -ne $fieldValue) {\n $overrideName = if ($useSingleOutputName) { $OutputVariableName } else { '' }\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -FieldName $fieldName -OverrideName $overrideName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$fieldValue) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created++\n }\n }\n }\n }\n else {\n $variableName = ConvertTo-AkeylessOutputVariableName -SecretPath $SecretPath -OverrideName $OutputVariableName\n Set-AkeylessSensitiveOutput -Name $variableName -Value ([string]$Response) -StepName $StepName -PrintVariableNames $PrintVariableNames\n $created = 1\n }\n\n return $created\n}\n\nfunction Get-AwsHmacSha256Bytes {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n $hmac = New-Object System.Security.Cryptography.HMACSHA256 (, $Key)\n return $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message))\n}\n\nfunction Get-AwsHmacSha256Hex {\n param (\n [byte[]]$Key,\n [string]$Message\n )\n\n return ([BitConverter]::ToString((Get-AwsHmacSha256Bytes -Key $Key -Message $Message))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsSigningKey {\n param (\n [string]$SecretKey,\n [string]$DateStamp,\n [string]$Region,\n [string]$Service\n )\n\n $kSecret = [Text.Encoding]::UTF8.GetBytes(\"AWS4$SecretKey\")\n $kDate = Get-AwsHmacSha256Bytes -Key $kSecret -Message $DateStamp\n $kRegion = Get-AwsHmacSha256Bytes -Key $kDate -Message $Region\n $kService = Get-AwsHmacSha256Bytes -Key $kRegion -Message $Service\n return Get-AwsHmacSha256Bytes -Key $kService -Message 'aws4_request'\n}\n\nfunction Get-AwsSha256Hex {\n param ([string]$Text)\n\n $sha = [System.Security.Cryptography.SHA256]::Create()\n return ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)))).Replace('-', '').ToLowerInvariant()\n}\n\nfunction Get-AwsCredentialChain {\n $accessKeyId = $env:AWS_ACCESS_KEY_ID\n $secretAccessKey = $env:AWS_SECRET_ACCESS_KEY\n $sessionToken = $env:AWS_SESSION_TOKEN\n\n if (-not [string]::IsNullOrWhiteSpace($accessKeyId) -and -not [string]::IsNullOrWhiteSpace($secretAccessKey)) {\n return [pscustomobject]@{\n AccessKeyId = $accessKeyId.Trim()\n SecretAccessKey = $secretAccessKey.Trim()\n SessionToken = if ([string]::IsNullOrWhiteSpace($sessionToken)) { '' } else { $sessionToken.Trim() }\n }\n }\n\n try {\n $imdsToken = Invoke-RestMethod -Method Put -Uri 'http://169.254.169.254/latest/api/token' -Headers @{ 'X-aws-ec2-metadata-token-ttl-seconds' = '21600' } -TimeoutSec 2\n $roleName = Invoke-RestMethod -Uri 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n $roleCreds = Invoke-RestMethod -Uri \"http://169.254.169.254/latest/meta-data/iam/security-credentials/$roleName\" -Headers @{ 'X-aws-ec2-metadata-token' = $imdsToken } -TimeoutSec 2\n if ($null -eq $roleCreds -or [string]::IsNullOrWhiteSpace($roleCreds.AccessKeyId)) {\n throw 'EC2 instance metadata returned no IAM credentials'\n }\n\n return [pscustomobject]@{\n AccessKeyId = [string]$roleCreds.AccessKeyId\n SecretAccessKey = [string]$roleCreds.SecretAccessKey\n SessionToken = [string]$roleCreds.Token\n }\n }\n catch {\n throw \"AWS credentials were not found in environment variables or EC2 instance metadata: $($_.Exception.Message)\"\n }\n}\n\nfunction New-AwsIamCloudId {\n param (\n [string]$AccessKeyId,\n [string]$SecretAccessKey,\n [string]$SessionToken = '',\n [string]$Region = 'us-east-1',\n [string]$StsUrl = 'https://sts.amazonaws.com/'\n )\n\n $service = 'sts'\n $method = 'POST'\n $hostName = ([Uri]$StsUrl).Host\n $body = 'Action=GetCallerIdentity&Version=2011-06-15'\n $amzDate = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')\n $dateStamp = $amzDate.Substring(0, 8)\n $payloadHash = Get-AwsSha256Hex -Text $body\n\n $headers = [ordered]@{\n Host = $hostName\n 'Content-Type' = 'application/x-www-form-urlencoded; charset=utf-8'\n 'Content-Length' = [string]$body.Length\n 'X-Amz-Date' = $amzDate\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $headers['X-Amz-Security-Token'] = $SessionToken\n }\n\n $canonicalHeaders = ($headers.GetEnumerator() | ForEach-Object { \"$($_.Key.ToLowerInvariant()):$($_.Value)\" }) -join \"`n\"\n $signedHeaders = (($headers.Keys | ForEach-Object { $_.ToLowerInvariant() }) | Sort-Object) -join ';'\n $canonicalRequest = @(\n $method\n '/'\n ''\n \"$canonicalHeaders`n\"\n $signedHeaders\n $payloadHash\n ) -join \"`n\"\n\n $credentialScope = \"$dateStamp/$Region/$service/aws4_request\"\n $stringToSign = @(\n 'AWS4-HMAC-SHA256'\n $amzDate\n $credentialScope\n (Get-AwsSha256Hex -Text $canonicalRequest)\n ) -join \"`n\"\n\n $signingKey = Get-AwsSigningKey -SecretKey $SecretAccessKey -DateStamp $dateStamp -Region $Region -Service $service\n $signature = Get-AwsHmacSha256Hex -Key $signingKey -Message $stringToSign\n $authorization = \"AWS4-HMAC-SHA256 Credential=$AccessKeyId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature\"\n\n $requestHeaders = @{\n Authorization = @($authorization)\n 'Content-Length' = @([string]$body.Length)\n Host = @($hostName)\n 'Content-Type' = @('application/x-www-form-urlencoded; charset=utf-8')\n 'X-Amz-Date' = @($amzDate)\n }\n\n if (-not [string]::IsNullOrWhiteSpace($SessionToken)) {\n $requestHeaders['X-Amz-Security-Token'] = @($SessionToken)\n }\n\n $payload = [ordered]@{\n sts_request_method = $method\n sts_request_url = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($StsUrl))\n sts_request_body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($body))\n sts_request_headers = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($requestHeaders | ConvertTo-Json -Compress)))\n }\n\n return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress)))\n}\n\nfunction Resolve-AwsIamCloudId {\n param (\n [string]$CloudId,\n [string]$Region,\n [string]$StsUrl\n )\n\n if (-not [string]::IsNullOrWhiteSpace($CloudId)) {\n return $CloudId.Trim()\n }\n\n $credentials = Get-AwsCredentialChain\n return New-AwsIamCloudId -AccessKeyId $credentials.AccessKeyId -SecretAccessKey $credentials.SecretAccessKey -SessionToken $credentials.SessionToken -Region $Region -StsUrl $StsUrl\n}\n\n$GATEWAY_URL = $OctopusParameters['Akeyless.Retrieve.Static.GatewayUrl']\n$AUTH_TOKEN = $OctopusParameters['Akeyless.Retrieve.Static.AuthToken']\n$SECRET_DEFINITIONS = $OctopusParameters['Akeyless.Retrieve.Static.Secrets']\n$FOLDER_PATH = $OctopusParameters['Akeyless.Retrieve.Static.FolderPath']\n$RETRIEVAL_METHOD = $OctopusParameters['Akeyless.Retrieve.Static.RetrievalMethod']\n$RECURSIVE_SEARCH = $OctopusParameters['Akeyless.Retrieve.Static.RecursiveSearch']\n$FIELD_VALUES = $OctopusParameters['Akeyless.Retrieve.Static.FieldValues']\n$PRINT_VARIABLE_NAMES = $OctopusParameters['Akeyless.Retrieve.Static.PrintVariableNames']\n$VERSION_TEXT = $OctopusParameters['Akeyless.Retrieve.Static.Version']\n$StepName = $OctopusParameters['Octopus.Step.Name']\n\nif ([string]::IsNullOrWhiteSpace($GATEWAY_URL)) {\n $GATEWAY_URL = 'https://api.akeyless.io'\n}\nif ([string]::IsNullOrWhiteSpace($AUTH_TOKEN)) {\n throw 'Required parameter Auth Token not specified'\n}\nif ([string]::IsNullOrWhiteSpace($RETRIEVAL_METHOD)) {\n $RETRIEVAL_METHOD = 'Single'\n}\nif ([string]::IsNullOrWhiteSpace($RECURSIVE_SEARCH)) {\n $RECURSIVE_SEARCH = 'False'\n}\nif ([string]::IsNullOrWhiteSpace($PRINT_VARIABLE_NAMES)) {\n $PRINT_VARIABLE_NAMES = 'False'\n}\n\n$version = 0\nif (-not [string]::IsNullOrWhiteSpace($VERSION_TEXT)) {\n if (-not [int]::TryParse($VERSION_TEXT, [ref]$version)) {\n throw \"Version must be a positive integer, got '$VERSION_TEXT'\"\n }\n}\n\n$printNames = ($PRINT_VARIABLE_NAMES -eq 'True')\n$recursive = ($RECURSIVE_SEARCH -eq 'True')\n$fields = Parse-AkeylessFieldDefinitions -RawValue $FIELD_VALUES\n$created = 0\n\nif ($RETRIEVAL_METHOD.ToUpper().Trim() -eq 'FOLDER') {\n if ([string]::IsNullOrWhiteSpace($FOLDER_PATH)) {\n throw 'Folder path is required when retrieval method is Folder'\n }\n\n $secretPaths = Get-AkeylessSecretsRecursively -GatewayUrl $GATEWAY_URL -Token $AUTH_TOKEN -FolderPath $FOLDER_PATH -Recursive $recursive\n foreach ($secretPath in $secretPaths) {\n $created += Publish-AkeylessSecretValue -GatewayUrl $GATEWAY_URL -Token $AUTH_TOKEN -SecretPath $secretPath -StepName $StepName -PrintVariableNames $printNames -Fields $fields -Version $version\n }\n}\nelse {\n $definitions = Parse-AkeylessSecretDefinitions -RawValue $SECRET_DEFINITIONS\n if ($definitions.Count -eq 0) {\n throw 'At least one secret path is required when retrieval method is Single or Multiple'\n }\n\n foreach ($definition in $definitions) {\n $created += Publish-AkeylessSecretValue -GatewayUrl $GATEWAY_URL -Token $AUTH_TOKEN -SecretPath $definition.Path -StepName $StepName -PrintVariableNames $printNames -OutputVariableName $definition.OutputVariableName -Fields $fields -Version $version\n }\n}\n\nWrite-Host \"Created $created output variables\"" }, "Parameters": [ { - "Name": "Akeyless.Retrieve.Static.GatewayUrl", "HelpText": "The Akeyless API or Gateway URL. For SaaS, use https://api.akeyless.io.", + "Id": "10002000-0000-0000-0000-100020001001", + "Label": "Gateway URL", + "DefaultValue": "https://api.akeyless.io", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10002000-0000-0000-0000-100020001001", - "DefaultValue": "https://api.akeyless.io", - "Label": "Gateway URL" + "Name": "Akeyless.Retrieve.Static.GatewayUrl" }, { - "Name": "Akeyless.Retrieve.Static.AuthToken", "HelpText": "Authentication token from a previous Akeyless login step.", + "Id": "10002000-0000-0000-0000-100020001002", + "Label": "Auth Token", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "Sensitive" }, - "Id": "10002000-0000-0000-0000-100020001002", - "DefaultValue": "", - "Label": "Auth Token" + "Name": "Akeyless.Retrieve.Static.AuthToken" }, { - "Name": "Akeyless.Retrieve.Static.Secrets", "HelpText": "One secret per line in the format SecretPath | OutputVariableName.", + "Id": "10002000-0000-0000-0000-100020001003", + "Label": "Secret paths", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "MultiLineText" }, - "Id": "10002000-0000-0000-0000-100020001003", - "DefaultValue": "", - "Label": "Secret paths" + "Name": "Akeyless.Retrieve.Static.Secrets" }, { - "Name": "Akeyless.Retrieve.Static.FolderPath", "HelpText": "Akeyless folder to enumerate when retrieval method is Folder, e.g. `/production`", + "Id": "10002000-0000-0000-0000-100020001004", + "Label": "Folder path", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10002000-0000-0000-0000-100020001004", - "DefaultValue": "", - "Label": "Folder path" + "Name": "Akeyless.Retrieve.Static.FolderPath" }, { - "Name": "Akeyless.Retrieve.Static.RetrievalMethod", "HelpText": "Retrieve explicit secret paths, or enumerate static secrets in a folder.", + "Id": "10002000-0000-0000-0000-100020001005", + "Label": "Retrieval method", + "DefaultValue": "Single", "DisplaySettings": { "Octopus.SelectOptions": "Single|Explicit secret paths\nFolder|Enumerate folder", "Octopus.ControlType": "Select" }, - "Id": "10002000-0000-0000-0000-100020001005", - "DefaultValue": "Single", - "Label": "Retrieval method" + "Name": "Akeyless.Retrieve.Static.RetrievalMethod" }, { - "Name": "Akeyless.Retrieve.Static.RecursiveSearch", "HelpText": "When enumerating a folder, also retrieve secrets from subfolders.", + "Id": "10002000-0000-0000-0000-100020001006", + "Label": "Recursive retrieval", + "DefaultValue": "False", "DisplaySettings": { "Octopus.ControlType": "Checkbox" }, - "Id": "10002000-0000-0000-0000-100020001006", - "DefaultValue": "False", - "Label": "Recursive retrieval" + "Name": "Akeyless.Retrieve.Static.RecursiveSearch" }, { - "Name": "Akeyless.Retrieve.Static.FieldValues", "HelpText": "For JSON secrets, choose fields in the format FieldName | OutputVariableName.", + "Id": "10002000-0000-0000-0000-100020001007", + "Label": "Field names", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "MultiLineText" }, - "Id": "10002000-0000-0000-0000-100020001007", - "DefaultValue": "", - "Label": "Field names" + "Name": "Akeyless.Retrieve.Static.FieldValues" }, { - "Name": "Akeyless.Retrieve.Static.Version", "HelpText": "Optional static secret version to retrieve. Leave blank for latest.", + "Id": "10002000-0000-0000-0000-100020001008", + "Label": "Secret version", + "DefaultValue": "", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" }, - "Id": "10002000-0000-0000-0000-100020001008", - "DefaultValue": "", - "Label": "Secret version" + "Name": "Akeyless.Retrieve.Static.Version" }, { - "Name": "Akeyless.Retrieve.Static.PrintVariableNames", "HelpText": "Write created output variable names to the task log.", + "Id": "10002000-0000-0000-0000-100020001009", + "Label": "Print output variable names", + "DefaultValue": "False", "DisplaySettings": { "Octopus.ControlType": "Checkbox" }, - "Id": "10002000-0000-0000-0000-100020001009", - "DefaultValue": "False", - "Label": "Print output variable names" + "Name": "Akeyless.Retrieve.Static.PrintVariableNames" } ], "LastModifiedBy": "akeyless-community", - "LastModifiedAt": "2026-06-11T08:07:13.221Z", + "LastModifiedAt": "2026-06-17T04:32:01.617Z", "$Meta": { - "ExportedAt": "2026-06-11T08:07:13.221Z", + "ExportedAt": "2026-06-17T04:32:01.617Z", "OctopusVersion": "2024.4.0", "Type": "ActionTemplate" },