Generate and set tenant variable on first deploy

In one of our deployment solutions, we have a project being deployed to hundreds of different tenants. We are now in the situation where we would like to add a new tenant variable, containing a random secret for each tenant. This variable is to be used later in the deploy process. This will also be relevant for adding new tenants later on.

I picture adding an initial step to the deploy process - something like: If secret tenant variable is empty, generate new secret and save to tenant variable. Then this variable can be used later on in the deploy process. Is something like this easy achievable?

Another option could be to have a runbook to generate and set the specific tenant variable. But the issue still remains: How can I easily set a tenant variable either from a deploy process or a runbook?

I have found this example on how to programmatically update tenant variables using the API/Octopus Client. But it appears to be a bit cumbersome. One could believe that when I’m already in a deploy process, I am in the “context” of a given tenant and aught to have easy access to the tenant’s variables. But I haven’t found any examples on how to do this.

If somebody could point me in the right direction, that would be much appreciated.

Hi Julian,

Thanks for the question!

The example API script that you found is the best starting point for updating the tenant variables. While you can read and access variables during the deployment process, there’s no access for editing them and persisting that value back to the project or tenant without using the API.

Another option for using the API script is to script out the secret creation and save it for all of your existing tenants at once instead of during a deployment or runbook. Then you would only need to set the value for newly created tenants, which could be set manually with other variable values or you could also use a runbook to create the value and save it for the tenant.

I hope that helps. Let me know if you have any other questions.

Best,
Ryan

Thank you for the quick response Ryan.

Your answer is indeed helpful. Knowing what limitations exists saves us from wasting time going down that path. I’ll look into a solution using the API.

In case anybody else might stumble upon this looking for a solution, here is the function we ended up using:

# Must reference Octopus.Client.dll prior to calling this function
# => https://octopus.com/docs/octopus-rest-api/octopus.client/getting-started
# Inspired by: https://help.octopus.com/t/setting-a-variable-on-a-tenant-using-powershell/622/2
function Update-TenantLibraryVariable {
    [CmdletBinding(SupportsShouldProcess=$true)]
    param (
        [string]$octopusUrl = "https://octopus",
        [Parameter(Mandatory=$true)]
        [string]$apiKey,
        [Parameter(Mandatory=$true)]
        [string]$tenantName,
        [Parameter(Mandatory=$true)]
        [string]$variableSetName,
        [Parameter(Mandatory=$true)]
        [string]$variableName,
        [Parameter(Mandatory=$true)]
        [string]$newValue,
        [switch]$force = $false
    )
    
    process {
        $octopusServer = New-Object Octopus.Client.OctopusServerEndpoint $octopusUrl,$apiKey 
        $octopusRepository = New-Object Octopus.Client.OctopusRepository $octopusServer 
        $tenantEditor = $octopusRepository.Tenants.CreateOrModify($tenantName)

        $libraryVariables = $tenantEditor.Variables.Instance.LibraryVariables
        $variableSet = $libraryVariables.Values | Where-Object -Property LibraryVariableSetName -EQ $variableSetName
        $variable = $variableSet.Templates | Where-Object -Property Name -EQ $variableName
        
        $existingValue = $variableSet.Variables[$variable.Id].Value
        # Additional check on "N/A" to handle variable set default value
        $noExistingValue = [string]::IsNullOrWhiteSpace($existingValue) -or ($existingValue -eq "N/A")
        if ($noExistingValue -or $force) {
        	# Existing value is automatically masked by Octopus
            Write-Host "Changing $variableName for $tenantName from '$existingValue' to '********'"
            $variableSet.Variables[$variable.Id] = $newValue
            
            # Possible to run with -WhatIf flag when doing local testing
            if ($PSCmdlet.ShouldProcess("TenantEditor", "Save()")) {
                $tenantEditor.Save() | Out-Null
            }
        }
        else {
            Write-Error "$variableName already has a value for $tenantName, and no -force parameter was specified"
        }
    }
}

We have added this as a part of a runbook in our project (with additional functionality for generating the tenant secret passed into newValue). This made it easy for us both to generate the required secret for all existing tenants, as well as having it handy for when adding new tenants.

That’s great, Julian! Thanks for sharing.

This topic was automatically closed 31 days after the last reply. New replies are no longer allowed.