Skip to content
Open
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
4 changes: 4 additions & 0 deletions src/serviceconnector-passwordless/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Release History
===============
3.1.0
++++++
* Add `az aks connection create`

Choose a reason for hiding this comment

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

🔎
az aks connection create should be backticked in the history notes for consistency with the previous usage.

3.0.2
++++++
* Some improvements and security issue fixes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,8 @@ def get_create_query(self):
def getSourceHandler(source_id, source_type):
if source_type in {RESOURCE.WebApp, RESOURCE.FunctionApp}:
return WebappHandler(source_id, source_type)
if source_type in {RESOURCE.KubernetesCluster}:
return KubernetesHandler(source_id, source_type)
if source_type in {RESOURCE.ContainerApp}:
return ContainerappHandler(source_id, source_type)
if source_type in {RESOURCE.SpringCloud, RESOURCE.SpringCloudDeprecated}:
Expand Down Expand Up @@ -995,6 +997,16 @@ def get_identity_pid(self):
pass


class KubernetesHandler(SourceHandler):
def get_identity_name(self):
raise CLIInternalError(
"System Identity is not supported for Kubernetes cluster.")

def get_identity_pid(self):
raise CLIInternalError(
"System Identity is not supported for Kubernetes cluster.")


class SpringHandler(SourceHandler):
def get_identity_name(self):
segments = parse_resource_id(self.source_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,28 @@ def get_source_display_name(sourcename):
client-id : Required. Client id of the user assigned identity.
subs-id : Required. Subscription id of the user assigned identity.
'''
workload_identity_param = ''
if AUTH_TYPE.WorkloadIdentity in auth_types:
if target in {RESOURCE.MysqlFlexible}:
workload_identity_param = '''
- name: --workload-identity
short-summary: The user-assigned managed identity used to create workload identity federation.
long-summary: |
Usage: --workload-identity <user-identity-resource-id> mysql-identity-id=<mysql-identity-id>

user-identity-resource-id: Required. The resource id of the user assigned identity. Please DO NOT use AKS control plane identity and kubelet identity which is not supported by federated identity credential.
mysql-identity-id : Optional. ID of identity used for MySQL flexible server Microsoft Entra Authentication. Ignore it if you are the server Microsoft Entra administrator.
'''
else:
workload_identity_param = '''
- name: --workload-identity
short-summary: The user-assigned managed identity used to create workload identity federation.
long-summary: |
Usage: --workload-identity <user-identity-resource-id>

user-identity-resource-id: Required. The resource id of the user assigned identity.
Please DO NOT use AKS control plane identity and kubelet identity which is not supported by federated identity credential.
'''
service_principal_param = ''
if AUTH_TYPE.ServicePrincipalSecret in auth_types:
if target in {RESOURCE.MysqlFlexible}:
Expand Down Expand Up @@ -334,6 +356,7 @@ def get_source_display_name(sourcename):
{secret_auto_param}
{system_identity_param}
{user_identity_param}
{workload_identity_param}
{service_principal_param}
examples:
- name: Create a connection between {source_display_name} and {target} interactively
Expand All @@ -355,6 +378,7 @@ def get_source_display_name(sourcename):
secret_auto_param=secret_auto_param,
system_identity_param=system_identity_param,
user_identity_param=user_identity_param,
workload_identity_param=workload_identity_param,
service_principal_param=service_principal_param,
source_params=source_params,
target_params=target_params,
Expand All @@ -370,6 +394,7 @@ def get_source_display_name(sourcename):
{secret_auto_param}
{system_identity_param}
{user_identity_param}
{workload_identity_param}
{service_principal_param}
examples:
- name: Update the client type of a connection with resource name
Expand All @@ -385,6 +410,7 @@ def get_source_display_name(sourcename):
secret_auto_param=secret_auto_param,
system_identity_param=system_identity_param,
user_identity_param=user_identity_param,
workload_identity_param=workload_identity_param,
service_principal_param=service_principal_param,
source_params=source_params,
connection_id=connection_id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
AddUserAssignedIdentityAuthInfo,
AddServicePrincipalAuthInfo,
AddUserAccountAuthInfo,
AddWorkloadIdentityAuthInfo
)

EX_SUPPORTED_AUTH_TYPE = SUPPORTED_AUTH_TYPE.copy()
Expand All @@ -26,6 +27,7 @@
RESOURCE.SpringCloud,
RESOURCE.SpringCloudDeprecated,
RESOURCE.FunctionApp,
RESOURCE.KubernetesCluster,
]

PASSWORDLESS_TARGET_RESOURCES = [
Expand All @@ -44,12 +46,19 @@
}

for resourceType in PASSWORDLESS_SOURCE_RESOURCES:
EX_SUPPORTED_AUTH_TYPE[resourceType] = {
# RESOURCE.Postgres: [AUTH_TYPE.Secret, AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.PostgresFlexible: [AUTH_TYPE.Secret, AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.MysqlFlexible: [AUTH_TYPE.Secret, AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.Sql: [AUTH_TYPE.Secret, AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
}
if RESOURCE.KubernetesCluster == resourceType:
EX_SUPPORTED_AUTH_TYPE[resourceType] = {
RESOURCE.PostgresFlexible: [AUTH_TYPE.Secret, AUTH_TYPE.WorkloadIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.MysqlFlexible: [AUTH_TYPE.Secret, AUTH_TYPE.WorkloadIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.Sql: [AUTH_TYPE.Secret, AUTH_TYPE.WorkloadIdentity, AUTH_TYPE.ServicePrincipalSecret],
}
else:
EX_SUPPORTED_AUTH_TYPE[resourceType] = {
# RESOURCE.Postgres: [AUTH_TYPE.Secret, AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.PostgresFlexible: [AUTH_TYPE.Secret, AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.MysqlFlexible: [AUTH_TYPE.Secret, AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.Sql: [AUTH_TYPE.Secret, AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
}

TARGET_RESOURCES_PARAMS = {
# RESOURCE.Postgres: {
Expand Down Expand Up @@ -152,6 +161,13 @@
'action': AddUserAssignedIdentityAuthInfo
}
},
AUTH_TYPE.WorkloadIdentity: {
'workload_identity_auth_info': {
'options': ['--workload-identity'],
'help': 'The workload identity auth info',
'action': AddWorkloadIdentityAuthInfo
}
},
AUTH_TYPE.ServicePrincipalSecret: {
'service_principal_auth_info_secret': {
'options': ['--service-principal'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,47 @@ def get_action(self, values, option_string, command_name):

d['auth_type'] = 'servicePrincipalSecret'
return d


class AddWorkloadIdentityAuthInfo(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
action = self.get_action(values, option_string, namespace.command)
# convert into user identity
namespace.user_identity_auth_info = action

def get_action(self, values, option_string, command_name): # pylint: disable=no-self-use
try:
# --workload-identity <user-identity-resource-id>
properties = defaultdict(list)
if is_mysql_target(command_name):
for val in values:
if '=' in val:
k, v = val.split('=', 1)
properties[k] = v
else:
properties['user-identity-resource-id'] = val
else:
if len(values) != 1:
raise ValidationError(
'Invalid number of values for --workload-identity <USER-IDENTITY-RESOURCE-ID>')
properties['user-identity-resource-id'] = values[0]
except ValueError:
raise ValidationError('Usage error: {} [VALUE]'.format(option_string))

d = {}
if is_mysql_target(command_name) and 'mysql-identity-id' in properties:
d['mysql-identity-id'] = properties['mysql-identity-id']
if 'user-identity-resource-id' in properties:
from ._utils import run_cli_cmd
output = run_cli_cmd('az identity show --ids "{}"'.format(properties['user-identity-resource-id']))
if output:
d['client_id'] = output.get('clientId')
d['subscription_id'] = properties['user-identity-resource-id'].split('/')[2]
else:
raise ValidationError('Invalid user identity resource ID.')
else:
raise ValidationError('Required values missing for parameter --workload-identity: \
user-identity-resource-id')

d['auth_type'] = 'userAssignedIdentity'
return d
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
# --------------------------------------------------------------------------------------------


VERSION = '3.0.2'
VERSION = '3.1.0'
NAME = 'serviceconnector-passwordless'
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
# --------------------------------------------------------------------------------------------


def connection_create_ext(cmd, client, # pylint: disable=too-many-locals,too-many-statements
# pylint: disable=too-many-locals,too-many-statements,unused-argument
def connection_create_ext(cmd, client,

Choose a reason for hiding this comment

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

🔎
Consider removing the addition of the argument workload_identity_auth_info if it is not used within the function body. Unused arguments can lead to confusion and clutter the code.

Refined suggestion:

# pylint: disable=too-many-locals,too-many-statements,unused-argument
def connection_create_ext(cmd, client,                             
                           connection_name=None, client_type=None,
                           source_resource_group=None, source_id=None,
                           target_resource_group=None, target_id=None,
                           secret_auth_info=None, secret_auth_info_auto=None,
                           user_identity_auth_info=None, system_identity_auth_info=None,
                           service_principal_auth_info_secret=None,
                           key_vault_id=None,
                           app_config_id=None,

connection_name=None, client_type=None,
source_resource_group=None, source_id=None,
target_resource_group=None, target_id=None,
secret_auth_info=None, secret_auth_info_auto=None,
user_identity_auth_info=None, system_identity_auth_info=None,
workload_identity_auth_info=None, # only used as arg
service_principal_auth_info_secret=None,
key_vault_id=None,
app_config_id=None,
Expand Down
2 changes: 1 addition & 1 deletion src/serviceconnector-passwordless/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
logger.warn("Wheel is not available, disabling bdist_wheel hook")


VERSION = '3.0.2'
VERSION = '3.1.0'

Choose a reason for hiding this comment

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

🔎

-    logger.warn("Wheel is not available, disabling bdist_wheel hook")
+    logger.warning("Wheel is not available, disabling bdist_wheel hook")

Refinement: Use logger.warning instead of deprecated logger.warn.

try:
from azext_serviceconnector_passwordless.config import VERSION
except ImportError:
Expand Down