Skip to content

Commit 9210afe

Browse files
authored
Merge pull request #105 from microsoft/feature/fabric-private-link-cleanup
feat: fabric automation updates
2 parents 16167a7 + 9fb289c commit 9210afe

38 files changed

Lines changed: 878 additions & 2107 deletions

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,17 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti
121121
| Requirement | Details |
122122
|-------------|---------|
123123
| **Azure Subscription** | Owner or Contributor + User Access Administrator permissions |
124-
| **Microsoft Fabric** | Access to create capacity, workspace (or existing Fabric capacity ID) |
124+
| **Microsoft Fabric** | Optional. Either access to create capacity/workspace, or provide existing Fabric capacity/workspace IDs, or disable Fabric automation |
125125
| **Microsoft Purview** | Existing tenant-level Purview account (or ability to create one) |
126126
| **Azure CLI** | Version 2.61.0 or later |
127127
| **Azure Developer CLI** | Version 1.15.0 or later |
128128
| **Quota** | Sufficient Azure OpenAI quota ([check here](./docs/quota_check.md)) |
129129
130-
> **Note:** If you enable Fabric capacity deployment, you must supply at least one valid Fabric capacity admin principal (Entra user UPN email or object ID) via `fabricCapacityAdmins`.
130+
> **Note:** Fabric automation is optional. To disable all Fabric automation, set `fabricCapacityPreset = 'none'` and `fabricWorkspacePreset = 'none'` in `infra/main.bicepparam`.
131131
132-
> **Note:** If you enable Fabric provisioning, the user running `azd` must have the **Fabric Administrator** role (or equivalent Fabric/Power BI tenant admin permissions) to call the required admin APIs.
132+
> **Note:** If you enable Fabric capacity deployment (`fabricCapacityPreset='create'`), you must supply at least one valid Fabric capacity admin principal (Entra user UPN email or object ID) via `fabricCapacityAdmins`.
133+
134+
> **Note:** If you enable Fabric provisioning (`fabricWorkspacePreset='create'`), the user running `azd` must have the **Fabric Administrator** role (or equivalent Fabric/Power BI tenant admin permissions) to call the required admin APIs.
133135
134136
</details>
135137
@@ -141,7 +143,7 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti
141143
| Azure AI Foundry | Standard | [Pricing](https://azure.microsoft.com/pricing/details/machine-learning/) |
142144
| Azure OpenAI | Pay-per-token | [Pricing](https://azure.microsoft.com/pricing/details/cognitive-services/openai-service/) |
143145
| Azure AI Search | Standard | [Pricing](https://azure.microsoft.com/pricing/details/search/) |
144-
| Microsoft Fabric | F8 Capacity | [Pricing](https://azure.microsoft.com/pricing/details/microsoft-fabric/) |
146+
| Microsoft Fabric | F8 Capacity (if enabled) | [Pricing](https://azure.microsoft.com/pricing/details/microsoft-fabric/) |
145147
| Virtual Network + Bastion | Standard | [Pricing](https://azure.microsoft.com/pricing/details/azure-bastion/) |
146148
147149
> **Cost Optimization:** Fabric capacity can be paused when not in use. Use `az fabric capacity suspend` to stop billing.
@@ -171,8 +173,6 @@ After deployment, you'll have a complete, enterprise-ready platform that unifies
171173
| **Governance** | Microsoft Purview with cataloging, scans, and DSPM | Track data lineage, enforce policies, and maintain compliance visibility |
172174
| **Security** | Private endpoints, managed identities, RBAC, network isolation | Zero public internet exposure—all traffic stays on the Microsoft backbone |
173175
174-
> 💡 **Note:** When Microsoft Fabric automation supports private link provisioning, the entire solution will operate with full network isolation end-to-end.
175-
176176
<br/>
177177
178178
### Key Features

azure.yaml

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@ metadata:
1515
# Pre/Post-provision automation hooks
1616
hooks:
1717
preprovision:
18+
# Integrated preprovision:
19+
# - Runs AI Landing Zone preprovision to generate deploy/ files and Template Specs
20+
# - Ensures our wrapper points to deploy/main.bicep (Template Spec-based) to avoid ARM 4MB template limit
21+
# On Windows, `shell: sh` may not be available; the PowerShell script is a fallback.
1822
- shell: sh
19-
run: ./submodules/ai-landing-zone/bicep/scripts/preprovision.sh
20-
interactive: true
23+
run: ./scripts/preprovision-integrated.sh
24+
interactive: false
25+
continueOnError: true
26+
27+
- shell: pwsh
28+
run: ./scripts/preprovision-integrated.ps1
29+
interactive: false
2130
continueOnError: false
2231

2332
postprovision:
@@ -45,18 +54,6 @@ hooks:
4554
shell: pwsh
4655
continueOnError: false
4756

48-
# Stage 3.3: Create Private Link Service Resource (prerequisite for private endpoint)
49-
- run: ./scripts/automationScripts/FabricWorkspace/SecureWorkspace/create_fabric_private_link_service.ps1
50-
interactive: false
51-
shell: pwsh
52-
continueOnError: false
53-
54-
# Stage 3.7: Setup Workspace Private Endpoint (for secure VNet access)
55-
- run: ./scripts/automationScripts/FabricWorkspace/SecureWorkspace/setup_workspace_private_endpoint.ps1
56-
interactive: false
57-
shell: pwsh
58-
continueOnError: false
59-
6057
# Stage 4: Assign Workspace to Domain
6158
- run: ./scripts/automationScripts/FabricWorkspace/CreateWorkspace/assign_workspace_to_domain.ps1
6259
interactive: false

docs/DeploymentGuide.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,22 @@ Edit `infra/main.bicepparam` or set environment variables:
152152
| Parameter | Description | Example |
153153
|-----------|-------------|---------|
154154
| `purviewAccountResourceId` | Resource ID of existing Purview account | `/subscriptions/.../Microsoft.Purview/accounts/...` |
155-
| `aiSearchAdditionalAccessObjectId` | Array of ObjectId's to apply RBAC role for Search Access | `["user@contoso.com"]` |
156-
| `fabricCapacitySku` | Fabric capacity SKU | `F8` (default) |
157-
| `fabricCapacityAdmins` | Fabric capacity admin principals (UPN emails or Entra object IDs) | `["user@contoso.com"]` |
158-
| `desiredFabricWorkspaceName` | Name for Fabric workspace | `workspace-myenv` |
155+
| `aiSearchAdditionalAccessObjectIds` | Array of Entra object IDs to grant Search roles | `["00000000-0000-0000-0000-000000000000"]` |
156+
| `fabricCapacityMode` | Fabric capacity mode: `create`, `byo`, or `none` | `create` |
157+
| `fabricWorkspaceMode` | Fabric workspace mode: `create`, `byo`, or `none` | `create` |
158+
| `fabricCapacitySku` | Fabric capacity SKU (only used when `fabricCapacityMode=create`) | `F8` (default) |
159+
| `fabricCapacityAdmins` | Fabric capacity admin principals (UPN emails or Entra object IDs) (required when `fabricCapacityMode=create`) | `["user@contoso.com"]` |
160+
| `fabricCapacityResourceId` | Existing Fabric capacity ARM resource ID (required when `fabricCapacityMode=byo`) | `/subscriptions/.../providers/Microsoft.Fabric/capacities/...` |
161+
| `fabricWorkspaceId` | Existing Fabric workspace ID (GUID) (required when `fabricWorkspaceMode=byo`) | `00000000-0000-0000-0000-000000000000` |
162+
| `fabricWorkspaceName` | Existing Fabric workspace name (used when `fabricWorkspaceMode=byo`) | `my-existing-workspace` |
159163

160164
```bash
161165
# Example: Set Purview account
162166
azd env set purviewAccountResourceId "/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.Purview/accounts/<account-name>"
167+
168+
# Example: Disable all Fabric automation
169+
azd env set fabricCapacityMode none
170+
azd env set fabricWorkspaceMode none
163171
```
164172

165173
</details>

docs/PARAMETER_GUIDE.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
# Parameter Guide for AI Landing Zone Deployment
22

3-
This guide explains every parameter in `infra/main.parameters.json` and how to customize your deployment.
3+
This guide focuses on configuration concepts for the **AI Landing Zone**.
4+
5+
> **Important**: This repository deploys using Bicep parameter files, not `infra/main.parameters.json`.
6+
>
7+
> - Primary parameters file: `infra/main.bicepparam`
8+
> - AI Landing Zone submodule parameters file (if you deploy it directly): `submodules/ai-landing-zone/bicep/infra/main.bicepparam`
9+
>
10+
> **Fabric options in this repo** are configured in `infra/main.bicepparam` via:
11+
> - `fabricCapacityPreset` (`create` | `byo` | `none`)
12+
> - `fabricWorkspacePreset` (`create` | `byo` | `none`)
13+
> - BYO inputs: `fabricCapacityResourceId`, `fabricWorkspaceId`, `fabricWorkspaceName`
414
515
## Table of Contents
616
1. [Basic Parameters](#basic-parameters)

docs/automation-outputs-mapping.md

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ The postprovision automation scripts consume deployment outputs via the `AZURE_O
2020

2121
| Bicep Output | Script Variable | Used By | Purpose |
2222
|-------------|-----------------|---------|---------|
23+
| `fabricCapacityModeOut` | `fabricCapacityMode` | Multiple Fabric scripts | Whether capacity is `create`, `byo`, or `none` |
24+
| `fabricWorkspaceModeOut` | `fabricWorkspaceMode` | Multiple Fabric scripts | Whether workspace is `create`, `byo`, or `none` |
2325
| `fabricCapacityId` | `FABRIC_CAPACITY_ID` | `ensure_active_capacity.ps1` | ARM resource ID of Fabric capacity |
24-
| `fabricCapacityResourceId` | `fabricCapacityId` | `create_fabric_workspace.ps1` | Resource ID for capacity assignment |
25-
| `desiredFabricWorkspaceName` | `FABRIC_WORKSPACE_NAME` | Multiple Fabric scripts | Target workspace name |
26+
| `fabricCapacityResourceIdOut` | `fabricCapacityId` | `create_fabric_workspace.ps1` | Resource ID for capacity assignment |
27+
| `fabricWorkspaceIdOut` | `FABRIC_WORKSPACE_ID` | Multiple Fabric scripts | Existing or created Fabric workspace ID |
28+
| `fabricWorkspaceNameOut` | `FABRIC_WORKSPACE_NAME` | Multiple Fabric scripts | Target workspace name |
29+
| `desiredFabricWorkspaceName` | `FABRIC_WORKSPACE_NAME` | Multiple Fabric scripts | Back-compat alias for `fabricWorkspaceName` |
2630
| `desiredFabricDomainName` | `domainName` | `create_fabric_domain.ps1` | Target domain name |
2731
| `fabricCapacityName` | - | - | Display name (optional) |
2832

@@ -79,7 +83,11 @@ When `azd up` completes, it sets:
7983
```bash
8084
export AZURE_OUTPUTS_JSON='{
8185
"fabricCapacityId": {"type":"String","value":"/subscriptions/.../fabricCapacities/fabric-xyz"},
82-
"desiredFabricWorkspaceName": {"type":"String","value":"ai-workspace"},
86+
"fabricCapacityModeOut": {"type":"String","value":"create"},
87+
"fabricWorkspaceModeOut": {"type":"String","value":"create"},
88+
"fabricWorkspaceNameOut": {"type":"String","value":"workspace-myenv"},
89+
"fabricWorkspaceIdOut": {"type":"String","value":""},
90+
"desiredFabricWorkspaceName": {"type":"String","value":"workspace-myenv"},
8391
"aiSearchName": {"type":"String","value":"search-xyz"},
8492
"aiSearchResourceGroup": {"type":"String","value":"rg-ai-landing-zone"},
8593
...
@@ -93,7 +101,10 @@ Scripts parse this JSON:
93101
if (-not $WorkspaceName -and $env:AZURE_OUTPUTS_JSON) {
94102
try {
95103
$out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json
96-
$WorkspaceName = $out.desiredFabricWorkspaceName.value
104+
$WorkspaceName = $out.fabricWorkspaceNameOut.value
105+
if (-not $WorkspaceName) {
106+
$WorkspaceName = $out.desiredFabricWorkspaceName.value
107+
}
97108
} catch {}
98109
}
99110
```
@@ -116,13 +127,15 @@ azd env get-values
116127

117128
# View specific output
118129
azd env get-value fabricCapacityId
130+
azd env get-value fabricCapacityModeOut
131+
azd env get-value fabricWorkspaceModeOut
119132
azd env get-value aiSearchName
120133
```
121134

122135
## Related Files
123136

124-
- **Infrastructure**: `/infra/main-orchestrator.bicep` (lines 313-349)
125-
- **Parameters**: `/infra/main-orchestrator.bicepparam`
137+
- **Infrastructure**: `/infra/main.bicep`
138+
- **Parameters**: `/infra/main.bicepparam`
126139
- **Automation Workflow**: `/azure.yaml` (postprovision hooks)
127140
- **Scripts**: `/scripts/automationScripts/`
128141

docs/examples/azure.yaml.private-networking.sample

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,7 @@ hooks:
2020
interactive: false
2121
continueOnError: false
2222

23-
# Step 1: Create or update the Fabric private link service resource in Azure
24-
- shell: pwsh
25-
run: ./scripts/automationScripts/FabricWorkspace/SecureWorkspace/create_fabric_private_link_service.ps1
26-
interactive: false
27-
continueOnError: false
28-
29-
# Step 2: Create/refresh the private endpoint and DNS configuration in the target VNet
30-
- shell: pwsh
31-
run: ./scripts/automationScripts/FabricWorkspace/SecureWorkspace/setup_workspace_private_endpoint.ps1
32-
interactive: false
33-
continueOnError: false
34-
35-
# Step 3: Lock down the Fabric workspace so that only private endpoints are allowed
23+
# Step 1: Lock down the Fabric workspace so that only private endpoints are allowed
3624
- shell: pwsh
3725
run: ./scripts/automationScripts/FabricWorkspace/SecureWorkspace/enable_fabric_workspace_inbound_protection.ps1
3826
interactive: false

docs/fabric-onelake-private-networking.md

Lines changed: 12 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ When deploying AI Search, AI Foundry, and Purview within a VNet (as configured i
3838
│ │ │ Lakehouse │ │ Lakehouse │ │ Lakehouse │ │ │
3939
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
4040
│ │ │ │
41-
│ │ Private Link Resource: privateLinkServicesForFabric │ │
41+
│ │ Private Link Resource: Service-managed (no ARM RP) │ │
4242
│ └─────────────────────────────────────────────────────────────┘ │
4343
└───────────────────────────────────────────────────────────────────┘
4444
```
@@ -47,12 +47,12 @@ When deploying AI Search, AI Foundry, and Purview within a VNet (as configured i
4747

4848
### 1. Fabric Workspace Private Links
4949

50-
Microsoft Fabric supports **workspace-level private links** that enable secure, private connectivity from Azure VNets to specific Fabric workspaces and their OneLake lakehouses.
50+
Microsoft Fabric supports **workspace-level private links** that enable secure, private connectivity from Azure VNets to specific Fabric workspaces and their OneLake lakehouses. This is a **service-managed** experience: there is currently **no customer-facing ARM/Bicep resource provider** to deploy or manage these objects.
5151

5252
> **Important:** As of November 2025 Azure AI Search cannot complete a shared private link where `group-id = "workspace"`. Our automation detects the failure message `Cannot create private endpoint for requested type 'workspace'` and skips the shared private link stage so OneLake indexers continue to work over public endpoints. Follow the steps in [Phase 2](#phase-2-configure-shared-private-link-from-ai-search-automated) to re-run the script when Microsoft enables the feature, and keep the workspace communication policy in **Allow** mode until the link can be provisioned.
5353
54-
- **Resource Provider**: `Microsoft.Fabric/privateLinkServicesForFabric`
55-
- **Target Subresource**: `workspace` (workspace-specific) or `tenant` (tenant-wide)
54+
- **Resource Provider**: _Not available in ARM/Bicep; service-managed only_
55+
- **Target Subresource**: `workspace` (workspace-specific) or `tenant` (tenant-wide) — managed by the service
5656
- **Workspace FQDN Format**: `https://{workspaceid}.z{xy}.blob.fabric.microsoft.com`
5757
- `{workspaceid}` = Workspace GUID without dashes
5858
- `{xy}` = First two characters of workspace GUID
@@ -83,7 +83,7 @@ Private DNS zones are required to resolve Fabric workspace FQDNs to private IPs:
8383

8484
### Phase 1: Enable Fabric Workspace Private Link (Manual - Post-Deployment)
8585

86-
> **Note**: Fabric workspace private links cannot be configured via ARM/Bicep as of October 2025. This must be done manually after workspace creation.
86+
> **Note**: Fabric workspace private links cannot be configured via ARM/Bicep. This must be done manually after workspace creation in the Fabric portal.
8787
8888
1. **Create Fabric Workspace** (via postprovision script: `create_fabric_workspace.ps1`)
8989

@@ -95,79 +95,17 @@ Private DNS zones are required to resolve Fabric workspace FQDNs to private IPs:
9595

9696
> **Note**: Once Microsoft enables workspace-targeted shared private links, the connection from AI Search should auto-approve because both resources live in the same subscription/tenant. Until then, the script will exit with a warning and no shared private link is created.
9797
98-
### Phase 2: Configure Shared Private Link from AI Search (Automated)
98+
### Phase 2: Configure Shared Private Link from AI Search (Not yet supported)
9999

100-
This is handled by the Bicep infrastructure in **Stage 7: Fabric Private Networking** and the **`setup_fabric_private_link.ps1`** postprovision script.
101-
102-
**Resources created (when supported)**:
103-
1. Private DNS zones for Fabric endpoints
104-
2. DNS zone virtual network links
105-
3. Shared private link from AI Search to Fabric workspace (via PowerShell script)
100+
Workspace-targeted shared private links from AI Search are not supported today; the automation path is disabled/no-op to avoid failures. Keep workspace communication policy in **Allow** mode until Microsoft enables the feature, then re-enable private access manually.
106101

107102
**RBAC tip:** Add Azure AD group object IDs to the `aiSearchAdditionalAccessObjectIds` parameter (or `azd env set aiSearchAdditionalAccessObjectIds "<objectId>"`) so interactive users inherit the same Search roles that the automation assigns to managed identities.
108103

109-
**Key Benefits of Automatic Approval**:
110-
-**No manual approval needed** - Connection is auto-approved because both resources are in the same subscription/tenant
111-
-**Consistent with other private endpoints** - Works like Storage, Cosmos DB, AI Search private endpoints
112-
-**Faster deployment** - No waiting for manual approval step
113-
-**Production-ready** - Fully automated end-to-end
114-
115-
**Bicep Configuration** (Stage 7):
116-
```bicep
117-
// Private DNS zones created
118-
resource analysisDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01'
119-
resource capacityDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01'
120-
resource powerQueryDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01'
121-
122-
// VNet links for DNS resolution
123-
resource analysisVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01'
124-
```
125-
126-
**PowerShell Script** (`setup_fabric_private_link.ps1`):
127-
```powershell
128-
# Step 1: Automatically creates shared private link with same-subscription auto-approval
129-
az search shared-private-link-resource create \
130-
--resource-group <rg-name> \
131-
--service-name <search-name> \
132-
--name fabric-workspace-link \
133-
--group-id workspace \
134-
--resource-id <fabric-workspace-resource-id>
135-
136-
# Connection status will be "Approved" automatically (2-3 minutes provisioning time) once Azure supports workspace shared private links
137-
138-
# Step 2: Configure workspace to deny public access (allow only private link connections)
139-
$policyBody = @{
140-
inbound = @{
141-
publicAccessRules = @{
142-
defaultAction = "Deny"
143-
}
144-
}
145-
} | ConvertTo-Json
146-
147-
Invoke-RestMethod `
148-
-Uri "https://api.fabric.microsoft.com/v1/workspaces/$workspaceId/networking/communicationPolicy" `
149-
-Headers $headers `
150-
-Method Put `
151-
-Body $policyBody `
152-
-ContentType 'application/json'
153-
154-
# Policy takes effect in up to 30 minutes
155-
```
156-
157-
**What Gets Automated (once the platform supports workspace shared private links)**:
158-
1. ✅ Shared private link creation (AI Search → Fabric)
159-
2. ✅ Automatic approval (same subscription/tenant)
160-
3. ✅ Workspace communication policy (deny public access)
161-
4. ✅ Verification of connection status
162-
163-
**Current Behavior (End of 2025)**:
164-
- ⚠️ Shared private link creation fails with `Cannot create private endpoint for requested type 'workspace'`
165-
- ⚠️ Script logs a warning and skips the shared private link stage
166-
- ✅ Workspace remains in **Allow** mode so indexing continues over public endpoints
167-
- ✅ You can re-run the script after Microsoft releases support; no additional changes required
168-
169-
**Remaining Manual Step** (one-time):
170-
- Enable workspace-level private link in Fabric portal (required before shared private link can be created)
104+
**Current Behavior**:
105+
- ⚠️ Workspace shared private link creation fails with `Cannot create private endpoint for requested type 'workspace'`.
106+
- ⚠️ Scripts that tried to deploy `Microsoft.Fabric/privateLinkServicesForFabric` are now disabled and act as no-ops.
107+
- ✅ Keep workspace in **Allow** mode so indexing continues over public endpoints until Microsoft delivers a supported path.
108+
- ✅ You can re-run the connectivity scripts later if the platform exposes a supported resource provider.
171109

172110
### Phase 3: Configure OneLake Data Source (Automated Script)
173111

0 commit comments

Comments
 (0)