Azure AppConfig KV - Retrieve Values

Octopus.AzurePowerShell exported 2023-11-30 by harrisonmeister belongs to ‘Azure’ category.

This step retrieves one or more key/values from an Azure App Configuration store and creates output variables for each value retrieved. These values can be used in other deployment or runbook process steps.

You can retrieve individual keys that match a specific name and you can choose a custom output variable name for each key.

Wildcard search is also supported using the * notation in the Key Names parameter. Note: Combining a wildcard search with custom output variable names is not supported.

Authentication is performed using an Azure Service Principal.


Required:

  • An Azure account with permissions to retrieve key/values from the Azure App Config store.
  • The az CLI on the target or worker. If the CLI can’t be found, the step will fail.

Notes:

  • Tested on Octopus 2024.1 using az version 2.38.0
  • Tested with both Windows PowerShell and PowerShell Core (on Linux).

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

Azure Account

Azure.AppConfig.KV.RetrieveValues.AzureAccount =

An Azure account with permissions to retrieve values from the Azure App Config store

Config Store Name

Azure.AppConfig.KV.RetrieveValues.ConfigStoreName =

The name of the Azure App Configuration store. Provide this or the Config store endpoint.

Config Store Endpoint

Azure.AppConfig.KV.RetrieveValues.ConfigStoreEndpoint =

The endpoint for the Azure App Configuration Store. Provide this or the Config store name.

Retrieval Method

Azure.AppConfig.KV.RetrieveValues.RetrievalMethod = all

Choose how the step calls the az cli. Either:

  • All: Retrieve all configuration values in one call
  • Individually: Retrieve configuration values in multiple calls; one for each key specified in the Key Names parameter.

Default: All

Key Names

Azure.AppConfig.KV.RetrieveValues.KeyNames =

Specify the names of the keys to be returned from Azure App Configuration in the format KeyName | OutputVariableName where:

  • KeyName is the key to retrieve. Wildcards are supported by adding * at the end of the key name.
  • OutputVariableName is the optional Octopus output variable name to store the key’s value in. If this value isn’t specified, an output name will be generated dynamically.

Note: Multiple keys can be retrieved by entering each one on a new line. Note: Combining a wildcard search with custom output variable names is not supported.

Labels

Azure.AppConfig.KV.RetrieveValues.Labels =

Labels are an attribute on keys. Provide one or more labels in the format label1,label2 to retrieve only selected keys that are tagged with those labels.

Note: You can include both label values and specify key names if you want.

Save sensitive output variables

Azure.AppConfig.KV.RetrieveValues.SaveAsSensitiveVariables = False

Set the Octopus output variables to Sensitive values. Default: False.

Suppress warnings

Azure.AppConfig.KV.RetrieveValues.SuppressWarnings = False

Suppress warnings from being written to the task log. For example, when a supplied key can’t be found in the Azure App Configuration store.

Treat Warnings as Errors

Azure.AppConfig.KV.RetrieveValues.TreatWarningsAsErrors = True

Treats warnings as errors. If enabled, the Suppress Warnings parameter is ignored.

Create AppSettings JSON

Azure.AppConfig.KV.RetrieveValues.CreateAppSettingsJson = False

Create an Azure App Service AppSettings JSON output variable called AppSettingsJson. This can be useful when using the Octopus Azure App Service step and you want to dynamically create the JSON app settings.

Azure.AppConfig.KV.RetrieveValues.PrintVariableNames = False

Write out the Octopus output variable names to the task log. Default: False.

Script body

Steps based on this template will execute the following PowerShell script.

$ErrorActionPreference = 'Stop'

# Variables
$global:AzureAppConfigStoreName = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.ConfigStoreName"]
$global:AzureAppConfigStoreEndpoint = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.ConfigStoreEndpoint"]
$global:AzureAppConfigRetrievalMethod = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.RetrievalMethod"]
$ConfigStoreKeyNames = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.KeyNames"]
$global:ConfigStoreLabels = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.Labels"]
$PrintVariableNames = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.PrintVariableNames"]
$SaveValuesAsSensitiveVariables = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.SaveAsSensitiveVariables"] -ieq "True"
$global:SuppressWarnings = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.SuppressWarnings"] -ieq "True"
$global:TreatWarningsAsErrors = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.TreatWarningsAsErrors"] -ieq "True"
$global:CreateAppSettingsJson = $OctopusParameters["Azure.AppConfig.KV.RetrieveValues.CreateAppSettingsJson"] -ieq "True"

# Validation
if ([string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreName) -and [string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreEndpoint)) {
    throw "Either parameter ConfigStoreName or ConfigStoreEndpoint not specified"
}

if ([string]::IsNullOrWhiteSpace($global:AzureAppConfigRetrievalMethod)) {
    throw "Required parameter Azure.AppConfig.KV.RetrieveValues.RetrievalMethod not specified"
}

if ([string]::IsNullOrWhiteSpace($ConfigStoreKeyNames) -and [string]::IsNullOrWhiteSpace($global:ConfigStoreLabels)) {
    throw "Either Azure.AppConfig.KV.RetrieveValues.KeyNames or Azure.AppConfig.KV.RetrieveValues.Labels not specified"
}

$RetrieveAllKeys = $global:AzureAppConfigRetrievalMethod -ieq "all"
$global:ConfigStoreParameters = ""
if (-not [string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreName)) {
    $global:ConfigStoreParameters += " --name ""$global:AzureAppConfigStoreName"""
}
if (-not [string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreEndpoint)) {
    $global:ConfigStoreParameters += " --endpoint ""$global:AzureAppConfigStoreEndpoint"""
}

### Helper functions
function Test-ForAzCLI() {
    $oldPreference = $ErrorActionPreference
    $ErrorActionPreference = "Stop"
    try { 
        if (Get-Command "az") {
            return $True
        }
    }
    catch { 
        return $false
    }
    finally { 
        $ErrorActionPreference = $oldPreference 
    }
}

function Write-OctopusWarning(
    [string] $Message
) {
    if ($global:TreatWarningsAsErrors) {
        throw "Error: $($Message)"
    }
    else {
        if ($global:SuppressWarnings -eq $False) {
            Write-Warning -Message $Message
        }
        else {
            Write-Verbose -Message $Message
        }
    }
}


function Save-OctopusVariable(
    [string]$variableName, 
    [string]$variableValue) {

    $VariableParams = @{name = $variableName; Value = $variableValue } 
                    
    if ($SaveValuesAsSensitiveVariables) {
        $VariableParams.Sensitive = $True
    }

    Set-OctopusVariable @VariableParams

    $global:VariablesCreated += 1

    if ($global:CreateAppSettingsJson) {
        $global:AppSettingsVariables += [PsCustomObject]@{name = $variableName; value = $variableValue; slotSetting = $false }
    }

    if ($PrintVariableNames) {
        Write-Host "Created output variable: ##{Octopus.Action[$StepName].Output.$variableName}"
    }
}

function Find-AzureAppConfigMatchesFromKey(
    [string]$KeyName,
    [bool]$IsWildCard,
    [string]$VariableName,
    [PsCustomObject]$AppConfigValues) {

    if ($IsWildCard -eq $False) {
        Write-Verbose "Finding exact match for: $($keyName)"
        $matchingAppConfigKeys = $appConfigValues | Where-Object { $_.key -ieq $keyName }
        if ($null -eq $matchingAppConfigKeys -or $matchingAppConfigKeys.Count -eq 0) {
            Write-OctopusWarning "Unable to find a matching key in Azure App Config for: $($keyName)"
        }
        else {
            if ($matchingAppConfigKeys.Count -gt 1) {
                Write-OctopusWarning "Found multiple matching keys ($($matchingAppConfigKeys.Count)) in Azure App Config for: $($keyName). This is usually due to multiple values with labels"

                foreach ($matchingAppConfigKey in $matchingAppConfigKeys) {
                    Write-Verbose "Found match for $($keyName) $(if(![string]::IsNullOrWhiteSpace($matchingAppConfigKey.label)) {"(label: $($matchingAppConfigKey.label))"})"
                    $variableValue = $matchingAppConfigKey.value
                    
                    if ([string]::IsNullOrWhiteSpace($variableName)) {
                        $variableName = $keyName.Trim()
                    }
                    
                    if (![string]::IsNullOrWhiteSpace($matchingAppConfigKey.label)) {
                        $variableName = "$($keyName.Trim())-$($matchingAppConfigKey.label)"
                        Write-Verbose "Appending label to variable name to avoid duplicate output name: $variableName"
                    }

                    Save-OctopusVariable -variableName $variableName -variableValue $variableValue
                }
            } 
            else {
                $matchingAppConfigKey = $matchingAppConfigKeys | Select-Object -First 1

                Write-Verbose "Found match for $($keyName)"
                $variableValue = $matchingAppConfigKey.value
    
                if ([string]::IsNullOrWhiteSpace($variableName)) {
                    $variableName = "$($keyName.Trim())"
                }
    
                Save-OctopusVariable -variableName $variableName -variableValue $variableValue
            }
        }
    }
    else {
        Write-Verbose "Finding wildcard match for: $($keyName)"
        $matchingAppConfigKeys = @($appConfigValues | Where-Object { $_.key -ilike $keyName })
        if ($matchingAppConfigKeys.Count -eq 0) {
            Write-OctopusWarning "Unable to find any matching keys in Azure App Config for wildcard: $($keyName)"
        }
        else {
            foreach ($match in $matchingAppConfigKeys) {
                # Have to explicitly set variable Name here as its a wildcard match
                $variableName = $match.key
                $variableValue = $match.value
                Write-Verbose "Found wildcard match '$variableName' $(if(![string]::IsNullOrWhiteSpace($matchingAppConfigKey.content_type)) {"($($matchingAppConfigKey.content_type))"})"
                Save-OctopusVariable -variableName $variableName -variableValue $variableValue
            }
        }
    }
}

function Find-AzureAppConfigMatchesFromLabels() {
    
    Write-Verbose "Retrieving values matching labels: $($global:ConfigStoreLabels)"
    $command = "az appconfig kv list $($global:ConfigStoreParameters) --label ""$global:ConfigStoreLabels"" --auth-mode login"
            
    Write-Verbose "Invoking expression: $command"
    $appConfigResponse = Invoke-Expression -Command $command
    $ExitCode = $LastExitCode
    Write-Verbose "az exit code: $ExitCode"
    if ($ExitCode -ne 0) {
        throw "Error retrieving appsettings. ExitCode: $ExitCode"
    }

    if ([string]::IsNullOrWhiteSpace($appConfigResponse)) {
        Write-OctopusWarning "Null or empty response received from Azure App Configuration service"
    }
    else {
        $appConfigValues = $appConfigResponse | ConvertFrom-Json
        if ($appConfigValues.Count -eq 0) {
            Write-OctopusWarning "Unable to find any matching keys in Azure App Config for labels: $($global:ConfigStoreLabels)"
        }
        else {
            Write-Verbose "Finding match(es) for labels: $($global:ConfigStoreLabels)"
            foreach ($appConfigValue in $appConfigValues) {
                # Have to explicitly set variable Name here as its a match based on label alone
                $variableName = $appConfigValue.key
                Write-Verbose "Found label match '$($appConfigValue.key)' $(if(![string]::IsNullOrWhiteSpace($appConfigValue.content_type)) {"($($appConfigValue.content_type))"})"
                if (![string]::IsNullOrWhiteSpace($appConfigValue.label)) {
                    $variableName = "$($variableName)-$($appConfigValue.label)"
                    Write-Verbose "Appending label to variable name to avoid duplicate output name: $variableName"
                }
                $variableValue = $appConfigValue.value
                
                Save-OctopusVariable -variableName $variableName -variableValue $variableValue
            }
        }
    }
}

# Check if Az cli is installed.
$azCliAvailable = Test-ForAzCLI
if ($azCliAvailable -eq $False) {
    throw "Cannot find the Azure CLI (az) on the machine. This must be available to continue."	
}

$Keys = @()
$global:VariablesCreated = 0
$global:AppSettingsVariables = @()
$StepName = $OctopusParameters["Octopus.Step.Name"]

# Extract key names+optional custom variable name
@(($ConfigStoreKeyNames -Split "`n").Trim()) | ForEach-Object {
    if (![string]::IsNullOrWhiteSpace($_)) {
        Write-Verbose "Working on: '$_'"
        $keyDefinition = ($_ -Split "\|")
        $keyName = $keyDefinition[0].Trim()
        $KeyIsWildcard = $keyName.EndsWith("*")
        $variableName = $null
        if ($keyDefinition.Count -gt 1) {
            if ($KeyIsWildcard) {
                throw "Key definition: '$_' evaluated as a wildcard with a custom variable name. This is not supported."
            }
            $variableName = $keyDefinition[1].Trim()
        }

        if ([string]::IsNullOrWhiteSpace($keyName)) {
            throw "Unable to establish key name from: '$($_)'"
        }

        $key = [PsCustomObject]@{
            KeyName       = $keyName
            KeyIsWildcard = $KeyIsWildcard
            VariableName  = if (![string]::IsNullOrWhiteSpace($variableName)) { $variableName } else { "" }
        }
        $Keys += $key
    }
}

$LabelsArray = $global:ConfigStoreLabels -Split "," | Where-Object { [string]::IsNullOrWhiteSpace($_) -eq $False }

Write-Verbose "Azure AppConfig Retrieval Method: $global:AzureAppConfigRetrievalMethod"
if (![string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreName)) {
    Write-Verbose "Azure AppConfig Store Name: $global:AzureAppConfigStoreName"
}
if (![string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreEndpoint)) {
    Write-Verbose "Azure AppConfig Store Endpoint: $global:AzureAppConfigStoreEndpoint"
}
Write-Verbose "Save sensitive variables: $SaveValuesAsSensitiveVariables"
Write-Verbose "Treat warnings as errors: $global:TreatWarningsAsErrors"
Write-Verbose "Suppress warnings: $global:SuppressWarnings"
Write-Verbose "Print variables: $PrintVariableNames"
Write-Verbose "Keys to retrieve: $($Keys.Count)"
Write-Verbose "Labels to retrieve: $($LabelsArray.Count)"

$appConfigResponse = $null

# Retrieving all keys should be more performant, but may have a larger payload response.
if ($RetrieveAllKeys) {
    
    if ($Keys.Count -gt 0) {
        Write-Host "Retrieving ALL config values from store"
        $command = "az appconfig kv list $($global:ConfigStoreParameters) --all --auth-mode login"
    
        if (![string]::IsNullOrWhiteSpace($global:ConfigStoreLabels)) {
            $command += " --label ""$($global:ConfigStoreLabels)"" "
        }
        Write-Verbose "Invoking expression: $command"
        $appConfigResponse = Invoke-Expression -Command $command
        $ExitCode = $LastExitCode
        Write-Verbose "az exit code: $ExitCode"
        if ($ExitCode -ne 0) {
            throw "Error retrieving appsettings. ExitCode: $ExitCode"
        }
    
        if ([string]::IsNullOrWhiteSpace($appConfigResponse)) {
            Write-OctopusWarning "Null or empty response received from Azure App Configuration service"
        }
        else {
            $appConfigValues = $appConfigResponse | ConvertFrom-Json
        }

        foreach ($key in $Keys) {
            $keyName = $key.KeyName
            $KeyIsWildcard = $key.KeyIsWildcard
            $variableName = $key.VariableName
        
            Find-AzureAppConfigMatchesFromKey -KeyName $keyName -IsWildcard $KeyIsWildcard -VariableName $variableName -AppConfigValues $appConfigValues
        }
    }
    # Possible that ONLY labels have been provided
    elseif ($LabelsArray.Count -gt 0) {
        Find-AzureAppConfigMatchesFromLabels 
    }
}
# Loop through and get keys based on the supplied names
else {
    
    Write-Host "Retrieving keys based on supplied names..."
    if ($Keys.Count -gt 0) {
        foreach ($key in $Keys) {
            $keyName = $key.KeyName
            $KeyIsWildcard = $key.KeyIsWildcard
            $variableName = $key.VariableName

            if ([string]::IsNullOrWhiteSpace($variableName)) {
                $variableName = "$($keyName.Trim())"
            }

            Write-Verbose "Retrieving values matching key: $($keyName) from store"
            $command = "az appconfig kv list $($global:ConfigStoreParameters) --key ""$keyName"" --auth-mode login"
            
            if (![string]::IsNullOrWhiteSpace($global:ConfigStoreLabels)) {
                $command += " --label ""$($global:ConfigStoreLabels)"" "
            }
            Write-Verbose "Invoking expression: $command"

            $appConfigResponse = Invoke-Expression -Command $command
            $ExitCode = $LastExitCode
            Write-Verbose "az exit code: $ExitCode"
            if ($ExitCode -ne 0) {
                throw "Error retrieving appsettings. ExitCode: $ExitCode"
            }

            if ([string]::IsNullOrWhiteSpace($appConfigResponse)) {
                Write-OctopusWarning "Null or empty response received from Azure App Configuration service"
            }
            else {
                $appConfigValues = $appConfigResponse | ConvertFrom-Json
                if ($appConfigValues.Count -eq 0) {
                    Write-OctopusWarning "Unable to find a matching key in Azure App Config for: $($keyName)"
                }
                else {
                    Write-Verbose "Finding match(es) for: $($keyName)"
                    Find-AzureAppConfigMatchesFromKey -KeyName $keyName -IsWildcard $KeyIsWildcard -VariableName $variableName -AppConfigValues $appConfigValues
                }
            }
        }
    }
}

if ($global:AppSettingsVariables.Count -gt 0 -and $global:CreateAppSettingsJson) {
    Write-Verbose "Creating AppSettings JSON output variable"
    $AppSettingsJson = ($global:AppSettingsVariables | Sort-Object -Property * -Unique) | ConvertTo-Json -Compress -Depth 10
    if ($SaveValuesAsSensitiveVariables) {
        Set-OctopusVariable -Name "AppSettingsJson" -Value $AppSettingsJson -Sensitive
    }
    else {
        Set-OctopusVariable -Name "AppSettingsJson" -Value $AppSettingsJson 
    }
    $global:VariablesCreated += 1
    if ($PrintVariableNames) {
        Write-Host "Created output variable: ##{Octopus.Action[$StepName].Output.AppSettingsJson}"
    }
}

Write-Host "Created $global:VariablesCreated output variable(s)"

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "5c4fbed9-dbba-4139-8440-d8e27318772e",
  "Name": "Azure AppConfig KV - Retrieve Values",
  "Description": "This step retrieves one or more key/values from an Azure App Configuration store and creates [output variables](https://octopus.com/docs/projects/variables/output-variables) for each value retrieved. These values can be used in other deployment or runbook process steps.\n\nYou can retrieve individual keys that match a specific name and you can choose a custom output variable name for each key.\n\nWildcard search is also supported using the `*` notation in the **Key Names** parameter. Note: Combining a wildcard search with custom output variable names is not supported.\n\nAuthentication is performed using an Azure Service Principal.\n\n---\n\n**Required:** \n- An Azure account with permissions to retrieve key/values from the Azure App Config store.\n- The `az` CLI on the target or worker. If the CLI can't be found, the step will fail. \n\nNotes:\n\n- Tested on Octopus `2024.1` using az version `2.38.0`\n- Tested with both Windows PowerShell and PowerShell Core (on Linux).\n\n",
  "Version": 2,
  "ExportedAt": "2023-11-30T15:10:08.828Z",
  "ActionType": "Octopus.AzurePowerShell",
  "Author": "harrisonmeister",
  "Packages": [],
  "Parameters": [
    {
      "Id": "4bc809d3-71d5-4f05-ba0e-994ef6649db0",
      "Name": "Azure.AppConfig.KV.RetrieveValues.AzureAccount",
      "Label": "Azure Account",
      "HelpText": "An Azure account with permissions to retrieve values from the Azure App Config store",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "AzureAccount"
      }
    },
    {
      "Id": "2b53fffb-cef1-4c3b-a070-aa6448fe84b0",
      "Name": "Azure.AppConfig.KV.RetrieveValues.ConfigStoreName",
      "Label": "Config Store Name",
      "HelpText": "The name of the Azure App Configuration store. Provide this or the **Config store endpoint**.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "41f5fa14-c1ce-4a27-9e63-06419e96f095",
      "Name": "Azure.AppConfig.KV.RetrieveValues.ConfigStoreEndpoint",
      "Label": "Config Store Endpoint",
      "HelpText": "The endpoint for the Azure App Configuration Store. Provide this or the **Config store name**.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "39d76051-4ede-47dd-8139-57f3801d215e",
      "Name": "Azure.AppConfig.KV.RetrieveValues.RetrievalMethod",
      "Label": "Retrieval Method",
      "HelpText": "Choose how the step calls the az cli. Either:\n- `All`: Retrieve all configuration values in one call\n- `Individually`: Retrieve configuration values in multiple calls; one for each key specified in the **Key Names** parameter.\n\nDefault: `All`",
      "DefaultValue": "all",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "all|All\nindividual|Individually"
      }
    },
    {
      "Id": "02d67ef3-d990-499c-bdbd-226d583ccdc0",
      "Name": "Azure.AppConfig.KV.RetrieveValues.KeyNames",
      "Label": "Key Names",
      "HelpText": "Specify the names of the keys to be returned from Azure App Configuration in the format `KeyName | OutputVariableName` where:\n\n- `KeyName` is the key to retrieve. Wildcards are supported by adding `*` at the end of the key name.\n- `OutputVariableName` is the _optional_ Octopus [output variable](https://octopus.com/docs/projects/variables/output-variables) name to store the key's value in. *If this value isn't specified, an output name will be generated dynamically*.\n\n**Note:** Multiple keys can be retrieved by entering each one on a new line. Note: Combining a wildcard search with custom output variable names is not supported.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "b97ff62d-6b14-4baa-abc2-9065632f5118",
      "Name": "Azure.AppConfig.KV.RetrieveValues.Labels",
      "Label": "Labels",
      "HelpText": "Labels are an attribute on keys. Provide one or more labels in the format `label1,label2` to retrieve only selected keys that are tagged with those labels.\n\n**Note:** You can include both label values *and* specify key names if you want.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "a5dfafc4-743e-4a56-8c89-cd001dc73af3",
      "Name": "Azure.AppConfig.KV.RetrieveValues.SaveAsSensitiveVariables",
      "Label": "Save sensitive output variables",
      "HelpText": "Set the Octopus [output variables](https://octopus.com/docs/projects/variables/output-variables) to Sensitive values. Default: `False`.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "c93c377c-e557-4b8a-a918-ee0c47f80c1b",
      "Name": "Azure.AppConfig.KV.RetrieveValues.SuppressWarnings",
      "Label": "Suppress warnings",
      "HelpText": "Suppress warnings from being written to the task log. For example, when a supplied key can't be found in the Azure App Configuration store.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "c51f515f-c749-4c00-90b3-fdc09b2cd426",
      "Name": "Azure.AppConfig.KV.RetrieveValues.TreatWarningsAsErrors",
      "Label": "Treat Warnings as Errors",
      "HelpText": "Treats warnings as errors. If enabled, the **Suppress Warnings** parameter is ignored. ",
      "DefaultValue": "True",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "166f88c8-2fb0-4a3c-a0dc-c4087a5ebc76",
      "Name": "Azure.AppConfig.KV.RetrieveValues.CreateAppSettingsJson",
      "Label": "Create AppSettings JSON",
      "HelpText": "Create an Azure App Service AppSettings JSON output variable called `AppSettingsJson`. This can be useful when using the Octopus **Azure App Service** step and you want to dynamically create the JSON app settings.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "159858de-5a33-4111-a059-de261f687b08",
      "Name": "Azure.AppConfig.KV.RetrieveValues.PrintVariableNames",
      "Label": "Print output variable names",
      "HelpText": "Write out the Octopus [output variable](https://octopus.com/docs/projects/variables/output-variables) names to the task log. Default: `False`.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "OctopusUseBundledTooling": "False",
    "Octopus.Action.Azure.AccountId": "#{Azure.AppConfig.KV.RetrieveValues.AzureAccount}",
    "Octopus.Action.Script.ScriptBody": "$ErrorActionPreference = 'Stop'\n\n# Variables\n$global:AzureAppConfigStoreName = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.ConfigStoreName\"]\n$global:AzureAppConfigStoreEndpoint = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.ConfigStoreEndpoint\"]\n$global:AzureAppConfigRetrievalMethod = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.RetrievalMethod\"]\n$ConfigStoreKeyNames = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.KeyNames\"]\n$global:ConfigStoreLabels = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.Labels\"]\n$PrintVariableNames = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.PrintVariableNames\"]\n$SaveValuesAsSensitiveVariables = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.SaveAsSensitiveVariables\"] -ieq \"True\"\n$global:SuppressWarnings = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.SuppressWarnings\"] -ieq \"True\"\n$global:TreatWarningsAsErrors = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.TreatWarningsAsErrors\"] -ieq \"True\"\n$global:CreateAppSettingsJson = $OctopusParameters[\"Azure.AppConfig.KV.RetrieveValues.CreateAppSettingsJson\"] -ieq \"True\"\n\n# Validation\nif ([string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreName) -and [string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreEndpoint)) {\n    throw \"Either parameter ConfigStoreName or ConfigStoreEndpoint not specified\"\n}\n\nif ([string]::IsNullOrWhiteSpace($global:AzureAppConfigRetrievalMethod)) {\n    throw \"Required parameter Azure.AppConfig.KV.RetrieveValues.RetrievalMethod not specified\"\n}\n\nif ([string]::IsNullOrWhiteSpace($ConfigStoreKeyNames) -and [string]::IsNullOrWhiteSpace($global:ConfigStoreLabels)) {\n    throw \"Either Azure.AppConfig.KV.RetrieveValues.KeyNames or Azure.AppConfig.KV.RetrieveValues.Labels not specified\"\n}\n\n$RetrieveAllKeys = $global:AzureAppConfigRetrievalMethod -ieq \"all\"\n$global:ConfigStoreParameters = \"\"\nif (-not [string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreName)) {\n    $global:ConfigStoreParameters += \" --name \"\"$global:AzureAppConfigStoreName\"\"\"\n}\nif (-not [string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreEndpoint)) {\n    $global:ConfigStoreParameters += \" --endpoint \"\"$global:AzureAppConfigStoreEndpoint\"\"\"\n}\n\n### Helper functions\nfunction Test-ForAzCLI() {\n    $oldPreference = $ErrorActionPreference\n    $ErrorActionPreference = \"Stop\"\n    try { \n        if (Get-Command \"az\") {\n            return $True\n        }\n    }\n    catch { \n        return $false\n    }\n    finally { \n        $ErrorActionPreference = $oldPreference \n    }\n}\n\nfunction Write-OctopusWarning(\n    [string] $Message\n) {\n    if ($global:TreatWarningsAsErrors) {\n        throw \"Error: $($Message)\"\n    }\n    else {\n        if ($global:SuppressWarnings -eq $False) {\n            Write-Warning -Message $Message\n        }\n        else {\n            Write-Verbose -Message $Message\n        }\n    }\n}\n\n\nfunction Save-OctopusVariable(\n    [string]$variableName, \n    [string]$variableValue) {\n\n    $VariableParams = @{name = $variableName; Value = $variableValue } \n                    \n    if ($SaveValuesAsSensitiveVariables) {\n        $VariableParams.Sensitive = $True\n    }\n\n    Set-OctopusVariable @VariableParams\n\n    $global:VariablesCreated += 1\n\n    if ($global:CreateAppSettingsJson) {\n        $global:AppSettingsVariables += [PsCustomObject]@{name = $variableName; value = $variableValue; slotSetting = $false }\n    }\n\n    if ($PrintVariableNames) {\n        Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$variableName}\"\n    }\n}\n\nfunction Find-AzureAppConfigMatchesFromKey(\n    [string]$KeyName,\n    [bool]$IsWildCard,\n    [string]$VariableName,\n    [PsCustomObject]$AppConfigValues) {\n\n    if ($IsWildCard -eq $False) {\n        Write-Verbose \"Finding exact match for: $($keyName)\"\n        $matchingAppConfigKeys = $appConfigValues | Where-Object { $_.key -ieq $keyName }\n        if ($null -eq $matchingAppConfigKeys -or $matchingAppConfigKeys.Count -eq 0) {\n            Write-OctopusWarning \"Unable to find a matching key in Azure App Config for: $($keyName)\"\n        }\n        else {\n            if ($matchingAppConfigKeys.Count -gt 1) {\n                Write-OctopusWarning \"Found multiple matching keys ($($matchingAppConfigKeys.Count)) in Azure App Config for: $($keyName). This is usually due to multiple values with labels\"\n\n                foreach ($matchingAppConfigKey in $matchingAppConfigKeys) {\n                    Write-Verbose \"Found match for $($keyName) $(if(![string]::IsNullOrWhiteSpace($matchingAppConfigKey.label)) {\"(label: $($matchingAppConfigKey.label))\"})\"\n                    $variableValue = $matchingAppConfigKey.value\n                    \n                    if ([string]::IsNullOrWhiteSpace($variableName)) {\n                        $variableName = $keyName.Trim()\n                    }\n                    \n                    if (![string]::IsNullOrWhiteSpace($matchingAppConfigKey.label)) {\n                        $variableName = \"$($keyName.Trim())-$($matchingAppConfigKey.label)\"\n                        Write-Verbose \"Appending label to variable name to avoid duplicate output name: $variableName\"\n                    }\n\n                    Save-OctopusVariable -variableName $variableName -variableValue $variableValue\n                }\n            } \n            else {\n                $matchingAppConfigKey = $matchingAppConfigKeys | Select-Object -First 1\n\n                Write-Verbose \"Found match for $($keyName)\"\n                $variableValue = $matchingAppConfigKey.value\n    \n                if ([string]::IsNullOrWhiteSpace($variableName)) {\n                    $variableName = \"$($keyName.Trim())\"\n                }\n    \n                Save-OctopusVariable -variableName $variableName -variableValue $variableValue\n            }\n        }\n    }\n    else {\n        Write-Verbose \"Finding wildcard match for: $($keyName)\"\n        $matchingAppConfigKeys = @($appConfigValues | Where-Object { $_.key -ilike $keyName })\n        if ($matchingAppConfigKeys.Count -eq 0) {\n            Write-OctopusWarning \"Unable to find any matching keys in Azure App Config for wildcard: $($keyName)\"\n        }\n        else {\n            foreach ($match in $matchingAppConfigKeys) {\n                # Have to explicitly set variable Name here as its a wildcard match\n                $variableName = $match.key\n                $variableValue = $match.value\n                Write-Verbose \"Found wildcard match '$variableName' $(if(![string]::IsNullOrWhiteSpace($matchingAppConfigKey.content_type)) {\"($($matchingAppConfigKey.content_type))\"})\"\n                Save-OctopusVariable -variableName $variableName -variableValue $variableValue\n            }\n        }\n    }\n}\n\nfunction Find-AzureAppConfigMatchesFromLabels() {\n    \n    Write-Verbose \"Retrieving values matching labels: $($global:ConfigStoreLabels)\"\n    $command = \"az appconfig kv list $($global:ConfigStoreParameters) --label \"\"$global:ConfigStoreLabels\"\" --auth-mode login\"\n            \n    Write-Verbose \"Invoking expression: $command\"\n    $appConfigResponse = Invoke-Expression -Command $command\n    $ExitCode = $LastExitCode\n    Write-Verbose \"az exit code: $ExitCode\"\n    if ($ExitCode -ne 0) {\n        throw \"Error retrieving appsettings. ExitCode: $ExitCode\"\n    }\n\n    if ([string]::IsNullOrWhiteSpace($appConfigResponse)) {\n        Write-OctopusWarning \"Null or empty response received from Azure App Configuration service\"\n    }\n    else {\n        $appConfigValues = $appConfigResponse | ConvertFrom-Json\n        if ($appConfigValues.Count -eq 0) {\n            Write-OctopusWarning \"Unable to find any matching keys in Azure App Config for labels: $($global:ConfigStoreLabels)\"\n        }\n        else {\n            Write-Verbose \"Finding match(es) for labels: $($global:ConfigStoreLabels)\"\n            foreach ($appConfigValue in $appConfigValues) {\n                # Have to explicitly set variable Name here as its a match based on label alone\n                $variableName = $appConfigValue.key\n                Write-Verbose \"Found label match '$($appConfigValue.key)' $(if(![string]::IsNullOrWhiteSpace($appConfigValue.content_type)) {\"($($appConfigValue.content_type))\"})\"\n                if (![string]::IsNullOrWhiteSpace($appConfigValue.label)) {\n                    $variableName = \"$($variableName)-$($appConfigValue.label)\"\n                    Write-Verbose \"Appending label to variable name to avoid duplicate output name: $variableName\"\n                }\n                $variableValue = $appConfigValue.value\n                \n                Save-OctopusVariable -variableName $variableName -variableValue $variableValue\n            }\n        }\n    }\n}\n\n# Check if Az cli is installed.\n$azCliAvailable = Test-ForAzCLI\nif ($azCliAvailable -eq $False) {\n    throw \"Cannot find the Azure CLI (az) on the machine. This must be available to continue.\"\t\n}\n\n$Keys = @()\n$global:VariablesCreated = 0\n$global:AppSettingsVariables = @()\n$StepName = $OctopusParameters[\"Octopus.Step.Name\"]\n\n# Extract key names+optional custom variable name\n@(($ConfigStoreKeyNames -Split \"`n\").Trim()) | ForEach-Object {\n    if (![string]::IsNullOrWhiteSpace($_)) {\n        Write-Verbose \"Working on: '$_'\"\n        $keyDefinition = ($_ -Split \"\\|\")\n        $keyName = $keyDefinition[0].Trim()\n        $KeyIsWildcard = $keyName.EndsWith(\"*\")\n        $variableName = $null\n        if ($keyDefinition.Count -gt 1) {\n            if ($KeyIsWildcard) {\n                throw \"Key definition: '$_' evaluated as a wildcard with a custom variable name. This is not supported.\"\n            }\n            $variableName = $keyDefinition[1].Trim()\n        }\n\n        if ([string]::IsNullOrWhiteSpace($keyName)) {\n            throw \"Unable to establish key name from: '$($_)'\"\n        }\n\n        $key = [PsCustomObject]@{\n            KeyName       = $keyName\n            KeyIsWildcard = $KeyIsWildcard\n            VariableName  = if (![string]::IsNullOrWhiteSpace($variableName)) { $variableName } else { \"\" }\n        }\n        $Keys += $key\n    }\n}\n\n$LabelsArray = $global:ConfigStoreLabels -Split \",\" | Where-Object { [string]::IsNullOrWhiteSpace($_) -eq $False }\n\nWrite-Verbose \"Azure AppConfig Retrieval Method: $global:AzureAppConfigRetrievalMethod\"\nif (![string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreName)) {\n    Write-Verbose \"Azure AppConfig Store Name: $global:AzureAppConfigStoreName\"\n}\nif (![string]::IsNullOrWhiteSpace($global:AzureAppConfigStoreEndpoint)) {\n    Write-Verbose \"Azure AppConfig Store Endpoint: $global:AzureAppConfigStoreEndpoint\"\n}\nWrite-Verbose \"Save sensitive variables: $SaveValuesAsSensitiveVariables\"\nWrite-Verbose \"Treat warnings as errors: $global:TreatWarningsAsErrors\"\nWrite-Verbose \"Suppress warnings: $global:SuppressWarnings\"\nWrite-Verbose \"Print variables: $PrintVariableNames\"\nWrite-Verbose \"Keys to retrieve: $($Keys.Count)\"\nWrite-Verbose \"Labels to retrieve: $($LabelsArray.Count)\"\n\n$appConfigResponse = $null\n\n# Retrieving all keys should be more performant, but may have a larger payload response.\nif ($RetrieveAllKeys) {\n    \n    if ($Keys.Count -gt 0) {\n        Write-Host \"Retrieving ALL config values from store\"\n        $command = \"az appconfig kv list $($global:ConfigStoreParameters) --all --auth-mode login\"\n    \n        if (![string]::IsNullOrWhiteSpace($global:ConfigStoreLabels)) {\n            $command += \" --label \"\"$($global:ConfigStoreLabels)\"\" \"\n        }\n        Write-Verbose \"Invoking expression: $command\"\n        $appConfigResponse = Invoke-Expression -Command $command\n        $ExitCode = $LastExitCode\n        Write-Verbose \"az exit code: $ExitCode\"\n        if ($ExitCode -ne 0) {\n            throw \"Error retrieving appsettings. ExitCode: $ExitCode\"\n        }\n    \n        if ([string]::IsNullOrWhiteSpace($appConfigResponse)) {\n            Write-OctopusWarning \"Null or empty response received from Azure App Configuration service\"\n        }\n        else {\n            $appConfigValues = $appConfigResponse | ConvertFrom-Json\n        }\n\n        foreach ($key in $Keys) {\n            $keyName = $key.KeyName\n            $KeyIsWildcard = $key.KeyIsWildcard\n            $variableName = $key.VariableName\n        \n            Find-AzureAppConfigMatchesFromKey -KeyName $keyName -IsWildcard $KeyIsWildcard -VariableName $variableName -AppConfigValues $appConfigValues\n        }\n    }\n    # Possible that ONLY labels have been provided\n    elseif ($LabelsArray.Count -gt 0) {\n        Find-AzureAppConfigMatchesFromLabels \n    }\n}\n# Loop through and get keys based on the supplied names\nelse {\n    \n    Write-Host \"Retrieving keys based on supplied names...\"\n    if ($Keys.Count -gt 0) {\n        foreach ($key in $Keys) {\n            $keyName = $key.KeyName\n            $KeyIsWildcard = $key.KeyIsWildcard\n            $variableName = $key.VariableName\n\n            if ([string]::IsNullOrWhiteSpace($variableName)) {\n                $variableName = \"$($keyName.Trim())\"\n            }\n\n            Write-Verbose \"Retrieving values matching key: $($keyName) from store\"\n            $command = \"az appconfig kv list $($global:ConfigStoreParameters) --key \"\"$keyName\"\" --auth-mode login\"\n            \n            if (![string]::IsNullOrWhiteSpace($global:ConfigStoreLabels)) {\n                $command += \" --label \"\"$($global:ConfigStoreLabels)\"\" \"\n            }\n            Write-Verbose \"Invoking expression: $command\"\n\n            $appConfigResponse = Invoke-Expression -Command $command\n            $ExitCode = $LastExitCode\n            Write-Verbose \"az exit code: $ExitCode\"\n            if ($ExitCode -ne 0) {\n                throw \"Error retrieving appsettings. ExitCode: $ExitCode\"\n            }\n\n            if ([string]::IsNullOrWhiteSpace($appConfigResponse)) {\n                Write-OctopusWarning \"Null or empty response received from Azure App Configuration service\"\n            }\n            else {\n                $appConfigValues = $appConfigResponse | ConvertFrom-Json\n                if ($appConfigValues.Count -eq 0) {\n                    Write-OctopusWarning \"Unable to find a matching key in Azure App Config for: $($keyName)\"\n                }\n                else {\n                    Write-Verbose \"Finding match(es) for: $($keyName)\"\n                    Find-AzureAppConfigMatchesFromKey -KeyName $keyName -IsWildcard $KeyIsWildcard -VariableName $variableName -AppConfigValues $appConfigValues\n                }\n            }\n        }\n    }\n}\n\nif ($global:AppSettingsVariables.Count -gt 0 -and $global:CreateAppSettingsJson) {\n    Write-Verbose \"Creating AppSettings JSON output variable\"\n    $AppSettingsJson = ($global:AppSettingsVariables | Sort-Object -Property * -Unique) | ConvertTo-Json -Compress -Depth 10\n    if ($SaveValuesAsSensitiveVariables) {\n        Set-OctopusVariable -Name \"AppSettingsJson\" -Value $AppSettingsJson -Sensitive\n    }\n    else {\n        Set-OctopusVariable -Name \"AppSettingsJson\" -Value $AppSettingsJson \n    }\n    $global:VariablesCreated += 1\n    if ($PrintVariableNames) {\n        Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.AppSettingsJson}\"\n    }\n}\n\nWrite-Host \"Created $global:VariablesCreated output variable(s)\""
  },
  "Category": "Azure",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/azure-app-config-kv-retrieve-values.json",
  "Website": "/step-templates/5c4fbed9-dbba-4139-8440-d8e27318772e",
  "Logo": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADNQTFRF////AHjXf7vrv931QJrh7/f8EIDaIIncMJHfYKvmz+b3n8zw3+76j8Ttr9XycLPpUKLkkKvYFAAABGZJREFUeNrsnNmCqjoQRc1MEiD8/9cer7Yt2KBJZQC8ez07sKlKTQlcLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoUSnt8YxXlFuGHSbIaxvj+fip4btkLn1blkWLaF5v03yLhLOYlVuGYfMOMZzNGxCOzhjTJqFkXnjq3Dr1yyvPI3hGl3Ih3zzHHNKudRstRhX5O58vIcShY67Gq6EPIESlzUWvazaGAOGbvU7ArDu/g8M4o8opDZWvbvPzlL/MMBE8jT9T9W7PbAJlHPTBFRf9yVTEcs63msXz2UHLSgf650G/d5t+wjbxxB2UCMqGrk8/LFSD7uJMeNt5bcJCyQZyAe5Fo9KYfWS2flQrr4b4tpuzaeWjYs49rt9LHf9uZD7+VbyVi9EBNrjYjuq2sxQOrl+p+HuBVu45qvqfq691ttYFQ5KyKbyJgaIY/NGxrlWZwlwGvmvu1oY3PuAv0niTq6tZ78jk//9uc1r1r4lQki7y7sp2Tu4V1y2iLoqFTqi1lIGcpFiebrZNZ1dOkF0cCIlO8jQ47nCkam9Lilz9GhDF1I6XGLzfnhwDIIZVfI7+8SSgfHsijqXENOGJF5QorG4EcW0OrScqX/dDrXpr70Ut/BII+1OfECPuYz/NWxYmgrCsUskxPvyhgmrw+WGZ6lGTuOlIyCYWTFyWjpM5KIZRUIOwjRNYRQ6tZF9BXtk8hWAHPtLNJ727Fq0JSkC1FDRRF0Jalj0d5qVh2KEpM2TuSsCYTCT6ZkdmFYI9LrYp5QayWbo6NXlZwcRD/61pth5Fq5EX423QQxNjhqWvvklkljOLkYjrmphXPZOJOk6Pg7HKMsrtQKcowzZoK3rx1ZUelGMdQA/HaKkjAt2RgqpZeYqbNbH7Hp2ct4nqfSPOfe0ftiSTZJydOV6rG5bQbyLK+nRuCC0343PzDgiOXyQA5c14BTZi98uR/5KJ1SnatLdoO50WWBQZPTq0VgsklU3h932actuo17ayrHrb/3ykiegd3KbqF2wbV6RrlsJ07yLcpsWFTul9RyK6ZScr+tk7oNrFj0o7HQUlj4EiEvJ6rPLKSmlMZCrksl1OnLaRkxc+/HB1naMhNtT/6yM2bDs6azCRHrM3aVPN7aW8irD/10B8njpAMcsl8okXcdKrl4sPsLmQVy/Sj90ucPRc/d/Bxxj+dXSpCayen32D+hLi16MsIV8gfCXrYp6ySsiJKRUF0XXiLpVbFU+fNv4r7mOwhFsX4ZdwpSi1DYs2jb6ebZ9788cblTzMrYhu7sf/17IFdtuviJ2ioHA6pMHkoH4CLUeMBU7iGkxuM/YgcdderF9ibRdc7O982F1HpYhjfWUe+x5a6pjop9iNLfoePvlsdZdTSMwfxSmTY20Q0eHnUNzga1edeNmmqbg18aMVR1L9vwSXHF9TfIWBxpKLs2hj3eQeBC0USvp2HHF3eIkRdhFOd6ER8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/I/4J8AAo/80BciBec4AAAAASUVORK5CYII=",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Thursday, November 30, 2023