Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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>/$user_number/g" parameters-my.json
sed -i "s/<SUBSCRIPTION_ID>/$subscription_id/g" parameters-my.json
sed -i "s/<CONNECTED_CLUSTER_NAME>/$connected_cluster_name/g" parameters-my.json
sed -i "s/<EXTENSION_NAME>/$extension_name/g" parameters-my.json
sed -i "s/<NAMESPACE>/$namespace/g" parameters-my.json
sed -i "s/<CUSTOM_LOCATION_NAME>/$custom_location_name/g" parameters-my.json
sed -i "s/<DATA_CONTROLLER_NAME>/$data_controller_name/g" parameters-my.json
sed -i "s/<LOG_ANALYTICS_WORKSPACE_ID>/$log_analytics_workspace_id/g" parameters-my.json
sed -i "s/<LOG_ANALYTICS_PRIMARY_KEY>/$escaped_key/g" parameters-my.json
sed -i "s/<GENERATE_GUID_1>/$guid1/g" parameters-my.json
sed -i "s/<GENERATE_GUID_2>/$guid2/g" parameters-my.json
sed -i "s/<DC_DASHBOARD_USERNAME>/mhadmin/g" parameters-my.json

# Alternative: manual replacement
# Edit parameters-my.json and replace:
# <USER_NUMBER> with your user number
# <SUBSCRIPTION_ID> with your subscription ID
# <CONNECTED_CLUSTER_NAME> with the name of your arc-enabled connected cluster
# <EXTENSION_NAME> with the desired extension name
# <NAMESPACE> with the Kubernetes namespace for arc data services
# <CUSTOM_LOCATION_NAME> with the custom location name
# <DATA_CONTROLLER_NAME> with the desired data controller name
# <LOG_ANALYTICS_WORKSPACE_ID> with workspace ID
# <LOG_ANALYTICS_PRIMARY_KEY> with workspace key
# <GENERATE_GUID_1> with first new GUID (use 'uuidgen' command)
# <GENERATE_GUID_2> with second new GUID (use 'uuidgen' command again)
# <DC_DASHBOARD_USERNAME> with the desired dashboard username (e.g., mhadmin)
```

### Step 3: Deploy the Arc Data Controller
Expand Down Expand Up @@ -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
```
Expand All @@ -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
Expand All @@ -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"
Expand All @@ -179,7 +184,7 @@ echo "Server=$master_ip,$node_port;Database=master;User Id=sa;Password=<your-pas
#### 3. Enter connection details:
**💡Note: In this lab we are using the public ip to connect to the service.**
- **Input type**: Parameters
- **Server:** `<Public-IP-Master-Node>,<NODE_PORT>` (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:** `<Public-IP-Master-Node>,<NODE_PORT>` (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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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/<USER_NUMBER>-k8s-arc-enabled"
"connectedClusterName": {
"value": "<CONNECTED_CLUSTER_NAME>"
},
"k8sExtensionName": {
"value": "<USER_NUMBER>-onprem-ext"
"value": "<EXTENSION_NAME>"
},
"namespace": {
"value": "<USER_NUMBER>-onprem"
"value": "<NAMESPACE>"
},
"location": {
"value": "westeurope"
},
"roleDefinitionId_Contributor": {
"value": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<USER_NUMBER>-k8s-arc/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
},
"roleAssignment_Contributor": {
"value": "<GENERATE_GUID_1>"
},
"roleDefinitionId_MonitoringMetricsPublisher": {
"value": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<USER_NUMBER>-k8s-arc/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb"
},
"roleAssignment_MonitoringMetricsPublisher": {
"value": "<GENERATE_GUID_2>"
},
"customLocationName": {
"value": "<USER_NUMBER>-onprem"
},
"customLocationId": {
"value": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<USER_NUMBER>-k8s-arc/providers/Microsoft.ExtendedLocation/customLocations/<USER_NUMBER>-onprem"
"value": "<CUSTOM_LOCATION_NAME>"
},
"customLocation_hostResourceId": {
"value": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<USER_NUMBER>-k8s-arc/providers/Microsoft.Kubernetes/connectedClusters/<USER_NUMBER>-k8s-arc-enabled"
},
"customLocation_clusterExtensionIds": {
"value": [
"/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<USER_NUMBER>-k8s-arc/providers/Microsoft.Kubernetes/connectedClusters/<USER_NUMBER>-k8s-arc-enabled/providers/Microsoft.KubernetesConfiguration/extensions/<USER_NUMBER>-onprem-ext"
]
},
"resourceSyncRuleName": {
"value": "<USER_NUMBER>-onprem/defaultResourceSyncRule"
"dataController_metricsAndLogsDashboardUsername": {
"value": "<DC_DASHBOARD_USERNAME>"
},
"dataController_metricsAndLogsDashboardPassword": {
"value": null
Expand All @@ -55,14 +32,8 @@
"storageClass": {
"value": "local-path"
},
"dataController_subscription": {
"value": "<SUBSCRIPTION_ID>"
},
"dataController_resourceGroup": {
"value": "<USER_NUMBER>-k8s-arc"
},
"dataControllerName": {
"value": "<USER_NUMBER>-dc"
"value": "<DATA_CONTROLLER_NAME>"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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": {
Expand All @@ -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": {
Expand All @@ -100,20 +72,24 @@
"description": "The storage class for data and logs storage."
}
},
"dataController_subscription": {
"type": "String"
},
"dataController_resourceGroup": {
"type": "String"
},
"dataControllerName": {
"type": "String",
"metadata": {
"description": "The name of the data controller."
}
}
},
"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": [
{
Expand Down Expand Up @@ -141,7 +117,7 @@
},
"configurationProtectedSettings": {}
},
"scope": "[parameters('parentResource')]"
"scope": "[variables('parentResource')]"
},
{
"type": "Microsoft.Authorization/roleAssignments",
Expand All @@ -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]"
}
},
Expand All @@ -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]"
}
},
Expand All @@ -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": {
Expand All @@ -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": {
Expand Down Expand Up @@ -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"
},
Expand Down
Loading