Skip to content

Commit ddcd6cd

Browse files
authored
[AKS] az aks get-credentials: Convert device code mode kubeconfig to Azure CLI token format to bypass conditional access login blocks (#32167)
1 parent b804a0e commit ddcd6cd

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

src/azure-cli/azure/cli/command_modules/acs/custom.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,10 +1627,66 @@ def aks_get_credentials(cmd, client, resource_group_name, name, admin=False,
16271627
encoding='UTF-8')
16281628
_print_or_merge_credentials(
16291629
path, kubeconfig, overwrite_existing, context_name)
1630+
1631+
# Check if kubeconfig requires kubelogin with devicecode and convert it
1632+
if uses_kubelogin_devicecode(kubeconfig):
1633+
if which("kubelogin"):
1634+
try:
1635+
# Run kubelogin convert-kubeconfig -l azurecli
1636+
subprocess.run(
1637+
["kubelogin", "convert-kubeconfig", "-l", "azurecli"],
1638+
cwd=os.path.dirname(path),
1639+
check=True,
1640+
)
1641+
logger.warning("Converted kubeconfig to use Azure CLI authentication.")
1642+
except subprocess.CalledProcessError as e:
1643+
logger.warning("Failed to convert kubeconfig with kubelogin: %s", str(e))
1644+
except Exception as e: # pylint: disable=broad-except
1645+
logger.warning("Error running kubelogin: %s", str(e))
1646+
else:
1647+
logger.warning(
1648+
"The kubeconfig uses devicecode authentication which requires kubelogin. "
1649+
"Please install kubelogin from https://github.com/Azure/kubelogin or run "
1650+
"'az aks install-cli' to install both kubectl and kubelogin. "
1651+
"If devicecode login fails, try running "
1652+
"'kubelogin convert-kubeconfig -l azurecli' to unblock yourself."
1653+
)
1654+
16301655
except (IndexError, ValueError):
16311656
raise CLIError("Fail to find kubeconfig file.")
16321657

16331658

1659+
def uses_kubelogin_devicecode(kubeconfig: str) -> bool:
1660+
try:
1661+
config = yaml.safe_load(kubeconfig)
1662+
1663+
# Check if users section exists and has at least one user
1664+
if not config or not config.get('users') or len(config['users']) == 0:
1665+
return False
1666+
1667+
first_user = config['users'][0]
1668+
user_info = first_user.get('user', {})
1669+
exec_info = user_info.get('exec', {})
1670+
1671+
# Check if command is kubelogin
1672+
command = exec_info.get('command', '')
1673+
if 'kubelogin' not in command:
1674+
return False
1675+
1676+
# Check if args contains --login and devicecode
1677+
args = exec_info.get('args', [])
1678+
# Join args into a string for easier pattern matching
1679+
args_str = ' '.join(args)
1680+
# Check for '--login devicecode' or '-l devicecode'
1681+
if '--login devicecode' in args_str or '-l devicecode' in args_str:
1682+
return True
1683+
return False
1684+
except (yaml.YAMLError, KeyError, TypeError, AttributeError) as e:
1685+
# If there's any error parsing the kubeconfig, assume it doesn't require kubelogin
1686+
logger.debug("Error parsing kubeconfig: %s", str(e))
1687+
return False
1688+
1689+
16341690
def _handle_merge(existing, addition, key, replace):
16351691
if not addition.get(key, False):
16361692
return

src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6621,6 +6621,17 @@ def test_aks_automatic_sku(self, resource_group, resource_group_location):
66216621
],
66226622
)
66236623

6624+
# get-credentials
6625+
fd, temp_path = tempfile.mkstemp()
6626+
self.kwargs.update({'file': temp_path})
6627+
try:
6628+
self.cmd(
6629+
'aks get-credentials -g {resource_group} -n {name} --file "{file}"')
6630+
self.assertGreater(os.path.getsize(temp_path), 0)
6631+
finally:
6632+
os.close(fd)
6633+
os.remove(temp_path)
6634+
66246635
# scale the cluster
66256636
scale_cluster_cmd = (
66266637
"aks scale --resource-group={resource_group} --name={name} "

0 commit comments

Comments
 (0)