diff --git a/src/azure-cli/azure/cli/command_modules/acs/custom.py b/src/azure-cli/azure/cli/command_modules/acs/custom.py index 3da42ea369f..6d9b5a13b91 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/custom.py +++ b/src/azure-cli/azure/cli/command_modules/acs/custom.py @@ -1627,10 +1627,66 @@ def aks_get_credentials(cmd, client, resource_group_name, name, admin=False, encoding='UTF-8') _print_or_merge_credentials( path, kubeconfig, overwrite_existing, context_name) + + # Check if kubeconfig requires kubelogin with devicecode and convert it + if uses_kubelogin_devicecode(kubeconfig): + if which("kubelogin"): + try: + # Run kubelogin convert-kubeconfig -l azurecli + subprocess.run( + ["kubelogin", "convert-kubeconfig", "-l", "azurecli"], + cwd=os.path.dirname(path), + check=True, + ) + logger.warning("Converted kubeconfig to use Azure CLI authentication.") + except subprocess.CalledProcessError as e: + logger.warning("Failed to convert kubeconfig with kubelogin: %s", str(e)) + except Exception as e: # pylint: disable=broad-except + logger.warning("Error running kubelogin: %s", str(e)) + else: + logger.warning( + "The kubeconfig uses devicecode authentication which requires kubelogin. " + "Please install kubelogin from https://github.com/Azure/kubelogin or run " + "'az aks install-cli' to install both kubectl and kubelogin. " + "If devicecode login fails, try running " + "'kubelogin convert-kubeconfig -l azurecli' to unblock yourself." + ) + except (IndexError, ValueError): raise CLIError("Fail to find kubeconfig file.") +def uses_kubelogin_devicecode(kubeconfig: str) -> bool: + try: + config = yaml.safe_load(kubeconfig) + + # Check if users section exists and has at least one user + if not config or not config.get('users') or len(config['users']) == 0: + return False + + first_user = config['users'][0] + user_info = first_user.get('user', {}) + exec_info = user_info.get('exec', {}) + + # Check if command is kubelogin + command = exec_info.get('command', '') + if 'kubelogin' not in command: + return False + + # Check if args contains --login and devicecode + args = exec_info.get('args', []) + # Join args into a string for easier pattern matching + args_str = ' '.join(args) + # Check for '--login devicecode' or '-l devicecode' + if '--login devicecode' in args_str or '-l devicecode' in args_str: + return True + return False + except (yaml.YAMLError, KeyError, TypeError, AttributeError) as e: + # If there's any error parsing the kubeconfig, assume it doesn't require kubelogin + logger.debug("Error parsing kubeconfig: %s", str(e)) + return False + + def _handle_merge(existing, addition, key, replace): if not addition.get(key, False): return diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py index b67aec71c8c..870f9f0de1e 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py @@ -6621,6 +6621,17 @@ def test_aks_automatic_sku(self, resource_group, resource_group_location): ], ) + # get-credentials + fd, temp_path = tempfile.mkstemp() + self.kwargs.update({'file': temp_path}) + try: + self.cmd( + 'aks get-credentials -g {resource_group} -n {name} --file "{file}"') + self.assertGreater(os.path.getsize(temp_path), 0) + finally: + os.close(fd) + os.remove(temp_path) + # scale the cluster scale_cluster_cmd = ( "aks scale --resource-group={resource_group} --name={name} "