Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/aks-preview/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ To release a new version, please select a new version number (usually plus 1 to

Pending
+++++++
* `az aks get-credentials`: Convert device code mode kubeconfig to Azure CLI token format to bypass conditional access login blocks.

19.0.0b4
+++++++
Expand Down
31 changes: 31 additions & 0 deletions src/aks-preview/azext_aks_preview/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,37 @@ def print_or_merge_credentials(path, kubeconfig, overwrite_existing, context_nam
os.remove(temp_path)


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 _merge_kubernetes_configurations(existing_file, addition_file, replace, context_name=None):
existing = _load_kubernetes_configuration(existing_file)
addition = _load_kubernetes_configuration(addition_file)
Expand Down
26 changes: 26 additions & 0 deletions src/aks-preview/azext_aks_preview/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import threading
import time
import webbrowser
import subprocess

from azext_aks_preview._client_factory import (
CUSTOM_MGMT_AKS_PREVIEW,
Expand Down Expand Up @@ -72,6 +73,8 @@
get_all_extensions_in_allow_list,
raise_validation_error_if_extension_type_not_in_allow_list,
get_extension_in_allow_list,
uses_kubelogin_devicecode,
which,
)
from azext_aks_preview._podidentity import (
_ensure_managed_identity_operator_permission,
Expand Down Expand Up @@ -1526,6 +1529,29 @@ def aks_get_credentials(
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) as exc:
raise CLIError("Fail to find kubeconfig file.") from exc

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5321,6 +5321,17 @@ def test_aks_automatic_sku(self, resource_group, resource_group_location):
],
)

# get-credentials
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Queued live test to validate the change.

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} "
Expand Down
Loading