diff --git a/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/solution.md b/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/solution.md index a08bd72f3..5dbb3c3fe 100644 --- a/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/solution.md +++ b/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/solution.md @@ -24,23 +24,25 @@ The deployment uses ARM templates as this is the most robust way for deployment # Get current user's number (e.g., LabUser-37@... or hackuser-067@... -> 37 or 67) export azure_user=$(az account show --query user.name --output tsv) export user_number=$(echo "$azure_user" | cut -d'@' -f1 | sed -E -n 's/.*[^0-9]([0-9]+)$/\1/p' | sed 's/^0*//') -export subscription_id=$(az account show --query id -o tsv) export resource_group="$user_number-k8s-arc" +export connected_cluster_name="$user_number-k8s-arc-enabled" +export extension_name="$user_number-arc-data-controller-ext" +export namespace="$user_number-arc-data-controller" +export custom_location_name="$user_number-arc-data-controller" +export data_controller_name="$user_number-dc" # Get Log Analytics workspace details export log_analytics_workspace_id=$(az monitor log-analytics workspace show -g $resource_group -n "$user_number-law" --query customerId -o tsv) export log_analytics_primary_key=$(az monitor log-analytics workspace get-shared-keys -g $resource_group -n "$user_number-law" --query primarySharedKey -o tsv) -# Generate new GUIDs for role assignments -export guid1=$(uuidgen) -export guid2=$(uuidgen) - echo "Your values:" -echo "USER_NUMBER: $user_number" -echo "SUBSCRIPTION_ID: $subscription_id" +echo "RESOURCE_GROUP: $resource_group" +echo "CONNECTED_CLUSTER_NAME: $connected_cluster_name" +echo "EXTENSION_NAME: $extension_name" +echo "NAMESPACE: $namespace" +echo "CUSTOM_LOCATION_NAME: $custom_location_name" +echo "DATA_CONTROLLER_NAME: $data_controller_name" echo "LOG_ANALYTICS_WORKSPACE_ID: $log_analytics_workspace_id" -echo "GUID 1: $guid1" -echo "GUID 2: $guid2" ``` ### Step 2: Update parameters.json @@ -59,21 +61,25 @@ cp parameters.json parameters-my.json # Note: Escape special sed characters in the primary key to handle any potential delimiters escaped_key=$(printf '%s\n' "$log_analytics_primary_key" | sed 's/[\/&]/\\&/g') -sed -i "s//$user_number/g" parameters-my.json -sed -i "s//$subscription_id/g" parameters-my.json +sed -i "s//$connected_cluster_name/g" parameters-my.json +sed -i "s//$extension_name/g" parameters-my.json +sed -i "s//$namespace/g" parameters-my.json +sed -i "s//$custom_location_name/g" parameters-my.json +sed -i "s//$data_controller_name/g" parameters-my.json sed -i "s//$log_analytics_workspace_id/g" parameters-my.json sed -i "s//$escaped_key/g" parameters-my.json -sed -i "s//$guid1/g" parameters-my.json -sed -i "s//$guid2/g" parameters-my.json +sed -i "s//mhadmin/g" parameters-my.json # Alternative: manual replacement # Edit parameters-my.json and replace: -# with your user number -# with your subscription ID +# with the name of your arc-enabled connected cluster +# with the desired extension name +# with the Kubernetes namespace for arc data services +# with the custom location name +# with the desired data controller name # with workspace ID # with workspace key -# with first new GUID (use 'uuidgen' command) -# with second new GUID (use 'uuidgen' command again) +# with the desired dashboard username (e.g., mhadmin) ``` ### Step 3: Deploy the Arc Data Controller @@ -121,13 +127,12 @@ az deployment operation group list \ ```bash # Set SQL MI name (must start with a letter for Kubernetes naming rules) export sqlmi_name="sqlmi-${user_number}-1" -export custom_location="${user_number}-onprem" # Create SQL MI (you'll be prompted for sql admin user and password) az sql mi-arc create \ --name $sqlmi_name \ --resource-group $resource_group \ - --custom-location $custom_location \ + --custom-location $custom_location_name \ --cores-request 1 \ --memory-request 3Gi ``` @@ -139,7 +144,7 @@ The creation takes 5-10 minutes. Monitor progress: az sql mi-arc show --name $sqlmi_name --resource-group $resource_group --query "properties.k8sRaw.status.state" -o tsv # Check pods in the namespace -kubectl get pods -n ${user_number}-onprem -l app.kubernetes.io/name=$sqlmi_name +kubectl get pods -n $namespace -l app.kubernetes.io/name=$sqlmi_name ``` ## Task 3 - Connect to your SQL Managed Instance @@ -155,7 +160,7 @@ kubectl get pods -n ${user_number}-onprem -l app.kubernetes.io/name=$sqlmi_name master_ip=$(az vm list-ip-addresses --resource-group "${user_number}-k8s-onprem" --name "${user_number}-k8s-master" --query "[0].virtualMachine.network.privateIpAddresses[0]" --output tsv) # Get the NodePort assigned to SQL MI -node_port=$(kubectl get svc ${sqlmi_name}-external-svc -n ${user_number}-onprem -o jsonpath='{.spec.ports[0].nodePort}') +node_port=$(kubectl get svc ${sqlmi_name}-external-svc -n $namespace -o jsonpath='{.spec.ports[0].nodePort}') echo "Connection details:" echo "Server: $master_ip,$node_port" @@ -179,7 +184,7 @@ echo "Server=$master_ip,$node_port;Database=master;User Id=sa;Password=,` (e.g., `20.123.45.67,31433`, optionally, use the following command: ```echo "$master_ip,$(kubectl get svc ${sqlmi_name}-external-svc -n ${custom_location} -o jsonpath='{.spec.ports[0].nodePort}')"``` + - **Server:** `,` (e.g., `20.123.45.67,31433`, optionally, use the following command: ```echo "$master_ip,$(kubectl get svc ${sqlmi_name}-external-svc -n $namespace -o jsonpath='{.spec.ports[0].nodePort}')"``` - **Trust Server Certificate:** Yes - **Authentication Type:** SQL Login - **User name:** (the admin account you entered during SQL MI creation) diff --git a/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/templates/parameters.json b/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/templates/parameters.json index 07a78606a..91a0e0d89 100644 --- a/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/templates/parameters.json +++ b/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/templates/parameters.json @@ -2,46 +2,23 @@ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - "parentResource": { - "value": "Microsoft.Kubernetes/connectedClusters/-k8s-arc-enabled" + "connectedClusterName": { + "value": "" }, "k8sExtensionName": { - "value": "-onprem-ext" + "value": "" }, "namespace": { - "value": "-onprem" + "value": "" }, "location": { "value": "westeurope" }, - "roleDefinitionId_Contributor": { - "value": "/subscriptions//resourceGroups/-k8s-arc/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" - }, - "roleAssignment_Contributor": { - "value": "" - }, - "roleDefinitionId_MonitoringMetricsPublisher": { - "value": "/subscriptions//resourceGroups/-k8s-arc/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb" - }, - "roleAssignment_MonitoringMetricsPublisher": { - "value": "" - }, "customLocationName": { - "value": "-onprem" - }, - "customLocationId": { - "value": "/subscriptions//resourceGroups/-k8s-arc/providers/Microsoft.ExtendedLocation/customLocations/-onprem" + "value": "" }, - "customLocation_hostResourceId": { - "value": "/subscriptions//resourceGroups/-k8s-arc/providers/Microsoft.Kubernetes/connectedClusters/-k8s-arc-enabled" - }, - "customLocation_clusterExtensionIds": { - "value": [ - "/subscriptions//resourceGroups/-k8s-arc/providers/Microsoft.Kubernetes/connectedClusters/-k8s-arc-enabled/providers/Microsoft.KubernetesConfiguration/extensions/-onprem-ext" - ] - }, - "resourceSyncRuleName": { - "value": "-onprem/defaultResourceSyncRule" + "dataController_metricsAndLogsDashboardUsername": { + "value": "" }, "dataController_metricsAndLogsDashboardPassword": { "value": null @@ -55,14 +32,8 @@ "storageClass": { "value": "local-path" }, - "dataController_subscription": { - "value": "" - }, - "dataController_resourceGroup": { - "value": "-k8s-arc" - }, "dataControllerName": { - "value": "-dc" + "value": "" } } } \ No newline at end of file diff --git a/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/templates/template.json b/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/templates/template.json index 58d89efda..e40aea7bb 100644 --- a/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/templates/template.json +++ b/03-Azure/01-03-Infrastructure/03_Hybrid_Azure_Arc_Kubernetes/walkthrough/challenge-04/templates/template.json @@ -2,10 +2,10 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { - "parentResource": { + "connectedClusterName": { "type": "String", "metadata": { - "description": "The resource ID of the arc-enabled K8s cluster where the extension will be installed." + "description": "The name of the arc-enabled connected Kubernetes cluster." } }, "k8sExtensionName": { @@ -24,28 +24,18 @@ "type": "String", "defaultValue": "westeurope" }, - "roleDefinitionId_Contributor": { - "type": "String", - "metadata": { - "description": "The role definition ID for the Contributor role." - } - }, "roleAssignment_Contributor": { "type": "String", + "defaultValue": "[newGuid()]", "metadata": { - "description": "The role assignment name (GUID) for the Contributor role." - } - }, - "roleDefinitionId_MonitoringMetricsPublisher": { - "type": "String", - "metadata": { - "description": "The role definition ID for the Monitoring Metrics Publisher role." + "description": "The role assignment name (GUID) for the Contributor role. Auto-generated if not provided." } }, "roleAssignment_MonitoringMetricsPublisher": { "type": "String", + "defaultValue": "[newGuid()]", "metadata": { - "description": "The role assignment name (GUID) for the Monitoring Metrics Publisher role." + "description": "The role assignment name (GUID) for the Monitoring Metrics Publisher role. Auto-generated if not provided." } }, "customLocationName": { @@ -54,28 +44,10 @@ "description": "The name of the custom location." } }, - "customLocationId": { - "type": "String", - "metadata": { - "description": "The ID of the custom location." - } - }, - "customLocation_hostResourceId": { + "dataController_metricsAndLogsDashboardUsername": { "type": "String", "metadata": { - "description": "The host resource ID of the custom location." - } - }, - "customLocation_clusterExtensionIds": { - "type": "Array", - "metadata": { - "description": "The cluster extension IDs of the custom location." - } - }, - "resourceSyncRuleName": { - "type": "String", - "metadata": { - "description": "The name of the resource sync rule." + "description": "The username for the metrics and logs dashboard." } }, "dataController_metricsAndLogsDashboardPassword": { @@ -100,12 +72,6 @@ "description": "The storage class for data and logs storage." } }, - "dataController_subscription": { - "type": "String" - }, - "dataController_resourceGroup": { - "type": "String" - }, "dataControllerName": { "type": "String", "metadata": { @@ -113,7 +79,17 @@ } } }, - "variables": {}, + "variables": { + "parentResource": "[concat('Microsoft.Kubernetes/connectedClusters/', parameters('connectedClusterName'))]", + "roleDefinitionId_Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "roleDefinitionId_MonitoringMetricsPublisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "customLocationId": "[resourceId('Microsoft.ExtendedLocation/customLocations', parameters('customLocationName'))]", + "customLocation_hostResourceId": "[resourceId('Microsoft.Kubernetes/connectedClusters', parameters('connectedClusterName'))]", + "customLocation_clusterExtensionIds": [ + "[extensionResourceId(resourceId('Microsoft.Kubernetes/connectedClusters', parameters('connectedClusterName')), 'Microsoft.KubernetesConfiguration/extensions', parameters('k8sExtensionName'))]" + ], + "resourceSyncRuleName": "[concat(parameters('customLocationName'), '/defaultResourceSyncRule')]" + }, "functions": [], "resources": [ { @@ -141,7 +117,7 @@ }, "configurationProtectedSettings": {} }, - "scope": "[parameters('parentResource')]" + "scope": "[variables('parentResource')]" }, { "type": "Microsoft.Authorization/roleAssignments", @@ -151,7 +127,7 @@ "[parameters('k8sExtensionName')]" ], "properties": { - "roleDefinitionId": "[parameters('roleDefinitionId_Contributor')]", + "roleDefinitionId": "[variables('roleDefinitionId_Contributor')]", "principalId": "[reference(parameters('k8sExtensionName'), '2021-09-01', 'Full').identity.principalId]" } }, @@ -163,7 +139,7 @@ "[parameters('roleAssignment_Contributor')]" ], "properties": { - "roleDefinitionId": "[parameters('roleDefinitionId_MonitoringMetricsPublisher')]", + "roleDefinitionId": "[variables('roleDefinitionId_MonitoringMetricsPublisher')]", "principalId": "[reference(parameters('k8sExtensionName'), '2021-09-01', 'Full').identity.principalId]" } }, @@ -177,23 +153,23 @@ ], "properties": { "hostType": "Kubernetes", - "hostResourceId": "[parameters('customLocation_hostResourceId')]", + "hostResourceId": "[variables('customLocation_hostResourceId')]", "namespace": "[parameters('namespace')]", "displayName": "", - "clusterExtensionIds": "[parameters('customLocation_clusterExtensionIds')]", + "clusterExtensionIds": "[variables('customLocation_clusterExtensionIds')]", "authentication": {} } }, { "type": "Microsoft.ExtendedLocation/customLocations/resourceSyncRules", "apiVersion": "2021-08-31-preview", - "name": "[parameters('resourceSyncRuleName')]", + "name": "[variables('resourceSyncRuleName')]", "location": "[parameters('location')]", "dependsOn": [ "[parameters('customLocationName')]" ], "properties": { - "targetResourceGroup": "[concat('/subscriptions/', parameters('dataController_subscription'), '/resourceGroups/', parameters('dataController_resourceGroup'))]", + "targetResourceGroup": "[resourceGroup().id]", "priority": 100, "selector": { "matchLabels": { @@ -208,20 +184,20 @@ "name": "[parameters('dataControllerName')]", "location": "[parameters('location')]", "extendedLocation": { - "name": "[parameters('customLocationId')]", + "name": "[variables('customLocationId')]", "type": "CustomLocation" }, "dependsOn": [ - "[last(split(parameters('resourceSyncRuleName'), '/'))]" + "[last(split(variables('resourceSyncRuleName'), '/'))]" ], "tags": {}, "properties": { "metricsDashboardCredential": { - "username": "mhadmin", + "username": "[parameters('dataController_metricsAndLogsDashboardUsername')]", "password": "[parameters('dataController_metricsAndLogsDashboardPassword')]" }, "logsDashboardCredential": { - "username": "mhadmin", + "username": "[parameters('dataController_metricsAndLogsDashboardUsername')]", "password": "[parameters('dataController_metricsAndLogsDashboardPassword')]" }, "logAnalyticsWorkspaceConfig": { @@ -262,8 +238,8 @@ "azure": { "autoUploadMetrics": "true", "autoUploadLogs": "true", - "subscription": "[parameters('dataController_subscription')]", - "resourceGroup": "[parameters('dataController_resourceGroup')]", + "subscription": "[subscription().subscriptionId]", + "resourceGroup": "[resourceGroup().name]", "location": "[parameters('location')]", "connectionMode": "direct" },