From 9904154ee1d7c9fc85525a4f32181ec01d928fa9 Mon Sep 17 00:00:00 2001 From: Harshitha Akkaraju Date: Fri, 5 Jun 2026 15:57:07 -0700 Subject: [PATCH] [App Service] Fix az functionapp function keys list returning null values with azure-mgmt-web 11.0.0 --- .../cli/command_modules/appservice/custom.py | 7 +++- .../test_functionapp_commands_thru_mock.py | 42 ++++++++++++++++++- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/custom.py b/src/azure-cli/azure/cli/command_modules/appservice/custom.py index 4f788cb9119..0e2192b06b9 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/custom.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/custom.py @@ -11437,8 +11437,11 @@ def update_function_key(cmd, resource_group_name, name, function_name, key_name, def list_function_keys(cmd, resource_group_name, name, function_name, slot=None): client = web_client_factory(cmd.cli_ctx) if slot: - return client.web_apps.list_function_keys_slot(resource_group_name, name, function_name, slot) - return client.web_apps.list_function_keys(resource_group_name, name, function_name) + keys = client.web_apps.list_function_keys_slot(resource_group_name, name, function_name, slot) + else: + keys = client.web_apps.list_function_keys(resource_group_name, name, function_name) + # SDK may return .properties as None for flat dictionary responses; fall back to raw payload + return keys.properties if keys.properties is not None else dict(keys) def delete_function_key(cmd, resource_group_name, name, key_name, function_name=None, slot=None): diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_functionapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_functionapp_commands_thru_mock.py index e7e3b8ae6ba..2174b32810d 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_functionapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_functionapp_commands_thru_mock.py @@ -17,7 +17,8 @@ remove_remote_build_app_settings, config_source_control, validate_app_settings_in_scm, - update_container_settings_functionapp) + update_container_settings_functionapp, + list_function_keys) from azure.cli.core.profiles import ResourceType from azure.cli.core.azclierror import (AzureInternalError, UnclassifiedUserFault) from azure.cli.core.azclierror import ResourceNotFoundError @@ -849,3 +850,42 @@ def test_flex_parse_raw_stacks_prefers_functions_worker_runtime_when_present(sel # assert self.assertEqual(matched.name, 'dotnet-isolated') + + @mock.patch('azure.cli.command_modules.appservice.custom.web_client_factory', autospec=True) + def test_list_function_keys_unwraps_broken_string_dictionary(self, web_client_factory_mock): + # azure-mgmt-web 11.0.0 deserializes flat dictionary responses with .properties = None + from azure.mgmt.web import models as _models + from azure.mgmt.web._utils.model_base import _deserialize + + broken = _deserialize( + _models.StringDictionary, + {'default': 'vvrX4LJY1JWbimFI28UM', 'myCustomKey': 'abc'}) + self.assertIsNone(broken.properties) + + cmd_mock = _get_test_cmd() + client_mock = mock.MagicMock() + client_mock.web_apps.list_function_keys.return_value = broken + web_client_factory_mock.return_value = client_mock + + result = list_function_keys(cmd_mock, 'rg', 'app', 'httpget') + + self.assertEqual(result, {'default': 'vvrX4LJY1JWbimFI28UM', 'myCustomKey': 'abc'}) + client_mock.web_apps.list_function_keys.assert_called_once_with('rg', 'app', 'httpget') + client_mock.web_apps.list_function_keys_slot.assert_not_called() + + @mock.patch('azure.cli.command_modules.appservice.custom.web_client_factory', autospec=True) + def test_list_function_keys_uses_properties_when_sdk_returns_enveloped_response(self, web_client_factory_mock): + # Prefers .properties when populated (forward-compatible with a future SDK fix) + fixed = mock.MagicMock() + fixed.properties = {'default': 'abc'} + + cmd_mock = _get_test_cmd() + client_mock = mock.MagicMock() + client_mock.web_apps.list_function_keys_slot.return_value = fixed + web_client_factory_mock.return_value = client_mock + + result = list_function_keys(cmd_mock, 'rg', 'app', 'httpget', slot='staging') + + self.assertEqual(result, {'default': 'abc'}) + client_mock.web_apps.list_function_keys_slot.assert_called_once_with('rg', 'app', 'httpget', 'staging') + client_mock.web_apps.list_function_keys.assert_not_called()