diff --git a/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/README.md b/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/README.md index f3c0953d6f2..f88659b560d 100644 --- a/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/README.md +++ b/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/README.md @@ -29,23 +29,42 @@ The *control plane RG* is a container for the *persistent* resources of MLOS (re ```shell # With Powershell, recommended to use Powershell 7 + # If provisioning results DB include '-resultsDbArmsParamsFile', otherwise omit + + # If setting up with a Service Principal ./setup-rg.ps1 ` -controlPlaneArmParamsFile $controlPlaneArmParamsFile ` - -resultsDbArmParamsFile $resultsDbArmParamsFile # If provisioning results DB, otherwise omit ` - -servicePrincipalName $servicePrincipalName ` -resourceGroupName $resourceGroupName ` + -resultsDbArmParamsFile $resultsDbArmParamsFile ` + -servicePrincipalName $servicePrincipalName ` -certName $certName + + # If setting up with a Managed Identity + ./setup-rg.ps1 ` + -controlPlaneArmParamsFile $controlPlaneArmParamsFile ` + -resourceGroupName $resourceGroupName ` + -resultsDbArmParamsFile $resultsDbArmParamsFile ` + -managedIdentityname $managedIdentityName ``` ```sh # With bash # If provisioning results DB include '--resultsDbArmsParamsFile', otherwise omit + + # If setting up with a Service Principal ./setup-rg.sh \ --controlPlaneArmParamsFile $controlPlaneArmParamsFile \ + --resourceGroupName $resourceGroupName \ --resultsDbArmParamsFile $resultsDbArmParamsFile \ --servicePrincipalName $servicePrincipalName \ - --resourceGroupName $resourceGroupName \ --certName $certName + + # If setting up with a Managed Identity + ./setup-rg.sh \ + --controlPlaneArmParamsFile $controlPlaneArmParamsFile \ + --resourceGroupName $resourceGroupName \ + --resultsDbArmParamsFile $resultsDbArmParamsFile \ + --managedIdentityname $managedIdentityName ``` where `$*ArmParamsFile` can be the corresponding `*.parameters.json` and from before. However, it also follows the same usage as `--parameters` in [az deployment group create](https://learn.microsoft.com/en-us/cli/azure/deployment/group?view=azure-cli-latest#az-deployment-group-create-examples). diff --git a/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/rg-template.json b/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/rg-template.json index 8fbe12e518c..d823a69b2ae 100644 --- a/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/rg-template.json +++ b/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/rg-template.json @@ -363,6 +363,13 @@ "kvName": { "type": "string", "value": "[variables('kvName')]" + }, + "storageAccountNames": { + "type": "array", + "copy": { + "count": "[length(parameters('storageAccountLocations'))]", + "input": "[concat(variables('storageAccountName'), parameters('storageAccountLocations')[copyIndex()])]" + } } } } diff --git a/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/setup-rg.ps1 b/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/setup-rg.ps1 index c32e41279b0..ecb6f0375e4 100644 --- a/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/setup-rg.ps1 +++ b/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/setup-rg.ps1 @@ -11,14 +11,17 @@ param( # Results DB ARM template params [Parameter(Mandatory=$False)] [string] $resultsDbArmParamsFile, - # Other params - [Parameter(Mandatory=$True)] - [string] $servicePrincipalName, [Parameter(Mandatory=$True)] [string] $resourceGroupName, - [Parameter(Mandatory=$True)] + # Managed Identity params + [Parameter(Mandatory=$True, ParameterSetName="ByMI")] + [string] $managedIdentityName, + # Service Principal params + [Parameter(Mandatory=$True, ParameterSetName="BySP")] + [string] $servicePrincipalName, + [Parameter(Mandatory=$True, ParameterSetName="BySP")] [string] $certName, - [Parameter(Mandatory=$False)] + [Parameter(Mandatory=$False, ParameterSetName="BySP")] [int] $certExpirationYears = 1 ) @@ -36,6 +39,9 @@ if (!$?) { return } +$vmName = $deploymentResults.properties.outputs.vmName.value +$storageAccountNames = $deploymentResults.properties.outputs.storageAccountNames.value + # Conditional provisioning of results DB if ($resultsDbArmParamsFile) { Write-Output "Provisioning results DB..." @@ -51,7 +57,6 @@ if ($resultsDbArmParamsFile) { } else { $dbName = $dbDeploymentResults.properties.outputs.dbName.value - $vmName = $deploymentResults.properties.outputs.vmName.value $vmIpAddress = $deploymentResults.properties.outputs.vmIpAddress.value # VM IP access for results DB @@ -83,41 +88,77 @@ $certThumbprint = az keyvault certificate show ` 2>$null ` || "NOCERT" -if ($certThumbprint == "NOCERT") { - # The cert does not exist yet. - # Create the service principal if doesn't exist, storing the cert in the keyvault - # If it does exist, this also patches the current service principal with the role - az ad sp create-for-rbac ` - --name $servicePrincipalName ` - --role "Contributor" ` - --scopes $resourceGroupId ` - --create-cert ` - --cert $certName ` - --keyvault $kvName ` - --years $certExpirationYears -} else { - # The cert already exists in the keyvault. - - # Ensure the SP exists with correct roles, without creating a cert. - az ad sp create-for-rbac ` - --name $servicePrincipalName ` - --role "Contributor" ` - --scopes $resourceGroupId ` - - # SP's certs, which are stored in the registered application instead - $servicePrincipalAppId = az ad sp list ` - --display-name $servicePrincipalName ` - --query "[?servicePrincipalType == 'Application'].appId" ` - --output tsv - $spCertThumbprints = az ad app credential list ` - --id $servicePrincipalAppId ` - --cert ` - --query "[].customKeyIdentifier" ` - --output tsv - - if ($spCertThumbprints.Contains($certThumbprint)) { - Write-Output "Keyvault contains the certificate '$certName' that is linked to the service principal '$servicePrincipalName' already." - } else { - Write-Warning "Keyvault already contains a certificate called '$certName', but is not linked with the service principal '$servicePrincipalName'! Skipping cert handling" +switch ($PSCmdlet.ParameterSetName) { + "BySP" { + if ($certThumbprint -eq "NOCERT") { + # The cert does not exist yet. + # Create the service principal if doesn't exist, storing the cert in the keyvault + # If it does exist, this also patches the current service principal with the role + az ad sp create-for-rbac ` + --name $servicePrincipalName ` + --role "Contributor" ` + --scopes $resourceGroupId ` + --create-cert ` + --cert $certName ` + --keyvault $kvName ` + --years $certExpirationYears + } else { + # The cert already exists in the keyvault. + + # Ensure the SP exists with correct roles, without creating a cert. + az ad sp create-for-rbac ` + --name $servicePrincipalName ` + --role "Contributor" ` + --scopes $resourceGroupId ` + + # SP's certs, which are stored in the registered application instead + $servicePrincipalAppId = az ad sp list ` + --display-name $servicePrincipalName ` + --query "[?servicePrincipalType == 'Application'].appId" ` + --output tsv + $spCertThumbprints = az ad app credential list ` + --id $servicePrincipalAppId ` + --cert ` + --query "[].customKeyIdentifier" ` + --output tsv + + if ($spCertThumbprints.Contains($certThumbprint)) { + Write-Output "Keyvault contains the certificate '$certName' that is linked to the service principal '$servicePrincipalName' already." + } else { + Write-Warning "Keyvault already contains a certificate called '$certName', but is not linked with the service principal '$servicePrincipalName'! Skipping cert handling" + } + } + break + } + "ByMI" { + # Ensure the user managed identity is created + $miId = az identity create ` + --name $managedIdentityName ` + --resource-group $resourceGroupName ` + --query "principalId" --output tsv + + Write-Output "Using managed identity $managedIdentityName with principalId $miId" + + Write-Output "Assigning the identity access to the Resource Group..." + az role assignment create ` + --assignee $miId ` + --role "Contributor" ` + --scope $resourceGroupId + + # Assign the identity to the VM + Write-Output "Assigning the identity to the VM..." + az vm identity assign --name $vmName --resource-group $resourceGroupName --identities $managedIdentityName + + # Assign the identity access to the storage accounts + foreach ($storageAccountName in $storageAccountNames) { + Write-Output "Assigning the identity the role for $storageAccountName..." + $storageAccountResourceId = az storage account show --name $storageAccountName --resource-group $resourceGroupName --query "id" --output tsv + az role assignment create ` + --assignee $miId ` + --role "Storage File Data Privileged Contributor" ` + --scope $storageAccountResourceId + } + break } } + diff --git a/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/setup-rg.sh b/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/setup-rg.sh index 27e4cb53760..ea8875745e6 100755 --- a/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/setup-rg.sh +++ b/mlos_bench/mlos_bench/config/services/remote/azure/scripts/setup-rg/setup-rg.sh @@ -19,6 +19,12 @@ $script_name --resultsDbArmParamsFile someResultsDbArmParamsFile.json --servicePrincipalName someServicePrincipalName --certName someCertName +OR +$script_name + --resourceGroupName someRgName + --controlPlaneArmParamsFile someControlPlaneArmParamsFile.json + --resultsDbArmParamsFile someResultsDbArmParamsFile.json + --managedIdentityName someManagedIdentityName USAGE exit 1 } @@ -36,15 +42,20 @@ case $1 in resultsDbArmParamsFile="$2" shift 2 ;; - # Other params - --servicePrincipalName) - servicePrincipalName="$2" - shift 2 - ;; --resourceGroupName) resourceGroupName="$2" shift 2 ;; + # Managed Identity params + --managedIdentityName) + managedIdentityName="$2" + shift 2 + ;; + # Service Principal params + --servicePrincipalName) + servicePrincipalName="$2" + shift 2 + ;; --certName) certName="$2" shift 2 @@ -64,9 +75,9 @@ if [ -z "${resourceGroupName:-}" ]; then usage "missing required resourceGroupName" elif [ -z "${controlPlaneArmParamsFile:-}" ]; then usage "missing required controlPlaneArmParamsFile" -elif [ -z "${servicePrincipalName:-}" ]; then - usage "missing required servicePrincipalName" -elif [ -z "${certName:-}" ]; then +elif [ -z "${servicePrincipalName:-}" ] && [ -z "${managedIdentityName:-}" ]; then + usage "missing required servicePrincipalName or managedIdentityName" +elif [ -n "${servicePrincipalName:-}" ] && [ -z "${certName:-}" ]; then usage "missing required certName" fi @@ -102,6 +113,9 @@ if [[ $? -ne 0 ]]; then exit 1 fi +vmName=$(echo "$deploymentResults" | jq -r ".properties.outputs.vmName.value") +storageAccountNames=$(echo "$deploymentResults" | jq -r ".properties.outputs.storageAccountNames.value[]") + # Conditional provisioning of results DB if [[ "$resultsDbArmParamsFile" ]]; then echo "Provisioning results DB..." @@ -116,7 +130,6 @@ if [[ "$resultsDbArmParamsFile" ]]; then echo "Error in provisioning results DB!" else dbName=$(echo "$dbDeploymentResults" | jq -r ".properties.outputs.dbName.value") - vmName=$(echo "$deploymentResults" | jq -r ".properties.outputs.vmName.value") vmIpAddress=$(echo "$deploymentResults" | jq -r ".properties.outputs.vmIpAddress.value") # VM IP access for results DB @@ -140,51 +153,83 @@ az role assignment create \ --role "Key Vault Administrator" \ --scope "$kvId" -# Check if cert of same name exists in keyvault already -certThumbprint=$(az keyvault certificate show \ - --name "$certName" \ - --vault-name "$kvName" \ - --query "x509ThumbprintHex" --output tsv \ - 2> /dev/null \ - || echo "NOCERT" \ - ) - -if [[ $certThumbprint == "NOCERT" ]]; then - # The cert does not exist yet. - # Create the service principal if doesn't exist, storing the cert in the keyvault - # If it does exist, this also patches the current service principal with the role - az ad sp create-for-rbac \ - --name "$servicePrincipalName" \ - --role "Contributor" \ - --scopes "$resourceGroupId" \ - --create-cert \ - --cert "$certName" \ - --keyvault "$kvName" \ - --years "$certExpirationYears" -else - # The cert already exists in the keyvault. - - # Ensure the SP exists with correct roles, without creating a cert. - az ad sp create-for-rbac \ - --name "$servicePrincipalName" \ - --role "Contributor" \ - --scopes "$resourceGroupId" - - # SP's certs, which are stored in the registered application instead - servicePrincipalAppId=$(az ad sp list \ - --display-name "$servicePrincipalName" \ - --query "[?servicePrincipalType == 'Application'].appId" \ - --output tsv \ - ) - spCertThumbprints=$(az ad app credential list \ - --id "$servicePrincipalAppId" \ - --cert \ - --query "[].customKeyIdentifier" \ - --output tsv \ +if [ -n "${servicePrincipalName:-}" ]; then + # Check if cert of same name exists in keyvault already + certThumbprint=$(az keyvault certificate show \ + --name "$certName" \ + --vault-name "$kvName" \ + --query "x509ThumbprintHex" --output tsv \ + 2> /dev/null \ + || echo "NOCERT" \ ) - if [[ $spCertThumbprints == *$certThumbprint* ]]; then - echo "Keyvault contains the certificate '$certName' that is linked to the service principal '$servicePrincipalName' already." + + if [[ $certThumbprint == "NOCERT" ]]; then + # The cert does not exist yet. + # Create the service principal if doesn't exist, storing the cert in the keyvault + # If it does exist, this also patches the current service principal with the role + az ad sp create-for-rbac \ + --name "$servicePrincipalName" \ + --role "Contributor" \ + --scopes "$resourceGroupId" \ + --create-cert \ + --cert "$certName" \ + --keyvault "$kvName" \ + --years "$certExpirationYears" else - echo "Keyvault already contains a certificate called '$certName', but is not linked with the service principal '$servicePrincipalName'! Skipping cert handling" + # The cert already exists in the keyvault. + + # Ensure the SP exists with correct roles, without creating a cert. + az ad sp create-for-rbac \ + --name "$servicePrincipalName" \ + --role "Contributor" \ + --scopes "$resourceGroupId" + + # SP's certs, which are stored in the registered application instead + servicePrincipalAppId=$(az ad sp list \ + --display-name "$servicePrincipalName" \ + --query "[?servicePrincipalType == 'Application'].appId" \ + --output tsv \ + ) + spCertThumbprints=$(az ad app credential list \ + --id "$servicePrincipalAppId" \ + --cert \ + --query "[].customKeyIdentifier" \ + --output tsv \ + ) + if [[ $spCertThumbprints == *$certThumbprint* ]]; then + echo "Keyvault contains the certificate '$certName' that is linked to the service principal '$servicePrincipalName' already." + else + echo "Keyvault already contains a certificate called '$certName', but is not linked with the service principal '$servicePrincipalName'! Skipping cert handling" + fi fi +elif [ -n "${managedIdentityName:-}" ]; then + # Ensure the user managed identity is created + miId=$(az identity create \ + --name "${managedIdentityName}" \ + --resource-group "${resourceGroupName}" \ + --query "principalId" --output tsv \ + ) + echo "Using managed identity ${managedIdentityName} with principalId $miId" + + # Assign the identity access to the resource group + echo "Assigning the identity access to the Resource Group..." + az role assignment create \ + --assignee "${miId}" \ + --role "Contributor" \ + --scope "${resourceGroupId}" + + # Assign the identity to the VM + echo "Assigning the identity to the VM..." + az vm identity assign --name "${vmName}" --resource-group "${resourceGroupName}" --identities "${managedIdentityName}" + + # Assign the identity access to the storage accounts + for storageAccountName in $storageAccountNames; do + echo "Assigning the identity the role for ${storageAccountName}..." + storageAccountResourceId=$(az storage account show --name "${storageAccountName}" --resource-group "${resourceGroupName}" --query "id" --output tsv) + az role assignment create \ + --assignee "${miId}" \ + --role "Storage File Data Privileged Contributor" \ + --scope "${storageAccountResourceId}" + + done fi