Unable to update sensitive variables (Common and Projects Template scope) for tenants via REST API

Hello,

We are not able to update sensitive variables (Common and Projects Template scope) for tenants via REST API. Here is some info:

Case:
We want to update variables (Common Tenant template and Project Template for specified tenant.
We get a some kind of template via Invoke-RestMethod:

function Get-TenantVariables
{
  <#
    .Synopsis
      Get variables for specified tenant
    .DESCRIPTION
      Get variables for specified tenant
    .EXAMPLE
      Get-TenantVariables -TenantName 'New Tenant Template' -OctopusAPIKey 'API-JC92IPF2PMJUHYM30O1QWRSDOA'
    .EXAMPLE
      Get-TenantVariables -TenantName 'New Tenant Template' -OctopusAPIKey 'API-JC92IPF2PMJUHYM30O1QWRSDOA' | ConvertTo-Json -Depth 10 | Out-File tenantvars.json
    .LINK
      https://octopus.com/docs/octopus-rest-api/examples/tenants/update-tenant-variable
  #>

  param (
      [string][Parameter(Mandatory=$false)]$OctopusURL='http://localhost',
      [string][Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]$TenantName,
      [string][Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]$OctopusAPIKey,
      [switch]$ToJsonFile
  )

  # Define working variables
  $header = @{ "X-Octopus-ApiKey" = $OctopusAPIKey }

  # Get Tenant
  $tenantsSearch = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/tenants?name=$TenantName" -Headers $header)
  $tenant = $tenantsSearch.Items | Select-Object -First 1
  if(!$tenant) {Write-Output "Tenant '$TenantName' doesn't exist"; exit}
  $variables = (Invoke-RestMethod -Method Get -Uri "$OctopusURL/api/tenants/$($tenant.Id)/variables" -Headers $header)
  return $variables

}
Get-TenantVariables -TenantName 'New Tenant Template' -OctopusAPIKey 'API-XXX' | ConvertTo-Json -Depth 10 | Out-File tenantvars.json

Then we change values in the template (based on Update tenant variables - Octopus Deploy)

function Update-TenantVariables
{
  <#
    .Synopsis
      Update variables for specified tenant
    .DESCRIPTION
      Update variables for specified tenant
    .EXAMPLE
      Update-TenantVariables -TenantName 'New Tenant Template' -OctopusAPIKey 'API-XXX' -TenantVariablesFile tenantvars.json  -TenantConfig tenantconfig.json
    .LINK
      https://octopus.com/docs/octopus-rest-api/examples/tenants/update-tenant-variable
  #>

  param (
      [string][Parameter(Mandatory=$false)]$OctopusURL='http://localhost',
      [string][Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]$TenantName,
      [string][Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]$OctopusAPIKey,
      [string][Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]$TenantVariablesFile,
      [string][Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]$TenantConfig

  )

  # Define working variables
  $header = @{ "X-Octopus-ApiKey" = $OctopusAPIKey }

  # Get Tenant
  $tenantsSearch = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/tenants?name=$TenantName" -Headers $header)
  $tenant = $tenantsSearch.Items | Select-Object -First 1
  if(!$tenant) {Write-Output "Tenant '$TenantName' doesn't exist"; exit}
  $variables = Get-Content $TenantVariablesFile | ConvertFrom-Json -Depth 10
  $config = Get-Content $TenantConfig | ConvertFrom-Json -Depth 10

  # Update variables
  $variables.TenantId = $tenant.Id
  $variables.TenantName = $TenantName

# Some Project
  $variables.ProjectVariables.'Projects-11'.Variables.'Environments-11'.'1aad00c3-e723-456e-8c73-809df6fb8aa5' = $config.SomeVar
  $variables.ProjectVariables.'Projects-11'.Variables.'Environments-12'.'1aad00c3-e723-456e-8c73-809df6fb8aa5' = $config.SomeVar
  $variables.ProjectVariables.'Projects-11.Variables.'Environments-13'.'1aad00c3-e723-456e-8c73-809df6fb8aa5' = $config.SomeVar
  $variables.ProjectVariables.'Projects-11'.Variables.'Environments-11'.'2aad00c3-e723-456e-8c73-809df6fb8aa5' = $config.SensitiveVar
  $variables.ProjectVariables.'Projects-11'.Variables.'Environments-12'.'2aad00c3-e723-456e-8c73-809df6fb8aa5' = $config.SentitiveVar
  $variables.ProjectVariables.'Projects-11.Variables.'Environments-13'.'2aad00c3-e723-456e-8c73-809df6fb8aa5' = $config.SensitiveVar

  # Some Variable set
  $variables.LibraryVariables.'LibraryVariableSets-1'.Variables.'e9f6b010-0804-4b40-a32e-39ffa4429ea7' = $config.SensitiveVar

  # Links
  $variables.Links.Self = "/api/tenants/$($tenant.Id)/variables"
  $variables.Links.Tenant = "/api/tenants/$($tenant.Id)"

  # Update the variables with the new value
  Invoke-RestMethod -Method Put -Uri "$octopusURL/api/tenants/$($tenant.Id)/variables" -Headers $header -Body ($variables | ConvertTo-Json -Depth 10)
}
Update-TenantVariables -TenantName 'New Tenant Template' -OctopusAPIKey 'API-XXX' -TenantVariablesFile tenantvars.json  -TenantConfig tenantconfig.json

Problem:
All variables (Library and Project Variables) are updated w/o issues except sensitive variables. They are displayed in Octopus UI as a plaintext.

Few notes:
$tenantconfig.json file is:

{
  "SomeVar": "simple var",
  "SensitiveVar": "some secret"
}

When I get variables from any tenant, sensitive variables in json file looks like:

{
...
  "Environments-11": {
          "2aad00c3-e723-456e-8c73-809df6fb8aa5": {
            "HasValue": true,
            "NewValue": null
          }
  }
...

}

I tried to write something like above w/o any luck:

$variables.ProjectVariables.'Projects-11.Variables.'Environments-13'.'2aad00c3-e723-456e-8c73-809df6fb8aa5' = @{"HasValue"= $true; "NewValue" = $config.SensitiveVar}

Octopus version: 2018.10.1 LTS

Is it possible to update sensitive variables within POST API call to “/api/tenants/$($tenant.Id)/variables”? ANy other suggestions?

P.S.
I read Unable to update sensitive varibale in octopus using powershell script - #17 by capanusha193, but can’t map it to my case when I need to update variables for specific tenant (both Library and Project)

Hi @ksandr.matveyev,

Sorry for the delay in getting back to you.
After looking at your script, I ended up grabbing the script from our examples page that you referenced: Update tenant variables - Octopus Deploy

I then worked backwards - to make it compatible with 2018.x.x

This worked for me - updating both normal and sensitive variables.

$ErrorActionPreference = "Stop";

# Define working variables
$octopusURL = "http://localhost"
$octopusAPIKey = "API-JDRYE5REIU2QXXXXXXXXX"
$header = @{ "X-Octopus-ApiKey" = $octopusAPIKey }

$spaceName = "Default" # Name of the Space
$tenantName = "TenantA" # The tenant name
$variableTemplateName = "SensitiveVar" # Choose the template Name
$newValue = "Brand New sensitive variable" # Choose a new variable value, assumes same per environment

# Get space
#$space = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/spaces/all" -Headers $header) | Where-Object {$_.Name -eq $spaceName}

# Get Tenant
$tenantsSearch = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/tenants?name=$tenantName" -Headers $header)
$tenant = $tenantsSearch.Items | Select-Object -First 1


# Get Tenant Variables
$variables = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/tenants/$($tenant.Id)/variables" -Headers $header)

# Get project templates
$projects = $variables.ProjectVariables | Get-Member | Where-Object {$_.MemberType -eq "NoteProperty"} | Select-Object -ExpandProperty "Name"

# Loop through each project template
foreach ($projectKey in $projects)
{
    # Get connected project
    $project = $variables.ProjectVariables.$projectKey
    $projectName = $project.ProjectName
    Write-Host "Working on Project: $projectName ($projectKey)"

    # Get Project template ID
    $variableTemplateId = ($project.Templates | Where-Object Name -eq $variableTemplateName | Select-Object -First 1).Id
    
    if($null -ne $variableTemplateId) {

        Write-Host "Found templateId for Template: $variableTemplateName = $variableTemplateId"
        $projectConnectedEnvironments = $project.Variables | Get-Member | Where-Object {$_.MemberType -eq "NoteProperty"} | Select-Object -ExpandProperty "Name"

        # Loop through each of the connected environments variables
        foreach($envKey in $projectConnectedEnvironments) {
            
            # Find variable which matches the current connected environment
            $currentValue = $project.Variables.$envKey.$variableTemplateId
            
            # If null / only default value exists, add new value in 
            if($null -eq $currentValue ) {
                Write-Host "No value found for $variableTemplateName, adding in new value = $newValue for Environment '$envKey' "
                $project.Variables.$envKey | Add-Member -MemberType NoteProperty -Name $variableTemplateId -Value $newValue
            } 
            else {
                # Get Current value
                Write-Host "Found $variableTemplateName in Environment '$envKey' with Value = $currentValue"
                # Set the new value for this connected environment
                $project.Variables.$envKey.$variableTemplateId = $newValue
            }        
        }
    }
    else {
        Write-Host "Couldnt find project template: $variableTemplateName for project $projectName"
    }

}
# Update the variables with the new value
Invoke-RestMethod -Method Put -Uri "$octopusURL/api/tenants/$($tenant.Id)/variables" -Headers $header -Body ($variables | ConvertTo-Json -Depth 10)

I hope this gets you unstuck, but please reach out again if this doesn’t work for you.

Regards,

Dane

Hi @dane.falvo ,
Unfortunately, it still doesn’t work for Project variables:

But, I’m able to set vars in Library variable sets with:

# works
if($null -eq $variables.LibraryVariables.'LibraryVariableSets-11'.Variables.'uuid'){
  Write-Output "Variable doesn't exist. Create new"
  $newvalue=[PSCustomObject]@{NewValue=$newpassword}
  $variables.LibraryVariables.'LibraryVariableSets-11'.Variables | 
      Add-Member -MemberType NoteProperty -Name "uuid" -Value $newvalue
} else {
  Write-Output "Variable exists. Updating"
  $variables.LibraryVariables.'LibraryVariableSets-11'.Variables.'uuid' = $newpassword
  
}

# doesn't work
if($null -eq $variables.ProjectVariables.'Projects-111'.Variables.'Environments-11'.'uuid'){
  Write-Output "Variable doesn't exist. Create new"
  $newvalue=[PSCustomObject]@{NewValue=$newpassword}
  $variables.ProjectVariables.'Projects-111'.Variables.'Environments-11' | 
      Add-Member -MemberType NoteProperty -Name "'uuid'" -Value $newvalue
} else {
  Write-Output "Variable exists. Updating"
  $variables.ProjectVariables.'Projects-111'.Variables.'Environments-11'.'uuid' = $newpassword
  
}

# Update the variables with the new value
Invoke-RestMethod -Method Put -Uri "$octopusURL/api/tenants/$($tenant.Id)/variables" -Headers $header -Body ($variables | ConvertTo-Json -Depth 10)

It has been fixed:

# wrong
Add-Member -MemberType NoteProperty -Name "'uuid'" -Value $newvalue
# right
Add-Member -MemberType NoteProperty -Name "uuid" -Value $newvalue
  • customPSObject is mandatory for new sensitive values. Otherwise, they appear as a plaintext for me

Excellent news @ksandr.matveyev!

Thanks for letting me know.

Dane.