|
|
# MDP Databricks-Primary Architecture — First Steps
|
|
|
|
|
|
## Context
|
|
|
|
|
|
Implementation of the Databricks-primary architecture (v8.0) on Azure for Greenfield's Modern Data Platform. Excluding disaster recovery and devtest environments for now. All Terraform scripts are placed in `/Implementation/Terraform`.
|
|
|
|
|
|
---
|
|
|
|
|
|
## Day 1 Tasks — Previous Conversation Summary
|
|
|
|
|
|
### D1-T1: Subscription Provisioning
|
|
|
|
|
|
- **Terraform artifacts produced:** `providers.tf` (AzureRM with `subscription_id`), `backend.tf` (state backend), `variables.tf` + `terraform.tfvars` (prod values).
|
|
|
- **Note:** Subscription creation itself is a manual/ticket process — Terraform assumes it exists.
|
|
|
|
|
|
### D1-T2: Resource Group Structure (6 RGs)
|
|
|
|
|
|
- **Terraform:** `networking/main.tf` lines 14–48
|
|
|
- **Resource groups created:**
|
|
|
- `rg-mdp-prod-network`
|
|
|
- `rg-mdp-prod-databricks`
|
|
|
- `rg-mdp-prod-storage`
|
|
|
- `rg-mdp-prod-governance`
|
|
|
- `rg-mdp-prod-keyvault`
|
|
|
- `rg-mdp-prod-monitoring`
|
|
|
|
|
|
### D1-T3: VNet Design & Deployment (starts D1, finishes D2)
|
|
|
|
|
|
- **Terraform:** `networking/main.tf`
|
|
|
- **Main VNet:** `10.100.0.0/16` with 3 subnets:
|
|
|
- `dbx-host`
|
|
|
- `dbx-container`
|
|
|
- `private-endpoints`
|
|
|
- **Transit VNet:** `10.101.0.0/24`
|
|
|
- **Peerings:** main↔transit, optional main↔hub
|
|
|
- **NAT Gateway** with public IP on both Databricks subnets
|
|
|
- **NSG** with full inbound/outbound rule set
|
|
|
- **4 private DNS zones** with VNet links
|
|
|
- **Outputs** wired in `outputs.tf`
|
|
|
- **Root module** `prod/main.tf` calls the networking module
|
|
|
|
|
|
---
|
|
|
|
|
|
## D1-T1: Manual Subscription Provisioning Guide
|
|
|
|
|
|
Before Terraform can run, the Azure subscription must exist. This is a manual process at Greenfield.
|
|
|
|
|
|
### Step 1 — Request or Create the Subscription
|
|
|
|
|
|
Submit a request to Cloud Infra (ServiceNow/ITSM ticket) with these specifications:
|
|
|
|
|
|
| Parameter | Value |
|
|
|
|---|---|
|
|
|
| Subscription name | `greenfield-mdp-prod` |
|
|
|
| Offer type | Enterprise Agreement (EA) |
|
|
|
| Billing account | Greenfield's EA enrollment |
|
|
|
| Region affinity | Canada Central |
|
|
|
| Management group | Place under Greenfield's MG hierarchy (e.g., `mg-greenfield-data` or `mg-greenfield-prod`) |
|
|
|
|
|
|
### Step 2 — Place Under the Correct Management Group
|
|
|
|
|
|
- Go to **Azure Portal → Management groups**
|
|
|
- Locate the target MG (e.g., `mg-greenfield-prod` or `mg-greenfield-data-platforms`)
|
|
|
- Move the new subscription under it (or ensure Cloud Infra places it there at creation time)
|
|
|
- Azure Policy assignments on that MG are inherited automatically
|
|
|
|
|
|
### Step 3 — Register Required Resource Providers
|
|
|
|
|
|
```bash
|
|
|
az account set --subscription "greenfield-mdp-prod"
|
|
|
|
|
|
az provider register --namespace Microsoft.Databricks
|
|
|
az provider register --namespace Microsoft.Storage
|
|
|
az provider register --namespace Microsoft.KeyVault
|
|
|
az provider register --namespace Microsoft.Network
|
|
|
az provider register --namespace Microsoft.ManagedIdentity
|
|
|
az provider register --namespace Microsoft.Purview
|
|
|
az provider register --namespace Microsoft.OperationalInsights
|
|
|
az provider register --namespace Microsoft.Monitor
|
|
|
az provider register --namespace Microsoft.EventHub
|
|
|
```
|
|
|
|
|
|
Verify with:
|
|
|
|
|
|
```bash
|
|
|
az provider list --query "[?registrationState=='Registered'].namespace" -o table
|
|
|
```
|
|
|
|
|
|
### Step 4 — Set Up RBAC on the Subscription
|
|
|
|
|
|
| Principal | Role | Scope |
|
|
|
|---|---|---|
|
|
|
| `sp-mdp-terraform-prod` (Terraform SP) | Contributor | Subscription |
|
|
|
| `sp-mdp-terraform-prod` | User Access Administrator | Subscription |
|
|
|
| `grp-mdp-account-admins` | Owner | Subscription |
|
|
|
| `grp-mdp-platform-engineers` | Contributor | Subscription |
|
|
|
|
|
|
```bash
|
|
|
SUB_ID=$(az account show --query id -o tsv)
|
|
|
|
|
|
az role assignment create --assignee <terraform-sp-app-id> \
|
|
|
--role "Contributor" --scope "/subscriptions/$SUB_ID"
|
|
|
|
|
|
az role assignment create --assignee <terraform-sp-app-id> \
|
|
|
--role "User Access Administrator" --scope "/subscriptions/$SUB_ID"
|
|
|
```
|
|
|
|
|
|
### Step 5 — Create the Terraform State Storage Account
|
|
|
|
|
|
In a separate management/shared subscription to avoid circular dependency:
|
|
|
|
|
|
```bash
|
|
|
az account set --subscription "<shared-infra-subscription>"
|
|
|
|
|
|
az group create --name rg-mdp-tfstate --location canadacentral
|
|
|
|
|
|
az storage account create \
|
|
|
--name staccmdptfstate \
|
|
|
--resource-group rg-mdp-tfstate \
|
|
|
--location canadacentral \
|
|
|
--sku Standard_GRS \
|
|
|
--kind StorageV2 \
|
|
|
--min-tls-version TLS1_2 \
|
|
|
--allow-blob-public-access false
|
|
|
|
|
|
az storage container create \
|
|
|
--name tfstate \
|
|
|
--account-name staccmdptfstate
|
|
|
|
|
|
az storage account blob-service-properties update \
|
|
|
--account-name staccmdptfstate \
|
|
|
--enable-versioning true
|
|
|
```
|
|
|
|
|
|
### Step 6 — Update `terraform.tfvars`
|
|
|
|
|
|
```bash
|
|
|
az account show --subscription "greenfield-mdp-prod" --query id -o tsv
|
|
|
```
|
|
|
|
|
|
Replace `REPLACE-WITH-PROD-SUBSCRIPTION-ID` in `terraform.tfvars` with the actual UUID.
|
|
|
|
|
|
### Step 7 — Validate Terraform Authentication
|
|
|
|
|
|
```bash
|
|
|
cd environments/prod
|
|
|
az login --service-principal -u <app-id> -p <secret> --tenant <tenant-id>
|
|
|
terraform init
|
|
|
terraform plan
|
|
|
```
|
|
|
|
|
|
### Handoff Summary
|
|
|
|
|
|
| Action | Who |
|
|
|
|---|---|
|
|
|
| Create EA subscription `greenfield-mdp-prod` | Cloud Infra (ticket) |
|
|
|
| Place under correct Management Group | Cloud Infra |
|
|
|
| Register resource providers | Cloud Infra or you (az CLI) |
|
|
|
| Create Terraform SP + RBAC | Cloud Infra / IAM team |
|
|
|
| Create tfstate storage account | You or DevOps |
|
|
|
| Update `terraform.tfvars` | You |
|
|
|
| Validate `terraform init/plan` | You |
|
|
|
|
|
|
---
|
|
|
|
|
|
## Azure Policy Setup
|
|
|
|
|
|
### Checking Existing Policies
|
|
|
|
|
|
```bash
|
|
|
az policy assignment list \
|
|
|
--scope "/subscriptions/52274961-4dda-48cc-a79b-3a0b74f8eaac" \
|
|
|
--query "[].{Name:displayName, Scope:scope, Policy:policyDefinitionId}" \
|
|
|
-o table
|
|
|
```
|
|
|
|
|
|
### 1. Allowed Locations
|
|
|
|
|
|
Built-in policy ID: `e56962a6-4747-49cd-b67b-bf8b01975c4c`
|
|
|
|
|
|
```bash
|
|
|
az policy assignment create \
|
|
|
--name "mdp-allowed-locations" \
|
|
|
--display-name "MDP - Restrict to Canada regions" \
|
|
|
--policy "e56962a6-4747-49cd-b67b-bf8b01975c4c" \
|
|
|
--scope "/subscriptions/52274961-4dda-48cc-a79b-3a0b74f8eaac" \
|
|
|
--params '{
|
|
|
"listOfAllowedLocations": {
|
|
|
"value": ["canadacentral", "canadaeast", "global"]
|
|
|
}
|
|
|
}'
|
|
|
```
|
|
|
|
|
|
> `global` is needed for tenant-level resources (Entra ID, Management Groups, Azure Policy itself).
|
|
|
|
|
|
### 2. Mandatory Tagging (Initiative Approach)
|
|
|
|
|
|
Built-in policy "Require a tag on resources": `871b6d14-10aa-478d-b590-94f262ecfa99`
|
|
|
|
|
|
**Quick approach — loop per tag:**
|
|
|
|
|
|
```bash
|
|
|
SCOPE="/subscriptions/52274961-4dda-48cc-a79b-3a0b74f8eaac"
|
|
|
POLICY_ID="871b6d14-10aa-478d-b590-94f262ecfa99"
|
|
|
|
|
|
for TAG in project environment cost-center owner data-classification; do
|
|
|
az policy assignment create \
|
|
|
--name "mdp-require-tag-${TAG}" \
|
|
|
--display-name "MDP - Require tag: ${TAG}" \
|
|
|
--policy "$POLICY_ID" \
|
|
|
--scope "$SCOPE" \
|
|
|
--params "{\"tagName\": {\"value\": \"${TAG}\"}}" \
|
|
|
--enforcement-mode Default
|
|
|
done
|
|
|
```
|
|
|
|
|
|
**Custom initiative approach (recommended):**
|
|
|
|
|
|
Create `mandatory-tags-initiative.json`:
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"properties": {
|
|
|
"displayName": "MDP - Mandatory Tags",
|
|
|
"policyType": "Custom",
|
|
|
"metadata": { "category": "Tags" },
|
|
|
"parameters": {},
|
|
|
"policyDefinitions": [
|
|
|
{
|
|
|
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
|
|
|
"parameters": { "tagName": { "value": "project" } }
|
|
|
},
|
|
|
{
|
|
|
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
|
|
|
"parameters": { "tagName": { "value": "environment" } }
|
|
|
},
|
|
|
{
|
|
|
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
|
|
|
"parameters": { "tagName": { "value": "cost-center" } }
|
|
|
},
|
|
|
{
|
|
|
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
|
|
|
"parameters": { "tagName": { "value": "owner" } }
|
|
|
},
|
|
|
{
|
|
|
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
|
|
|
"parameters": { "tagName": { "value": "data-classification" } }
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
```bash
|
|
|
az policy set-definition create \
|
|
|
--name "mdp-mandatory-tags" \
|
|
|
--display-name "MDP - Mandatory Tags" \
|
|
|
--definitions @mandatory-tags-initiative.json \
|
|
|
--subscription "52274961-4dda-48cc-a79b-3a0b74f8eaac"
|
|
|
|
|
|
az policy assignment create \
|
|
|
--name "mdp-mandatory-tags" \
|
|
|
--display-name "MDP - Mandatory Tags" \
|
|
|
--policy-set-definition "mdp-mandatory-tags" \
|
|
|
--scope "/subscriptions/52274961-4dda-48cc-a79b-3a0b74f8eaac"
|
|
|
```
|
|
|
|
|
|
### 3. Allowed VM SKUs
|
|
|
|
|
|
Built-in policy ID: `cccc23c7-8427-4f53-ad12-b6a63eb452b3`
|
|
|
|
|
|
```bash
|
|
|
az policy assignment create \
|
|
|
--name "mdp-allowed-vm-skus" \
|
|
|
--display-name "MDP - Allowed VM SKUs" \
|
|
|
--policy "cccc23c7-8427-4f53-ad12-b6a63eb452b3" \
|
|
|
--scope "/subscriptions/52274961-4dda-48cc-a79b-3a0b74f8eaac" \
|
|
|
--params '{
|
|
|
"listOfAllowedSKUs": {
|
|
|
"value": [
|
|
|
"Standard_DS3_v2",
|
|
|
"Standard_DS4_v2",
|
|
|
"Standard_DS5_v2",
|
|
|
"Standard_E8s_v5",
|
|
|
"Standard_E16s_v5",
|
|
|
"Standard_E32s_v5",
|
|
|
"Standard_L8s_v3",
|
|
|
"Standard_L16s_v3",
|
|
|
"Standard_NC8as_T4_v3",
|
|
|
"Standard_NC16as_T4_v3"
|
|
|
]
|
|
|
}
|
|
|
}'
|
|
|
```
|
|
|
|
|
|
**SKU rationale:**
|
|
|
|
|
|
- `DS` series — general-purpose Databricks workers
|
|
|
- `E` series — memory-optimized (data engineering)
|
|
|
- `L` series — storage-optimized (Delta caching)
|
|
|
- `NC` series — GPU clusters for ML
|
|
|
|
|
|
### Policy Assignment Scope Guidance
|
|
|
|
|
|
| Policy | Recommended scope |
|
|
|
|---|---|
|
|
|
| Allowed locations | Management group (likely already exists at Greenfield) |
|
|
|
| Mandatory tags | Management group (likely already exists at Greenfield) |
|
|
|
| Allowed VM SKUs | Subscription (workload-specific) |
|
|
|
|
|
|
### Enforcement Mode
|
|
|
|
|
|
For initial rollout, use `--enforcement-mode DoNotEnforce` to run in **audit-only mode** first. This flags non-compliant resources without blocking deployments. Switch to `Default` (enforce) after remediation.
|