Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

az functionapp config appsettings set Errors When Used in PowerShell with Multiple Settings #23919

Closed
aolszowka opened this issue Sep 16, 2022 · 7 comments
Assignees
Labels
Auto-Assign Auto assign by bot customer-reported Issues that are reported by GitHub users external to the Azure organization. CXP Attention This issue is handled by CXP team. Functions az functionapp Shell - PowerShell

Comments

@aolszowka
Copy link

Describe the bug

I have been trying to troubleshoot why I am unable to use the documented --settings switch with multiple space separated Key=Value pairs Documentation in a PowerShell script I have for provisioning. In my production script I end up having all of the secrets roll into the first secret which I am trying to reproduce.

I have created a small test script to try to troubleshoot the issue and was able to get a slightly different error message.

Command Name
az functionapp config appsettings set

Errors:

The command failed with an unexpected error. Here is the traceback:
not enough values to unpack (expected 2, got 1)
Traceback (most recent call last):
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/util.py", line 557, in shell_safe_json_parse
  File "json\__init__.py", line 359, in loads
  File "json\decoder.py", line 337, in decode
  File "json\decoder.py", line 355, in raw_decode
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/appservice/custom.py", line 392, in update_app_settings
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/util.py", line 583, in shell_safe_json_parse
azure.cli.core.azclierror.InvalidArgumentValueError: Failed to parse string as JSON:
at
Error detail: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\knack/cli.py", line 233, in invoke
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/__init__.py", line 663, in execute
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/__init__.py", line 726, in _run_jobs_serially
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/__init__.py", line 718, in _run_job
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/appservice/commands.py", line 40, in _ex_handler
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/__init__.py", line 697, in _run_job
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/__init__.py", line 333, in __call__
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/command_operation.py", line 121, in handler
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/appservice/custom.py", line 403, in update_app_settings
ValueError: not enough values to unpack (expected 2, got 1)

To Reproduce:

Steps to reproduce the behavior. Note that argument values have been redacted, as they may contain sensitive information.

  • az functionapp config appsettings set --name {} --resource-group {} --settings {} {} {} {} {} {} {}

The command is being generated from this PowerShell Script (I have redacted any private information):

$appSettingsToUpdate = [System.Collections.Generic.Dictionary[string, string]]::new()
$appSettingsToUpdate.Add("Setting1", "Set at $(Get-Date)")
$appSettingsToUpdate.Add("Setting2", "Set at $(Get-Date)")

[System.Text.StringBuilder]$settingsString = [System.Text.StringBuilder]::new()
foreach ($appSettingToUpdate in $appSettingsToUpdate.GetEnumerator()) {
    $settingsString.Append("""$($appSettingToUpdate.Key)=$($appSettingToUpdate.Value)"" ") | Out-Null
}

$expandedSettingsString = $settingsString.ToString()

# Set the new settings; This will restart the function app
az functionapp config appsettings set --name {REDACTED} --resource-group {REDACTED} --settings $expandedSettingsString | Out-Null

Expected Behavior

I would expect this to work. This should be equivalent to:

az functionapp config appsettings set --name {REDACTED} --resource-group {REDACTED} --settings "Setting1=Set at 09/16/2022 09:21:42" "Setting2=Set at 09/16/2022 09:21:42"

Which does work when manually run through the command line

Workaround

I suspect that there is probably some weird behavior with PowerShell and the way that this parses commands. I am going to attempt a work around by just providing the settings as a JSON file. However this is not documented to work this way for functionapp but is documented to work this way for webapp (See Documentation) I suspect that this will work even if not documented to do so (that should be fixed).

Environment Summary

Windows-10-10.0.19044-SP0
Python 3.10.5
Installer: MSI

azure-cli 2.40.0

Extensions:
azure-devops 0.25.0

Dependencies:
msal 1.18.0b1
azure-mgmt-resource 21.1.0b1

Additional Context

@ghost ghost added customer-reported Issues that are reported by GitHub users external to the Azure organization. Auto-Assign Auto assign by bot CXP Attention This issue is handled by CXP team. Functions az functionapp labels Sep 16, 2022
@yonzhan
Copy link
Collaborator

yonzhan commented Sep 16, 2022

route to CXP team

@aolszowka
Copy link
Author

I can confirm that the following workaround does indeed work:

$appSettingsToUpdate2 = [System.Collections.Generic.Dictionary[string, string]]::new()
$appSettingsToUpdate2.Add("Setting1", "Set at $(Get-Date)")
$appSettingsToUpdate2.Add("Setting2", "Set at $(Get-Date)")

$appSettingsToUpdate = foreach ($appSettingToUpdate in $appSettingsToUpdate2.GetEnumerator()) {
    [PSCustomObject]@{
        name        = $appSettingToUpdate.Key
        slotSetting = $false
        value       = $appSettingToUpdate.Value
    }
}

# Due to this issue we're going to work around this by creating a
# throw-away JSON file to pass to the command:
# https://github.com/Azure/azure-cli/issues/23920
# https://docs.microsoft.com/en-us/cli/azure/functionapp/config/appsettings?view=azure-cli-latest#az-functionapp-config-appsettings-set
$appSettingsJsonFile = New-TemporaryFile
try {
    $appSettingsToUpdate | ConvertTo-Json | Out-File -FilePath $appSettingsJsonFile.FullName

    # Set the new settings; This will restart the function app.
    # Note that as of 2022/09/16 taking a JSON file is undocumented but
    # does work.
    az functionapp config appsettings set --name {REDACTED} --resource-group {REDACTED} --settings "@$($appSettingsJsonFile.FullName)"
}
finally {
    # Cleanup after ourselves
    Remove-Item $appSettingsJsonFile
}

Properly updates the settings like so:

  {
    "name": "Setting1",
    "slotSetting": false,
    "value": "Set at 09/16/2022 10:45:02"
  },
  {
    "name": "Setting2",
    "slotSetting": false,
    "value": "Set at 09/16/2022 10:45:02"
  }

@jiasli
Copy link
Member

jiasli commented Sep 19, 2022

Duplicate of #23797

@jiasli jiasli marked this as a duplicate of #23797 Sep 19, 2022
@jiasli jiasli closed this as completed Sep 19, 2022
@aolszowka
Copy link
Author

@jiasli I'm confused, the linked issue seemed to be a problem with using a PowerShell Array if you look at the example and code provided I am passing a space separated string. As far as I understood the linked issue they were not passing in a space delimited string.

Can you show the above example working?

@jiasli
Copy link
Member

jiasli commented Sep 19, 2022

@aolszowka, here is the result of your $expandedSettingsString and how Azure CLI receives it:

> $expandedSettingsString
"Setting1=Set at 09/19/2022 11:19:10" "Setting2=Set at 09/19/2022 11:19:10"

# Set the new settings; This will restart the function app
> az functionapp config appsettings set --name REDACTED --resource-group REDACTED --settings $expandedSettingsString --debug
cli.knack.cli: Command arguments: ['functionapp', 'config', 'appsettings', 'set', '--name', 'REDACTED', '--resource-group', 'REDACTED', '--settings', 'Setting1=Set', 'at', '09/19/2022', '11:19:10 Setting2=Set', 'at', '09/19/2022', '11:19:10 ', '--debug']

$expandedSettingsString is one string, with space in it. The correct way to pass a list to Azure CLI is to pass each argument as a string, space-separated.

This should work:

$appSettingsToUpdate = [System.Collections.Generic.Dictionary[string, string]]::new()
$appSettingsToUpdate.Add("Setting1", "Set at $(Get-Date)")
$appSettingsToUpdate.Add("Setting2", "Set at $(Get-Date)")

$settings = [System.Collections.ArrayList]@()

foreach ($appSettingToUpdate in $appSettingsToUpdate.GetEnumerator()) {
    $settings.Add("$($appSettingToUpdate.Key)=$($appSettingToUpdate.Value)")
}

# Set the new settings; This will restart the function app
az functionapp config appsettings set --name REDACTED --resource-group REDACTED --settings @settings --debug

Output:

cli.knack.cli: Command arguments: ['functionapp', 'config', 'appsettings', 'set', '--name', 'REDACTED', '--resource-group', 'REDACTED', '--settings', 'Setting1=Set at 09/19/2022 11:28:21', 'Setting2=Set at 09/19/2022 11:28:21', '--debug']

@jiasli
Copy link
Member

jiasli commented Sep 19, 2022

The fact that $expandedSettingsString gets torn apart is due to PowerShell issue PowerShell/PowerShell#1995:

> $expandedSettingsString = """a=b c"" ""d=e f"""
> $expandedSettingsString
"a=b c" "d=e f"
> python -c "import sys; print(sys.argv)" $expandedSettingsString
['-c', 'a=b', 'c d=e', 'f']
> python -c "import sys; print(sys.argv)" '"a=b c" "d=e f"'
['-c', 'a=b', 'c d=e', 'f']

I guess it's because when calling native exe, PowerShell passes $expandedSettingsString as

""a=b c" "d=e f""

This is fixed in PowerShell 7.3:

> python -c "import sys; print(sys.argv)" $expandedSettingsString
['-c', '"a=b c" "d=e f"']
> python -c "import sys; print(sys.argv)" '"a=b c" "d=e f"'
['-c', '"a=b c" "d=e f"']

The correct way to pass multiple arguments is to use

> $expandedSettingsString="a=b c", "d=e f"
> python -c "import sys; print(sys.argv)" @expandedSettingsString
['-c', 'a=b c', 'd=e f']
> python -c "import sys; print(sys.argv)" "a=b c" "d=e f"
['-c', 'a=b c', 'd=e f']

@aolszowka
Copy link
Author

@jiasli BINGO! Thank you for linking the PowerShell issue that is what I was looking for as I felt the original code met the requirements of a space separated "Key=Value" pair. It is PowerShell's mishandling of the double quotes (in versions prior to 7.3) that was the root cause of this issue.

The intent of this line:

$settingsString.Append("""$($appSettingToUpdate.Key)=$($appSettingToUpdate.Value)"" ") | Out-Null

Was to produce a string that looked like "Setting1=Set at 2022/09/19" "Setting2=Set at 2022/09/19" however as per the linked PowerShell issue this was munged. It is helpful to know that the Azure CLI provides this output:

cli.knack.cli: Command arguments: ['functionapp', 'config', 'appsettings', 'set', '--name', 'REDACTED', '--resource-group', 'REDACTED', '--settings', 'Setting1=Set at 09/19/2022 11:28:21', 'Setting2=Set at 09/19/2022 11:28:21', '--debug']

@jiasli jiasli self-assigned this Sep 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Auto-Assign Auto assign by bot customer-reported Issues that are reported by GitHub users external to the Azure organization. CXP Attention This issue is handled by CXP team. Functions az functionapp Shell - PowerShell
Projects
None yet
Development

No branches or pull requests

3 participants