AppConfiguration icon indicating copy to clipboard operation
AppConfiguration copied to clipboard

Add "Az Powershell" commands for data operations

Open ivcarreras opened this issue 5 years ago • 9 comments

I am trying to consume this Azure service in my service implementation but I can't integrate it into our release pipelines process. I have PowerShell scripts that run after our ARM deployments to do final setups and I was hoping I can add all the settings and key vault endpoints needed for the service but only az appconfig is supported at this time. Running az appconfig within PoweShell scripts is not ideal, a proper Az Cmdlet would be much better.

ivcarreras avatar Feb 19 '20 17:02 ivcarreras

While we're waiting on Az module support, here is the equivalent command using the Azure CLI:

az appconfig kv set

Full docs here:

https://docs.microsoft.com/en-us/cli/azure/appconfig/kv?view=azure-cli-latest

And a useful article that goes into more detail on how else you can interact with an App Configuration Service instance:

https://zimmergren.net/introduction-azure-app-configuration-store-csharp-dotnetcore/

david-peden-q2 avatar Jul 28 '20 16:07 david-peden-q2

How can a token be fetched via Get-AzAccessToken for use with HTTP requests to configure store endpoints?

mattboothman avatar Dec 28 '20 23:12 mattboothman

Get-AzAccessToken only supports Azure resource management (management plane) endpoints. Please log feature request to https://github.com/Azure/azure-powershell/issues/new/choose

dingmeng-xue avatar Feb 03 '21 03:02 dingmeng-xue

Any news on this request? It seems like it should be something that's just there.

indigogiant avatar Nov 08 '21 04:11 indigogiant

When can we have this? I can't use Get-AzAccessToken, I can't mix azure cli with azure pwsh in pipeline without a load of faff. It's been a long while since it was requested, az cli has it, how long before pwsh gets it too?

Can we have a workaround that doesn't involve az cli, please?

mattboothman avatar Nov 08 '21 15:11 mattboothman

Get-AzAccessToken only supports Azure resource management (management plane) endpoints. Please log feature request to https://github.com/Azure/azure-powershell/issues/new/choose

I'm not sure this is really true. I've used it for a bunch of other data plane stuff, I'm sure.

mattboothman avatar Nov 08 '21 15:11 mattboothman

Hi @mattboothman ,

We are visiting Invoke-AzRestMethod now. Idea is cmdlet will analyze the domain of input URL and find the correct name of resource (resourceId) to use in further data plane request. The limitation is we only can support data plane API which resourceId and domain suffix have been declared in Azure Environment Get-AzEnvironment

Can it cover your requirement?

Thanks, Dingmeng

dingmeng-xue avatar Nov 09 '21 01:11 dingmeng-xue

This original request is now 2 years old. This seems like plenty of time to implement data plane cmdlets for App Configuration. Every other Azure resource I work with regularly has full support for native PowerShell actions, for both management and data plane operations. Why is this not forthcoming for App Configuration?

At this point should users assume this will just never happen? If so, can this be closed as "won't fix" so we can at least know what to expect, or more correctly, to not expect it?

traberc avatar May 16 '22 22:05 traberc

In case it helps someone else, here's an example of how to use Get-AzAccessToken with App Configuration to get a list of keys and add a new key/value pair.

$accessToken = Get-AzAccessToken -ResourceUrl 'https://azconfig.io'

# Get a list of keys
Invoke-RestMethod -Authentication Bearer -Token (ConvertTo-SecureString -AsPlainText $accessToken.Token -Force) -Uri "https://$appConfigName.azconfig.io/keys?api-version=1.0"

# Insert a new key/value pair
$keyValue = @{ value = 'Some Value'; content_type = 'text/plain', tags=@{ 'tag1' = 'value1', 'tag2' = 'value2' } }
Invoke-RestMethod -Authentication Bearer -Token (ConvertTo-SecureString -AsPlainText $accessToken.Token -Force) -Uri "https://$appConfigName.azconfig.io/kv/testKey?api-version=1.0" -Method Put -Body (ConvertTo-Json $keyValue) -ContentType 'application/vnd.microsoft.appconfig.kv+json'

NB: While it's fine for this example, when passing more complex objects to ConvertTo-Json you may need to pass -Depth N because the default depth is only 2, meaning anything more than two levels deep in the object hierarchy will be ignored.

indigogiant avatar May 31 '22 01:05 indigogiant

While we're waiting on Az module support, here is the equivalent command using the Azure CLI:

az appconfig kv set

Full docs here:

https://docs.microsoft.com/en-us/cli/azure/appconfig/kv?view=azure-cli-latest

And a useful article that goes into more detail on how else you can interact with an App Configuration Service instance:

https://zimmergren.net/introduction-azure-app-configuration-store-csharp-dotnetcore/

Using az cli requires a separate login to Azure as it does not detect the existing login done with Connect-AzAccount

miobrado avatar Oct 13 '22 15:10 miobrado

We are on the way to support AppConfiguration data plane feature. Since Az 9, you can use Invoke-AzRestMethod to send request to data plane of AppConfiguration directly. Az.Accounts will handle token automatically according to URL.

dingmeng-xue avatar Oct 13 '22 15:10 dingmeng-xue

Here's an example for reading key-values from App Configuration, although using a connection string instead of access token for authentication.

Keys can contain slash characters, so they need to be URL-encoded using [System.Net.WebUtility]::UrlEncode($Key) when building a $RequestUri.

function Invoke-AppConfigRequest {
    param(
        [Parameter(Mandatory = $true)] [string] $ConnectionString,  # 'Endpoint=...;Id=...;Secret=...'
        [Parameter(Mandatory = $true)] [string] $RequestUri,        # '/kv?api-version=1.0&key=some-url-encoded-key&label=*'
        [Parameter(Mandatory = $false)] [string] $Method = 'GET',   # 'GET', 'POST'
        [Parameter(Mandatory = $false)] [object] $Body = $null      # Accepts [object] to avoid implicit conversion of $null to empty string
    )

    $ConnectionStringValues = $ConnectionString -split ';' | ForEach-Object { $Tokens = $_ -split '=',2; @{ Key = $Tokens[0]; Value = $Tokens[1] } }
    $Endpoint = ($ConnectionStringValues | Where-Object { $_.Key -eq 'Endpoint' }).Value
    $Credential = ($ConnectionStringValues | Where-Object { $_.Key -eq 'Id' }).Value
    $Secret = ($ConnectionStringValues | Where-Object { $_.Key -eq 'Secret' }).Value
    if ([string]::IsNullOrWhitespace($Endpoint) -or [string]::IsNullOrWhitespace($Credential) -or [string]::IsNullOrWhitespace($Secret)) {
        throw "Invalid App Configuration connection string"
    }

    $UtcNow = (Get-Date).ToUniversalTime().ToString('ddd, d MMM yyyy HH:mm:ss \G\M\T')
    $EndpointHost = $Endpoint -replace '^https?://(.*)$','$1'
    $ContentHash = [Convert]::ToBase64String(
        [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash(
            [System.Text.Encoding]::UTF8.GetBytes($(if ($Body -ne $null) { "$Body" } else { '' }))
        )
    )
    $StringToSign = "$Method`n$RequestUri`n$UtcNow;$EndpointHost;$ContentHash"

    $HmacSha256 = New-Object System.Security.Cryptography.HMACSHA256
    $HmacSha256.Key = [Convert]::FromBase64String($Secret)
    $Signature = [Convert]::ToBase64String(
        $HmacSha256.ComputeHash(
            [System.Text.Encoding]::UTF8.GetBytes($StringToSign)
        )
    )

    $Headers = @{
        'Host' = $EndpointHost;
        'x-ms-date' =  $UtcNow;
        'x-ms-content-sha256' = $ContentHash;
        'Accept' = 'application/vnd.microsoft.appconfig.kv+json, application/json, application/problem+json';
        'Authorization' = "HMAC-SHA256 Credential=$Credential&SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=$Signature";
    }

    $Uri = "$Endpoint$RequestUri"
    $Response = Invoke-WebRequest -Method $Method -Uri $Uri -Headers $Headers -Body $Body
    if ($Response.StatusCode -eq 200) {
        [System.Text.Encoding]::UTF8.GetString($Response.Content) | ConvertFrom-Json
    }
}

Example invocation:

function Get-AppConfigKeyValue {
    param(
        [Parameter(Mandatory = $true)] [string] $ConnectionString,
        [Parameter(Mandatory = $true)] [string] $Key,
        [Parameter(Mandatory = $false)] [string] $Label = ''
    )

    $UrlEncodedKey = [System.Net.WebUtility]::UrlEncode($Key)
    $UrlEncodedLabel = [System.Net.WebUtility]::UrlEncode($Label)

    # https://learn.microsoft.com/azure/azure-app-configuration/rest-api-key-value
    $Method = 'GET'
    $ApiVersion = '1.0'
    $RequestUri = '/kv'
    #if (![string]::IsNullOrWhitespace($UrlEncodedKey)) {
    #    $RequestUri += "/$UrlEncodedKey"  # Strict key/label matching, no support for wildcards like *.
    #}
    $RequestUri += "?api-version=$ApiVersion"
    if (![string]::IsNullOrWhitespace($UrlEncodedKey)) {
        $RequestUri += "&key=$UrlEncodedKey"  # Key filter, accepts "*" to match all keys.
    }
    if (![string]::IsNullOrWhitespace($UrlEncodedLabel)) {
        $RequestUri += "&label=$UrlEncodedLabel"  # Label filter, accepts "*" to match all labels.
    } else {
        $RequestUri += "&label=%00"  # Matches KV without a label.
    }

    (Invoke-AppConfigRequest -ConnectionString $ConnectionString -RequestUri $RequestUri).items
}

miobrado avatar Oct 13 '22 22:10 miobrado

@dingmeng-xue thanks for the hot tip.

We are on the way to support AppConfiguration data plane feature. Since Az 9, you can use Invoke-AzRestMethod to send request to data plane of AppConfiguration directly. Az.Accounts will handle token automatically according to URL.

PS> Invoke-AzRestMethod -Uri "https://appcg-tlkappsettings.azconfig.io"
Invoke-AzRestMethod: The given key 'AzureAppConfigurationEndpointSuffix' was not present in the dictionary.

I upgraded to Az 9.1.1 after reading your comment but encountered the above error. It worked fine after running Connect-AzAccount to refresh my context. 👍 Very easy way to get at the data plane's REST interface! All the docs claim that Invoke-AzRestMethod is for control plane operations only, quite confusing.

t-l-k avatar Nov 21 '22 15:11 t-l-k

@t-l-k , That endpoint suffix was introduced recently. If your Azure context was built for a while, environment variable list doesn't contain new endpoint. Please try to Clear-AzContext and login again.

dingmeng-xue avatar Nov 21 '22 15:11 dingmeng-xue

3 years and counting now, is there going to be any priority for support Azure PowerShell?

wsmelton avatar May 23 '23 16:05 wsmelton

These are now available -- closing.

jimmyca15 avatar Aug 08 '23 00:08 jimmyca15