diff --git a/CHANGELOG.md b/CHANGELOG.md index 21014de747..05184d5771 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ ENHANCEMENTS: * Airlock requests contain a field with information about the files that were submitted ([#2504](https://github.com/microsoft/AzureTRE/pull/2504)) * UI - Operations and notifications stability improvements ([[#2530](https://github.com/microsoft/AzureTRE/pull/2530)) * UI - Initial implemetation of Workspace Airlock Request View ([#2512](https://github.com/microsoft/AzureTRE/pull/2512)) +* Azure ML workspace service assigns Azure ML Data Scientist role to Workspace Researchers ([#2539](https://github.com/microsoft/AzureTRE/pull/2539)) BUG FIXES: diff --git a/docs/tre-templates/workspace-services/azure-ml.md b/docs/tre-templates/workspace-services/azure-ml.md index d8836ec802..540ed6cfa3 100644 --- a/docs/tre-templates/workspace-services/azure-ml.md +++ b/docs/tre-templates/workspace-services/azure-ml.md @@ -6,6 +6,8 @@ This service installs the following resources into an existing virtual network w ![Azure Machine Learning Service](images/aml_service.png) +Any users with the role of `Workspace Researcher` will be assigned the `AzureML Data Scientist` role within the AML workspace. + ## Firewall Rules Please be aware that the following outbound Firewall rules are opened for the workspace when this service is deployed, including to Azure Storage. This does open the possibility to extract data from a workspace if the user is determined to do so. Work is ongoing to remove some of these requirements: diff --git a/templates/workspace_services/azureml/porter.yaml b/templates/workspace_services/azureml/porter.yaml index 65312294c3..3d9f9fdb3c 100644 --- a/templates/workspace_services/azureml/porter.yaml +++ b/templates/workspace_services/azureml/porter.yaml @@ -1,11 +1,19 @@ --- name: tre-service-azureml -version: 0.4.4 +version: 0.4.5 description: "An Azure TRE service for Azure Machine Learning" registry: azuretre dockerfile: Dockerfile.tmpl credentials: + # Credentials for interacting with the AAD Auth tenant + - name: auth_client_id + env: AUTH_CLIENT_ID + - name: auth_client_secret + env: AUTH_CLIENT_SECRET + - name: auth_tenant_id + env: AUTH_TENANT_ID + # Credentials for interacting with Azure - name: azure_tenant_id env: ARM_TENANT_ID - name: azure_subscription_id @@ -92,6 +100,9 @@ install: arm_client_id: "{{ bundle.credentials.azure_client_id }}" arm_client_secret: "{{ bundle.credentials.azure_client_secret }}" arm_use_msi: "{{ bundle.parameters.arm_use_msi }}" + auth_client_id: "{{ bundle.credentials.auth_client_id }}" + auth_client_secret: "{{ bundle.credentials.auth_client_secret }}" + auth_tenant_id: "{{ bundle.credentials.auth_tenant_id }}" backendConfig: resource_group_name: "{{ bundle.parameters.tfstate_resource_group_name }}" storage_account_name: "{{ bundle.parameters.tfstate_storage_account_name }}" @@ -118,6 +129,9 @@ upgrade: arm_client_id: "{{ bundle.credentials.azure_client_id }}" arm_client_secret: "{{ bundle.credentials.azure_client_secret }}" arm_use_msi: "{{ bundle.parameters.arm_use_msi }}" + auth_client_id: "{{ bundle.credentials.auth_client_id }}" + auth_client_secret: "{{ bundle.credentials.auth_client_secret }}" + auth_tenant_id: "{{ bundle.credentials.auth_tenant_id }}" backendConfig: resource_group_name: "{{ bundle.parameters.tfstate_resource_group_name }}" storage_account_name: "{{ bundle.parameters.tfstate_storage_account_name }}" @@ -144,6 +158,9 @@ uninstall: arm_tenant_id: "{{ bundle.credentials.azure_tenant_id }}" arm_client_id: "{{ bundle.credentials.azure_client_id }}" arm_client_secret: "{{ bundle.credentials.azure_client_secret }}" + auth_client_id: "{{ bundle.credentials.auth_client_id }}" + auth_client_secret: "{{ bundle.credentials.auth_client_secret }}" + auth_tenant_id: "{{ bundle.credentials.auth_tenant_id }}" backendConfig: resource_group_name: "{{ bundle.parameters.tfstate_resource_group_name }}" storage_account_name: "{{ bundle.parameters.tfstate_storage_account_name }}" diff --git a/templates/workspace_services/azureml/terraform/get_app_role_members.sh b/templates/workspace_services/azureml/terraform/get_app_role_members.sh new file mode 100755 index 0000000000..9de90ca735 --- /dev/null +++ b/templates/workspace_services/azureml/terraform/get_app_role_members.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -euo pipefail + +eval "$(jq -r '@sh "AUTH_CLIENT_ID=\(.auth_client_id) AUTH_CLIENT_SECRET=\(.auth_client_secret) AUTH_TENANT_ID=\(.auth_tenant_id) WORSKPACE_CLIENT_ID=\(.workspace_client_id)"')" + +az login --allow-no-subscriptions --service-principal --username "$AUTH_CLIENT_ID" --password "$AUTH_CLIENT_SECRET" --tenant "$AUTH_TENANT_ID" > /dev/null + +# get the service principal object id +sp=$(az rest --method GET --uri "https://graph.microsoft.com/v1.0/serviceprincipals?\$filter=appid eq '${WORSKPACE_CLIENT_ID}'" -o json) +spId=$(echo "$sp" | jq -r '.value[0].id') + +# filter to the Workspace Researcher Role +workspaceResearcherRoleId=$(echo "$sp" | jq -r '.value[0].appRoles[] | select(.value == "WorkspaceResearcher") | .id') +principals=$(az rest --method GET --uri "https://graph.microsoft.com/v1.0/serviceprincipals/${spId}/appRoleAssignedTo" -o json | jq -r --arg workspaceResearcherRoleId "${workspaceResearcherRoleId}" '.value[] | select(.appRoleId == $workspaceResearcherRoleId) | .principalId') + +jq -n --arg principals "$principals" '{"principals":$principals}' diff --git a/templates/workspace_services/azureml/terraform/roles.tf b/templates/workspace_services/azureml/terraform/roles.tf new file mode 100644 index 0000000000..6c32067682 --- /dev/null +++ b/templates/workspace_services/azureml/terraform/roles.tf @@ -0,0 +1,27 @@ + +data "azurerm_key_vault_secret" "workspace_client_id" { + name = "workspace-client-id" + key_vault_id = data.azurerm_key_vault.ws.id +} + +data "external" "app_role_members" { + program = ["bash", "${path.module}/get_app_role_members.sh"] + + query = { + auth_client_id = var.auth_client_id + auth_client_secret = var.auth_client_secret + auth_tenant_id = var.auth_tenant_id + workspace_client_id = data.azurerm_key_vault_secret.workspace_client_id.value + } +} + +data "azurerm_role_definition" "azure_ml_data_scientist" { + name = "AzureML Data Scientist" +} + +resource "azurerm_role_assignment" "app_role_members_aml_data_scientist" { + for_each = toset(split("\n", data.external.app_role_members.result.principals)) + scope = azapi_resource.aml_workspace.id + role_definition_id = data.azurerm_role_definition.azure_ml_data_scientist.id + principal_id = each.value +} diff --git a/templates/workspace_services/azureml/terraform/variables.tf b/templates/workspace_services/azureml/terraform/variables.tf index 042f237a72..2269674339 100644 --- a/templates/workspace_services/azureml/terraform/variables.tf +++ b/templates/workspace_services/azureml/terraform/variables.tf @@ -9,3 +9,16 @@ variable "arm_client_id" {} variable "arm_client_secret" {} variable "display_name" {} variable "description" {} + +variable "auth_tenant_id" { + type = string + description = "Used to authenticate into the AAD Tenant to get app role members" +} +variable "auth_client_id" { + type = string + description = "Used to authenticate into the AAD Tenant to get app role members" +} +variable "auth_client_secret" { + type = string + description = "Used to authenticate into the AAD Tenant to get app role members" +}