diff --git a/python/az/aro/azext_aro/_actions.py b/python/az/aro/azext_aro/_actions.py new file mode 100644 index 00000000000..bff7d49af3e --- /dev/null +++ b/python/az/aro/azext_aro/_actions.py @@ -0,0 +1,29 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the Apache License 2.0. + +import argparse + +from azext_aro.vendored_sdks.azure.mgmt.redhatopenshift.v2024_08_12_preview.models import PlatformWorkloadIdentity +from azure.cli.core.azclierror import CLIError + + +class AROPlatformWorkloadIdentityAddAction(argparse._AppendAction): + + def __call__(self, parser, namespace, values, option_string=None): + try: + if len(values) != 2: + msg = '%s requires 2 values in format: `OPERATOR_NAME RESOURCE_ID`' + raise argparse.ArgumentError( + self, msg % (option_string)) + + operator_name, resource_id = values + parsed = PlatformWorkloadIdentity( + operator_name=operator_name, + resource_id=resource_id + ) + + super(AROPlatformWorkloadIdentityAddAction, self).__call__( + parser, namespace, parsed, option_string) + + except ValueError: + raise CLIError('usage error: {} NAME ID'.format(option_string)) diff --git a/python/az/aro/azext_aro/_params.py b/python/az/aro/azext_aro/_params.py index 126c7cdaa65..f66842017ef 100644 --- a/python/az/aro/azext_aro/_params.py +++ b/python/az/aro/azext_aro/_params.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the Apache License 2.0. +from azext_aro._actions import AROPlatformWorkloadIdentityAddAction from azext_aro._validators import validate_cidr from azext_aro._validators import validate_client_id from azext_aro._validators import validate_client_secret @@ -56,7 +57,8 @@ def load_arguments(self, _): validator=validate_client_secret(isCreate=True)) c.argument('version', - options_list=['--version', c.deprecate(target='--install-version', redirect='--version', hide=True)], + options_list=[ + '--version', c.deprecate(target='--install-version', redirect='--version', hide=True)], help='OpenShift version to use for cluster creation.', validator=validate_version_format) @@ -76,13 +78,15 @@ def load_arguments(self, _): help='ResourceID of the DiskEncryptionSet to be used for master and worker VMs.', validator=validate_disk_encryption_set) c.argument('master_encryption_at_host', arg_type=get_three_state_flag(), - options_list=['--master-encryption-at-host', '--master-enc-host'], + options_list=['--master-encryption-at-host', + '--master-enc-host'], help='Encryption at host flag for master VMs. [Default: false]') c.argument('master_vm_size', help='Size of master VMs. [Default: Standard_D8s_v3]') c.argument('worker_encryption_at_host', arg_type=get_three_state_flag(), - options_list=['--worker-encryption-at-host', '--worker-enc-host'], + options_list=['--worker-encryption-at-host', + '--worker-enc-host'], help='Encryption at host flag for worker VMs. [Default: false]') c.argument('worker_vm_size', help='Size of worker VMs. [Default: Standard_D4s_v3]') @@ -123,6 +127,13 @@ def load_arguments(self, _): validator=validate_load_balancer_managed_outbound_ip_count, options_list=['--load-balancer-managed-outbound-ip-count', '--lb-ip-count']) + c.argument('enable_managed_identity', arg_group='Identity', arg_type=get_three_state_flag(), + help='Enable managed identity for this cluster.', is_preview=True) + c.argument('assign_platform_workload_identity', arg_group='Identity', + help='Assign a platform workload identity used within the cluster', is_preview=True, + options_list=['--assign-platform-workload-identity'], + action=AROPlatformWorkloadIdentityAddAction, nargs='+') + with self.argument_context('aro update') as c: c.argument('client_secret', help='Client secret of cluster service principal.', diff --git a/python/az/aro/azext_aro/custom.py b/python/az/aro/azext_aro/custom.py index d100ef21c5c..ddaf0179412 100644 --- a/python/az/aro/azext_aro/custom.py +++ b/python/az/aro/azext_aro/custom.py @@ -70,6 +70,8 @@ def aro_create(cmd, # pylint: disable=too-many-locals apiserver_visibility=None, ingress_visibility=None, load_balancer_managed_outbound_ip_count=None, + enable_managed_identity=False, + assign_platform_workload_identity=None, tags=None, version=None, no_wait=False): @@ -107,16 +109,18 @@ def aro_create(cmd, # pylint: disable=too-many-locals random_id = generate_random_id() aad = AADManager(cmd.cli_ctx) - if client_id is None: - client_id, client_secret = aad.create_application(cluster_resource_group or 'aro-' + random_id) - client_sp_id = aad.get_service_principal_id(client_id) - if not client_sp_id: - client_sp_id = aad.create_service_principal(client_id) + if not enable_managed_identity: + if client_id is None: + client_id, client_secret = aad.create_application(cluster_resource_group or 'aro-' + random_id) - rp_client_sp_id = aad.get_service_principal_id(resolve_rp_client_id()) - if not rp_client_sp_id: - raise ResourceNotFoundError("RP service principal not found.") + client_sp_id = aad.get_service_principal_id(client_id) + if not client_sp_id: + client_sp_id = aad.create_service_principal(client_id) + + rp_client_sp_id = aad.get_service_principal_id(resolve_rp_client_id()) + if not rp_client_sp_id: + raise ResourceNotFoundError("RP service principal not found.") if rp_mode_development(): worker_vm_size = worker_vm_size or 'Standard_D2s_v3' @@ -146,10 +150,6 @@ def aro_create(cmd, # pylint: disable=too-many-locals fips_validated_modules='Enabled' if fips_validated_modules else 'Disabled', version=version or '', ), - service_principal_profile=openshiftcluster.ServicePrincipalProfile( - client_id=client_id, - client_secret=client_secret, - ), network_profile=openshiftcluster.NetworkProfile( pod_cidr=pod_cidr or '10.128.0.0/14', service_cidr=service_cidr or '172.30.0.0/16', @@ -183,10 +183,25 @@ def aro_create(cmd, # pylint: disable=too-many-locals visibility=ingress_visibility or 'Public', ) ], + service_principal_profile=None, + platform_workload_identity_profile=None, ) - sp_obj_ids = [client_sp_id, rp_client_sp_id] - ensure_resource_permissions(cmd.cli_ctx, oc, True, sp_obj_ids) + if enable_managed_identity: + oc.platform_workload_identity_profile = openshiftcluster.PlatformWorkloadIdentityProfile( + platform_workload_identities=assign_platform_workload_identity + ) + + # TODO - perform client-side validation of required identity permissions + + else: + oc.service_principal_profile = openshiftcluster.ServicePrincipalProfile( + client_id=client_id, + client_secret=client_secret, + ) + + sp_obj_ids = [client_sp_id, rp_client_sp_id] + ensure_resource_permissions(cmd.cli_ctx, oc, True, sp_obj_ids) return sdk_no_wait(no_wait, client.open_shift_clusters.begin_create_or_update, resource_group_name=resource_group_name,