PSAzureAppConfiguration.psm1

#Region '.\Private\Get-LabelQuery.ps1' 0
function Get-LabelQuery {
    <#
        .SYNOPSIS
            Converts to label query
        .EXAMPLE
            Get-LabelQuery -Label \0
            Expected Output: '\0'
        .EXAMPLE
            Get-LabelQuery -Label Prod
            Expected Output: "\0,Prod"
        .EXAMPLE
            Get-LabelQuery -Label *
            Expected Output: "*"
        .EXAMPLE
            Get-LabelQuery -Label ""
            Expected Output: "\0"
    #>

    param(
        [string] $Label
        ,
        [switch] $ExcludeNoLabel
    )
    if (-not($Label) -or $Label -eq '\0' ){
        Return '\0'
    }
    if ( $PSBoundParameters.Label -eq '*') {
        Return '*'
    }
    if ( $PSBoundParameters.ExcludeNoLabel ) {
        Return $Label
    }
    Return "\0,$Label"
}
#EndRegion '.\Private\Get-LabelQuery.ps1' 34
#Region '.\Private\Invoke-AzCli.ps1' 0
function Invoke-AzCli {
    <#
        .SYNOPSIS
            Wrapper for Azure cli
            Tries to convert json, otherwise raw output is returned
        .EXAMPLE
            Invoke-AzCli -Arguments "appconfig -h"
        .EXAMPLE
            Invoke-AzCli -Arguments "appconfig kv list --name $store"
    #>

    param (
        [string] $Arguments
    )
    $expression = "& az $Arguments"
    Write-Verbose "Invoking: $expression"
    $Output = Invoke-Expression $expression -ErrorAction Stop
    try {
        Write-Output $Output | ConvertFrom-Json
    } catch {
        $Output
    }
}
#EndRegion '.\Private\Invoke-AzCli.ps1' 23
#Region '.\Public\Convert-KeyReference.ps1' 0
function Convert-KeyReference {
    <#
        .SYNOPSIS
            Converts Keyreferences in a string with values retrieved from a given dictionary
        .EXAMPLE
            $dict = @(
                @{
                    Key = "MyDomain"
                    Value = "Contoso"
                },
                @{
                    Key = "MyUserName"
                    Value = "Bob"
                },
                @{
                    Key = "Environment"
                    Value = "Production"
                }
            )
            $string = '$(MyDomain)\$(MyUserName)_$(Environment)'
            Convert-KeyReference -String $string -Dictionary $dict
 
            Expected Output: "Contoso\Bob_Production"
 
        .EXAMPLE
            $dict = @(
                @{
                    Key = "Server"
                    Value = "prod-001"
                },
                @{
                    Key = "Database"
                    Value = "RetailStore"
                },
                @{
                    Key = "Trusted_Connection"
                    Value = "yes"
                },
                @{
                    Key = "ConnectionString"
                    Value = 'Server=$(Server);Database=$(Database);Trusted_Connection=$(Trusted_Connection)'
                }
            )
            $string = 'ConnectionString=$(ConnectionString)'
            Convert-KeyReference -String $string -Dictionary $dict
            Expected Output: "ConnectionString=Server=prod-001;Database=RetailStore;Trusted_Connection=yes"
    #>

    [cmdletbinding()]
    param(
        [String] $String
        ,
        $Dictionary
        ,
        [String] $Prefix = '\$\('
        ,
        [String] $Suffix = '\)'
        ,
        [int] $Cycle = 0
    )
    if ($Cycle -ge 5) {
        Return
    }
    $stringOutput = $string
    $references = Get-KeyReference -String $string -Regex "$Prefix[\w.]*$Suffix"
    if (-not $references) {
        Return $stringOutput
    }
    $references | Foreach-Object {
        $refKey = $_
        $refValue = ($Dictionary | Where-Object {$_.key -eq $refkey}).Value
        if ($refValue) {
            Write-Verbose "$refkey resolved to $refValue"
            $stringOutput = $stringOutput -replace "$Prefix$refKey$Suffix",$refValue
        }
    }
    Convert-KeyReference -String $stringOutput -Dictionary $Dictionary -Cycle ($Cycle+1)
}
#EndRegion '.\Public\Convert-KeyReference.ps1' 78
#Region '.\Public\Get-AppConfigurationFeature.ps1' 0
function Get-AppConfigurationFeature {
    <#
        .SYNOPSIS
            Lists features from an Azure App Configuration
        .EXAMPLE
            Get-AppConfigurationFeature -Store $Store
    #>

    [cmdletbinding()]
    param(
        [string] $Feature = '*'
        ,
        [parameter(Mandatory)]
        [string] $Store
        ,
        [string] $Label = 'Acceptance'
        ,
        [switch] $ExcludeNoLabel
    )
    $labelQuery = Get-LabelQuery @PSBoundParameters
    $features = az appconfig feature list --name $Store --label $labelQuery --feature $Feature | ConvertFrom-Json
    # when multiple keys are returned, keep the one with the label
    $Output = $features | Group-object -Property Key | ForEach-Object {
        $_.Group | Sort-Object -Property Label -Descending | Select-Object -First 1
    }
    $Output | Add-Member -NotePropertyName Store -NotePropertyValue $Store
    Write-Output $Output
}
#EndRegion '.\Public\Get-AppConfigurationFeature.ps1' 28
#Region '.\Public\Get-AppConfigurationKeyValue.ps1' 0
function Get-AppConfigurationKeyValue {
    <#
        .SYNOPSIS
            Gets (a) key value(s) from an Azure App Configuration Store
            perform an az login first
        .EXAMPLE
            Get-AppConfigurationKeyValue -Store $store
 
            Gets all key values from $store
        .EXAMPLE
            Get-AppConfigurationKeyValue -Store $store -Key Common* -ConvertReferences
 
            Gets all key values starting with 'Common' from $store
            and converts referenced keys
        .EXAMPLE
            $keys = Get-AppConfigurationKeyValue -Store $store -Label Development -NoResolveSecret -Verbose
            $keys
 
            Gets all key values with labels null or Development from $store
            does not resolve keyvault secrets
        .EXAMPLE
            Get-AppConfigurationKeyValue -Store $store -Key .appconfig.featureflag*
 
            Gets all feature flags
            You can also use Get-AppConfigurationFeature
        .EXAMPLE
            $keys = Get-AppConfigurationKeyValue -Store $store -Top 200 -Label Development -Verbose
            $keys
 
            Gets all key values with labels null or Development from $store
            Increases the max limit to 200, 100 is default
    #>

    [cmdletbinding()]
    param(
        [string] $Key = '*'
        ,
        [parameter(Mandatory)]
        [string] $Store
        ,
        [string] $Label = '*'
        ,
        [switch] $NoResolveSecret
        ,
        [switch] $ExcludeNoLabel
        ,
        [switch] $ConvertReferences
        ,
        [int] $Top
    )
    # az appconfig kv list -h
    if (-not $NoResolveSecret) {
        $resolveKv = '--resolve-keyvault'
    }
    if ($Top) {
        $limitTop = "--top $Top"
    }
    $labelQuery = Get-LabelQuery @PSBoundParameters
    $m = Measure-Command {
        $keyValues = Invoke-AzCli -Arguments "appconfig kv list --name $Store --label $labelQuery --key $Key $limitTop $resolveKv"
    }
    Write-Verbose "Loaded $($keyValues.Count) keys in $($m.TotalMilliseconds)"
    # when specifying a Label, and keep the one with the label (ignore the no-label)
    $Output = $keyValues
    if ($Label -ne '*') {
        $Output = $Output | Group-object -Property Key | ForEach-Object {
            $_.Group | Sort-Object -Property Label -Descending | Select-Object -First 1
        }
    }
    foreach ($kv in $Output) {
        $value = $kv.value
        if ($kv.key -like '.appconfig.featureflag*') {
            $value = $value | ConvertFrom-Json
        }
        if ($ConvertReferences) {
            $value = Convert-KeyReference -String $value -Dictionary $Output
        }
        Write-Output @{ $kv.key = $value}
    }
}
#EndRegion '.\Public\Get-AppConfigurationKeyValue.ps1' 80
#Region '.\Public\Get-KeyReference.ps1' 0
function Get-KeyReference {
    <#
        .SYNOPSIS
            Parses a string and extracts the tokenized strings
            Default prefix and suffix is '$(' and ')'
            Only words chars and dots are allowed as key strings
        .EXAMPLE
            Get-KeyReference -String 'MyServer=$(MyApp.Database.Server);Database=$(MyApp.Database.Name);Trusted_Connection=Yes'
            Expected Output: @('MyApp.Database.Server','MyApp.Database.Name')
        .EXAMPLE
            $string = '$(MyDomain)\$(MyUserName)_$(Environment)'
            Get-KeyReference -String $string
            Expected Output: @('MyDomain','MyUserName','Environment')
    #>

    param(
        [string] $String
        ,
        [string] $regex = '\$\([\w.]*\)'
    )
    $references = ($string | select-string -pattern $regex -AllMatches).Matches.Value
    if ($references) {
        $references.replace('$(','').replace(')','')
    }
}
#EndRegion '.\Public\Get-KeyReference.ps1' 25
#Region '.\Public\Set-AppConfigurationFeature.ps1' 0
function Set-AppConfigurationFeature {
    <#
        .SYNOPSIS
            Lists features from an Azure App Configuration
            if state is not provided, it will toggle (on -> off, off -> on )
        .EXAMPLE
            $feature = Get-AppConfigurationFeature -Store $Store
            $feature = $feature | Set-AppConfigurationFeature
            $feature
 
            Togggle the feature on an off depending the current state
        .EXAMPLE
            Set-AppConfigurationFeature -State On -Feature $feature.key -Store $store -verbose
 
            Ensures a that state of a feature is On
    #>

    [cmdletbinding()]
    param(
        [parameter(ValueFromPipeline)]
        [psobject] $FeatureObject
        ,
        [string] $Feature
        ,
        [string] $Store
        ,
        [string] $Label = '\0'
        ,
        [string] $State
    )
    process {
        if ( $PSBoundParameters.ContainsKey('FeatureObject') ) {
            Write-Verbose 'Using pipeline object'
            $Store = $FeatureObject.Store
            $Feature = $FeatureObject.key
            $Label = $FeatureObject.Label
            $currentFeatureState = $FeatureObject.State
        }
        if ( -not$Label ) {
            $Label = '\0'
            Write-Verbose $Label
        }
        if ( -not($currentFeatureState) -and -not($PSBoundParameters.ContainsKey('State')) ) {
            Write-Verbose 'State not set, current state unknown, getting current state pipeline object'
            $currentFeatureState = (Get-AppConfigurationFeature -Feature $Feature -Label $Label -Store $store).State
        }
        if ( -not($PSBoundParameters.ContainsKey('State')) ){
            $state = if ($currentFeatureState -eq 'off') { 'on' } else { 'off' }
        }
        $verbs = @{
            'on' = 'enable'
            'off' = 'disable'
        }
        $Result = Invoke-AzCli "appconfig feature $($verbs[$state]) --name $Store --label $Label --feature $Feature --yes"
        $Result | Add-Member -NotePropertyName Store -NotePropertyValue $Store
        Write-Output $Result
    }
    end {
    }
}
#EndRegion '.\Public\Set-AppConfigurationFeature.ps1' 60