Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: federation improvements + template increment. #363

Merged
merged 3 commits into from
Sep 17, 2024
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
2 changes: 1 addition & 1 deletion azext_edge/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import os

VERSION = "0.7.0a8"
VERSION = "0.7.0a9"
EXTENSION_NAME = "azure-iot-ops"
EXTENSION_ROOT = os.path.dirname(os.path.abspath(__file__))
USER_AGENT = "IotOperationsCliExtension/{}".format(VERSION)
2 changes: 0 additions & 2 deletions azext_edge/edge/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,8 +629,6 @@ def load_iotops_help():
] = """
type: command
short-summary: Remove a user-assigned managed identity from the instance.
long-summary: |
This operation includes removing federation of the identity.

examples:
- name: Remove the desired user-assigned managed identity from the instance.
Expand Down
2 changes: 2 additions & 0 deletions azext_edge/edge/commands_edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,13 +272,15 @@ def instance_identity_assign(
mi_user_assigned: str,
federated_credential_name: Optional[str] = None,
usage_type: IdentityUsageType = IdentityUsageType.dataflow.value,
use_self_hosted_issuer: Optional[bool] = None,
**kwargs,
) -> dict:
return Instances(cmd).add_mi_user_assigned(
name=instance_name,
resource_group_name=resource_group_name,
mi_user_assigned=mi_user_assigned,
federated_credential_name=federated_credential_name,
use_self_hosted_issuer=use_self_hosted_issuer,
usage_type=usage_type,
**kwargs,
)
Expand Down
2 changes: 2 additions & 0 deletions azext_edge/edge/commands_secretsync.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def secretsync_enable(
keyvault_resource_id: str,
spc_name: Optional[str] = None,
skip_role_assignments: Optional[bool] = None,
use_self_hosted_issuer: Optional[bool] = None,
**kwargs,
) -> dict:
return Instances(cmd).enable_secretsync(
Expand All @@ -30,6 +31,7 @@ def secretsync_enable(
keyvault_resource_id=keyvault_resource_id,
spc_name=spc_name,
skip_role_assignments=skip_role_assignments,
use_self_hosted_issuer=use_self_hosted_issuer,
**kwargs,
)

Expand Down
6 changes: 6 additions & 0 deletions azext_edge/edge/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ def load_iotops_arguments(self, _):
options_list=["--fc"],
help="The federated credential name.",
)
context.argument(
"use_self_hosted_issuer",
options_list=["--self-hosted-issuer"],
arg_type=get_three_state_flag(),
help="Use the self-hosted oidc issuer for federation.",
)

with self.argument_context("iot ops identity") as context:
context.argument(
Expand Down
50 changes: 32 additions & 18 deletions azext_edge/edge/providers/orchestration/resources/instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,22 @@ def remove_mi_user_assigned(
mi_resource_id_container = parse_resource_id(mi_user_assigned)
instance = self.show(name=name, resource_group_name=resource_group_name)

cluster_resource = self.get_resource_map(instance).connected_cluster.resource
custom_location = self._get_associated_cl(instance)
namespace = custom_location["properties"]["namespace"]
oidc_issuer = self._ensure_oidc_issuer(cluster_resource)

cred_subject = get_cred_subject(namespace=namespace, service_account_name=SERVICE_ACCOUNT_DATAFLOW)
if not federated_credential_name:
federated_credential_name = get_fc_name(
cluster_name=cluster_resource["name"],
oidc_issuer=oidc_issuer,
subject=cred_subject,
)
self.unfederate_msi(mi_resource_id_container, federated_credential_name)
# TODO - @digimaun
# cluster_resource = self.get_resource_map(instance).connected_cluster.resource
# custom_location = self._get_associated_cl(instance)
# namespace = custom_location["properties"]["namespace"]
# oidc_issuer = self._ensure_oidc_issuer(cluster_resource)

# cred_subject = get_cred_subject(namespace=namespace, service_account_name=SERVICE_ACCOUNT_DATAFLOW)
# if not federated_credential_name:
# federated_credential_name = get_fc_name(
# cluster_name=cluster_resource["name"],
# oidc_issuer=oidc_issuer,
# subject=cred_subject,
# )
# TODO - @digimaun
if federated_credential_name:
self.unfederate_msi(mi_resource_id_container, federated_credential_name)

identity: dict = instance.get("identity", {})
if not identity:
Expand All @@ -190,6 +193,7 @@ def add_mi_user_assigned(
resource_group_name: str,
mi_user_assigned: str,
federated_credential_name: Optional[str] = None,
use_self_hosted_issuer: Optional[bool] = None,
**kwargs,
):
"""
Expand All @@ -199,7 +203,7 @@ def add_mi_user_assigned(
mi_resource_id_container = parse_resource_id(mi_user_assigned)
instance = self.show(name=name, resource_group_name=resource_group_name)
cluster_resource = self.get_resource_map(instance).connected_cluster.resource
oidc_issuer = self._ensure_oidc_issuer(cluster_resource)
oidc_issuer = self._ensure_oidc_issuer(cluster_resource, use_self_hosted_issuer)
custom_location = self._get_associated_cl(instance)
namespace = custom_location["properties"]["namespace"]
cred_subject = get_cred_subject(namespace=namespace, service_account_name=SERVICE_ACCOUNT_DATAFLOW)
Expand Down Expand Up @@ -234,6 +238,7 @@ def enable_secretsync(
federated_credential_name: Optional[str] = None,
spc_name: Optional[str] = None,
skip_role_assignments: bool = False,
use_self_hosted_issuer: Optional[bool] = None,
**kwargs,
):
mi_resource_id_container = parse_resource_id(mi_user_assigned)
Expand All @@ -242,6 +247,8 @@ def enable_secretsync(
keyvault: dict = self.resource_client.resources.get_by_id(
resource_id=keyvault_resource_id_container.resource_id, api_version=KEYVAULT_CLOUD_API_VERSION
)
# TODO - @digimaun
self.msi_mgmt_client._config.subscription_id = mi_resource_id_container.subscription_id
mi_user_assigned: dict = self.msi_mgmt_client.user_assigned_identities.get(
resource_group_name=mi_resource_id_container.resource_group_name,
resource_name=mi_resource_id_container.resource_name,
Expand All @@ -258,7 +265,7 @@ def enable_secretsync(
custom_location = self._get_associated_cl(instance)
namespace = custom_location["properties"]["namespace"]
cred_subject = get_cred_subject(namespace=namespace, service_account_name=SERVICE_ACCOUNT_SECRETSYNC)
oidc_issuer = self._ensure_oidc_issuer(cluster_resource)
oidc_issuer = self._ensure_oidc_issuer(cluster_resource, use_self_hosted_issuer)

cl_resources = resource_map.connected_cluster.get_aio_resources(custom_location_id=custom_location["id"])
secretsync_spc = self._find_existing_spc(cl_resources)
Expand Down Expand Up @@ -371,7 +378,7 @@ def _attempt_keyvault_role_assignments(self, keyvault: dict, mi_user_assigned: d
scope=keyvault["id"],
)

def _ensure_oidc_issuer(self, cluster_resource: dict) -> str:
def _ensure_oidc_issuer(self, cluster_resource: dict, use_self_hosted_issuer: Optional[bool] = None) -> str:
enabled_oidc = cluster_resource["properties"].get("oidcIssuerProfile", {}).get("enabled", False)
enabled_wlif = (
cluster_resource["properties"].get("securityProfile", {}).get("workloadIdentity", {}).get("enabled", False)
Expand All @@ -396,9 +403,10 @@ def _ensure_oidc_issuer(self, cluster_resource: dict) -> str:
raise ValidationError(error)

oidc_issuer_profile: dict = cluster_resource["properties"]["oidcIssuerProfile"]
issuer_url = oidc_issuer_profile.get("issuerUrl") or oidc_issuer_profile.get("selfHostedIssuerUrl")
issuer_key = "selfHostedIssuerUrl" if use_self_hosted_issuer else "issuerUrl"
issuer_url = oidc_issuer_profile.get(issuer_key)
if not issuer_url:
raise ValidationError("No issuer Url is available. Check cluster config.")
raise ValidationError(f"No {issuer_key} is available. Check cluster config.")
return issuer_url

def federate_msi(
Expand All @@ -418,6 +426,8 @@ def federate_msi(
"No new federated credential will be created."
)
return
# TODO - @digimaun
self.msi_mgmt_client._config.subscription_id = mi_resource_id_container.subscription_id
self.msi_mgmt_client.federated_identity_credentials.create_or_update(
resource_group_name=mi_resource_id_container.resource_group_name,
resource_name=mi_resource_id_container.resource_name,
Expand All @@ -436,6 +446,8 @@ def unfederate_msi(
mi_resource_id_container: ResourceIdContainer,
federated_credential_name: str,
):
# TODO - @digimaun
self.msi_mgmt_client._config.subscription_id = mi_resource_id_container.subscription_id
self.msi_mgmt_client.federated_identity_credentials.delete(
resource_group_name=mi_resource_id_container.resource_group_name,
resource_name=mi_resource_id_container.resource_name,
Expand All @@ -445,6 +457,8 @@ def unfederate_msi(
def _find_federated_cred(
self, mi_resource_id_container: ResourceIdContainer, issuer_url: str, subject: str
) -> Optional[dict]:
# TODO - @digimaun
self.msi_mgmt_client._config.subscription_id = mi_resource_id_container.subscription_id
cred_iteratable = self.msi_mgmt_client.federated_identity_credentials.list(
resource_group_name=mi_resource_id_container.resource_group_name,
resource_name=mi_resource_id_container.resource_name,
Expand Down
4 changes: 2 additions & 2 deletions azext_edge/edge/providers/orchestration/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def copy(self) -> "TemplateBlueprint":
IOT_OPERATIONS_VERSION_MONIKER = "v0.7.0-preview"

M2_ENABLEMENT_TEMPLATE = TemplateBlueprint(
commit_id="0f5d0d234045e945ab7808c6aa2a44dc0ce77723",
commit_id="a3a65663793fc761928c736b5e7732c68c0591d6",
content={
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"languageVersion": "2.0",
Expand Down Expand Up @@ -218,7 +218,7 @@ def copy(self) -> "TemplateBlueprint":
"AIO_EXTENSION_SUFFIX": "[take(uniqueString(resourceId('Microsoft.Kubernetes/connectedClusters', parameters('clusterName'))), 5)]",
"VERSIONS": {
"platform": "0.7.6",
"aio": "0.7.13",
"aio": "0.7.21",
"secretSyncController": "0.6.4",
"edgeStorageAccelerator": "2.1.1-preview",
"openServiceMesh": "1.2.9",
Expand Down