Skip to content

Commit d30be64

Browse files
singankitCopilotfoundry-samples automation
authored andcommitted
Add AMPLS for Application Insights telemetry in managed network template (18) (#548)
* Add AMPLS for Application Insights telemetry in template 18 Add Azure Monitor Private Link Scope (AMPLS) to enable hosted agents to export traces and telemetry to Application Insights via private network when using AllowOnlyApprovedOutbound isolation mode. Changes: - New module: azure-monitor-private-link.bicep - Creates Log Analytics Workspace and Application Insights - Creates AMPLS with scoped resources - Creates private DNS zones (monitor, oms, ods, agentsvc) - Creates private endpoint with DNS zone group - Updated managed-network.bicep: Added AMPLS outbound PE rule - Updated main.bicep: Integrated AMPLS module - Updated README.md: Documented AMPLS resources and DNS zones Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Automatic fixes * Link Application Insights to Foundry project via connection Add ApplicationInsights connection to the project so the hosted agent knows where to send telemetry. Without this connection, the AMPLS networking is in place but the project has no reference to App Insights. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Automatic fixes * Update Deploy to Azure button to point to PR branch The deploy button now points to the anksing/add-ampls-to-template-18 branch on microsoft-foundry/foundry-samples-pr so users can test the AMPLS changes before they are merged to main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix App Insights connection schema for Foundry project - Change category from 'ApplicationInsights' to 'AppInsights' - Change authType from 'AAD' to 'ApiKey' - Set target to resource ID instead of connection string - Add credentials with connection string key - Add appInsightsResourceId output from AMPLS module - Pass appInsightsResourceId to project module Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Automatic fixes * Add AMPLS to Terraform template 18 for App Insights telemetry Mirrors the Bicep AMPLS changes: - Add monitor.tf with Log Analytics, App Insights, AMPLS, DNS zones, PE - Add AMPLS managed network outbound rule - Add App Insights connection to Foundry project - Add AMPLS to outbound rules wait dependency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Revert Deploy to Azure button to point to main branch Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: foundry-samples automation <foundry-samples@noreply.github.com>
1 parent 85f2865 commit d30be64

8 files changed

Lines changed: 820 additions & 4 deletions

File tree

infrastructure/infrastructure-setup-bicep/18-managed-virtual-network/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,18 @@ Cosmos DB Account
518518
- Disabled local auth
519519
- Single region deployment
520520

521+
Azure Monitor (Application Insights & Log Analytics)
522+
- Log Analytics Workspace: Microsoft.OperationalInsights/workspaces
523+
- SKU: PerGB2018
524+
- Retention: 30 days
525+
- Application Insights: Microsoft.Insights/components
526+
- Kind: web
527+
- Linked to Log Analytics workspace
528+
- Azure Monitor Private Link Scope (AMPLS): microsoft.insights/privateLinkScopes
529+
- Access mode: PrivateOnly (ingestion and query)
530+
- Scoped resources: Application Insights + Log Analytics
531+
- Enables hosted agents to export telemetry via private network
532+
521533
### Network Security Design
522534

523535
Network Security
@@ -535,6 +547,7 @@ Private endpoints ensure secure, internal-only connectivity. Private endpoints a
535547
- Azure AI Search
536548
- Azure Storage
537549
- Azure Cosmos DB
550+
- Azure Monitor Private Link Scope (AMPLS) — enables telemetry export from hosted agents
538551

539552
**Private DNS Zones**
540553
| Private Link Resource Type | Sub Resource | Private DNS Zone Name | Public DNS Zone Forwarders |
@@ -543,6 +556,7 @@ Private endpoints ensure secure, internal-only connectivity. Private endpoints a
543556
| **Azure AI Search** | searchService| `privatelink.search.windows.net` | `search.windows.net` |
544557
| **Azure Cosmos DB** | Sql | `privatelink.documents.azure.com` | `documents.azure.com` |
545558
| **Azure Storage** | blob | `privatelink.blob.core.windows.net` | `blob.core.windows.net` |
559+
| **Azure Monitor (AMPLS)** | azuremonitor | `privatelink.monitor.azure.com`<br>`privatelink.oms.opinsights.azure.com`<br>`privatelink.ods.opinsights.azure.com`<br>`privatelink.agentsvc.azure-automation.net` | `monitor.azure.com`<br>`oms.opinsights.azure.com`<br>`ods.opinsights.azure.com`<br>`agentsvc.azure-automation.net` |
546560

547561

548562
### Authentication & Authorization
@@ -585,6 +599,7 @@ modules-network-secured/
585599
├── ai-account-identity.bicep # Microsoft Foundry deployment and configuration
586600
├── ai-project-identity.bicep # Foundry project deployment and connection configuration
587601
├── ai-search-role-assignments.bicep # AI Search RBAC configuration
602+
├── azure-monitor-private-link.bicep # Azure Monitor Private Link Scope (AMPLS) for telemetry
588603
├── azure-storage-account-role-assignment.bicep # Storage Account RBAC configuration
589604
├── blob-storage-container-role-assignments.bicep # Blob Storage Container RBAC configuration
590605
├── cosmos-container-role-assignments.bicep # CosmosDB container Account RBAC configuration

infrastructure/infrastructure-setup-bicep/18-managed-virtual-network/azuredeploy.json

Lines changed: 356 additions & 3 deletions
Large diffs are not rendered by default.

infrastructure/infrastructure-setup-bicep/18-managed-virtual-network/main.bicep

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,20 @@ module networkApproverRoleSearch 'modules-network-secured/network-connection-app
244244
}
245245
}
246246

247+
// Azure Monitor Private Link Scope (AMPLS) for Application Insights telemetry
248+
// This enables hosted agents to export traces/telemetry to App Insights via private network
249+
module ampls 'modules-network-secured/azure-monitor-private-link.bicep' = {
250+
name: 'ampls-${uniqueSuffix}-deployment'
251+
params: {
252+
location: location
253+
suffix: uniqueSuffix
254+
vnetName: vnet.outputs.virtualNetworkName
255+
vnetResourceGroupName: vnet.outputs.virtualNetworkResourceGroup
256+
vnetSubscriptionId: vnet.outputs.virtualNetworkSubscriptionId
257+
peSubnetName: vnet.outputs.peSubnetName
258+
}
259+
}
260+
247261
// Configure Managed Network for AI Services Account
248262
// This module sets up the managed virtual network and outbound PE rules to allow
249263
// secure communication from hosted agents to customer resources (Storage, AI Search, Cosmos DB)
@@ -255,12 +269,14 @@ module managedNetwork 'modules-network-secured/managed-network.bicep' = {
255269
storageAccountResourceId: storage.id
256270
cosmosDBResourceId: cosmosDB.id
257271
aiSearchResourceId: aiSearch.id
272+
amplsResourceId: ampls.outputs.amplsResourceId
258273
}
259274
dependsOn: [
260275
aiDependencies // Ensure dependent resources (Storage, CosmosDB, Search) are fully created
261276
networkApproverRoleStorage
262277
networkApproverRoleCosmos
263278
networkApproverRoleSearch
279+
ampls // Ensure AMPLS is created before adding the outbound rule
264280
]
265281
}
266282

@@ -324,6 +340,12 @@ module aiProject 'modules-network-secured/ai-project-identity.bicep' = {
324340
azureStorageName: aiDependencies.outputs.azureStorageName
325341
azureStorageSubscriptionId: aiDependencies.outputs.azureStorageSubscriptionId
326342
azureStorageResourceGroupName: aiDependencies.outputs.azureStorageResourceGroupName
343+
344+
// Application Insights for telemetry
345+
appInsightsName: ampls.outputs.appInsightsName
346+
appInsightsConnectionString: ampls.outputs.appInsightsConnectionString
347+
appInsightsResourceId: ampls.outputs.appInsightsResourceId
348+
327349
// dependent resources
328350
accountName: aiAccount.outputs.accountName
329351
}

infrastructure/infrastructure-setup-bicep/18-managed-virtual-network/modules-network-secured/ai-project-identity.bicep

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ param azureStorageName string
1616
param azureStorageSubscriptionId string
1717
param azureStorageResourceGroupName string
1818

19+
@description('Application Insights resource name for telemetry')
20+
param appInsightsName string = ''
21+
22+
@description('Application Insights connection string')
23+
param appInsightsConnectionString string = ''
24+
25+
@description('Application Insights resource ID')
26+
param appInsightsResourceId string = ''
27+
1928
resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' existing = {
2029
name: aiSearchName
2130
scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName)
@@ -90,6 +99,23 @@ resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-previ
9099
dependsOn: [project_connection_azure_storage]
91100
}
92101

102+
resource project_connection_app_insights 'connections@2025-04-01-preview' = if (appInsightsName != '') {
103+
name: appInsightsName
104+
properties: {
105+
category: 'AppInsights'
106+
target: appInsightsResourceId
107+
authType: 'ApiKey'
108+
credentials: {
109+
key: appInsightsConnectionString
110+
}
111+
metadata: {
112+
ApiType: 'Azure'
113+
ResourceId: appInsightsResourceId
114+
}
115+
}
116+
dependsOn: [project_connection_azureai_search]
117+
}
118+
93119
}
94120

95121
output projectName string = project.name
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
@description('Location for all resources')
2+
param location string
3+
4+
@description('Unique suffix for resource naming')
5+
param suffix string
6+
7+
@description('Name of the VNet to link DNS zones to')
8+
param vnetName string
9+
10+
@description('Resource group of the VNet')
11+
param vnetResourceGroupName string
12+
13+
@description('Subscription ID of the VNet')
14+
param vnetSubscriptionId string
15+
16+
@description('Name of the subnet for private endpoints')
17+
param peSubnetName string
18+
19+
// Reference existing VNet and subnet
20+
resource vnet 'Microsoft.Network/virtualNetworks@2023-04-01' existing = {
21+
name: vnetName
22+
scope: resourceGroup(vnetSubscriptionId, vnetResourceGroupName)
23+
}
24+
25+
resource peSubnet 'Microsoft.Network/virtualNetworks/subnets@2023-04-01' existing = {
26+
parent: vnet
27+
name: peSubnetName
28+
}
29+
30+
// Create Log Analytics Workspace
31+
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
32+
name: 'log-analytics-${suffix}'
33+
location: location
34+
properties: {
35+
sku: {
36+
name: 'PerGB2018'
37+
}
38+
retentionInDays: 30
39+
}
40+
}
41+
42+
// Create Application Insights
43+
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
44+
name: 'app-insights-${suffix}'
45+
location: location
46+
kind: 'web'
47+
properties: {
48+
Application_Type: 'web'
49+
WorkspaceResourceId: logAnalyticsWorkspace.id
50+
}
51+
}
52+
53+
// Create Azure Monitor Private Link Scope
54+
resource ampls 'microsoft.insights/privateLinkScopes@2021-07-01-preview' = {
55+
name: 'ampls-${suffix}'
56+
location: 'global'
57+
properties: {
58+
accessModeSettings: {
59+
ingestionAccessMode: 'PrivateOnly'
60+
queryAccessMode: 'PrivateOnly'
61+
}
62+
}
63+
}
64+
65+
// Link Application Insights to AMPLS
66+
resource appInsightsLink 'Microsoft.Insights/privateLinkScopes/scopedResources@2021-07-01-preview' = {
67+
parent: ampls
68+
name: 'appinsights-link'
69+
properties: {
70+
linkedResourceId: appInsights.id
71+
}
72+
}
73+
74+
// Link Log Analytics Workspace to AMPLS
75+
resource logAnalyticsLink 'Microsoft.Insights/privateLinkScopes/scopedResources@2021-07-01-preview' = {
76+
parent: ampls
77+
name: 'loganalytics-link'
78+
properties: {
79+
linkedResourceId: logAnalyticsWorkspace.id
80+
}
81+
}
82+
83+
// Private DNS Zones required for AMPLS
84+
var amplsDnsZones = [
85+
'privatelink.monitor.azure.com'
86+
'privatelink.oms.opinsights.azure.com'
87+
'privatelink.ods.opinsights.azure.com'
88+
'privatelink.agentsvc.azure-automation.net'
89+
]
90+
91+
resource dnsZones 'Microsoft.Network/privateDnsZones@2020-06-01' = [
92+
for zone in amplsDnsZones: {
93+
name: zone
94+
location: 'global'
95+
}
96+
]
97+
98+
resource dnsZoneLinks 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = [
99+
for (zone, i) in amplsDnsZones: {
100+
parent: dnsZones[i]
101+
name: '${replace(zone, '.', '-')}-link'
102+
location: 'global'
103+
properties: {
104+
virtualNetwork: {
105+
id: vnet.id
106+
}
107+
registrationEnabled: false
108+
}
109+
}
110+
]
111+
112+
// Create Private Endpoint for AMPLS
113+
resource amplsPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = {
114+
name: 'ampls-pe-${suffix}'
115+
location: location
116+
properties: {
117+
subnet: {
118+
id: peSubnet.id
119+
}
120+
privateLinkServiceConnections: [
121+
{
122+
name: 'ampls-connection'
123+
properties: {
124+
privateLinkServiceId: ampls.id
125+
groupIds: [
126+
'azuremonitor'
127+
]
128+
}
129+
}
130+
]
131+
}
132+
dependsOn: [
133+
appInsightsLink
134+
logAnalyticsLink
135+
]
136+
}
137+
138+
// DNS Zone Group for automatic DNS record registration
139+
resource dnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-04-01' = {
140+
parent: amplsPrivateEndpoint
141+
name: 'ampls-dns-group'
142+
properties: {
143+
privateDnsZoneConfigs: [
144+
{
145+
name: 'monitor'
146+
properties: {
147+
privateDnsZoneId: dnsZones[0].id
148+
}
149+
}
150+
{
151+
name: 'oms'
152+
properties: {
153+
privateDnsZoneId: dnsZones[1].id
154+
}
155+
}
156+
{
157+
name: 'ods'
158+
properties: {
159+
privateDnsZoneId: dnsZones[2].id
160+
}
161+
}
162+
{
163+
name: 'agentsvc'
164+
properties: {
165+
privateDnsZoneId: dnsZones[3].id
166+
}
167+
}
168+
]
169+
}
170+
}
171+
172+
output amplsResourceId string = ampls.id
173+
output appInsightsName string = appInsights.name
174+
output appInsightsResourceId string = appInsights.id
175+
output appInsightsConnectionString string = appInsights.properties.ConnectionString
176+
output logAnalyticsWorkspaceId string = logAnalyticsWorkspace.id

infrastructure/infrastructure-setup-bicep/18-managed-virtual-network/modules-network-secured/managed-network.bicep

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ param cosmosDBResourceId string
1717
@description('Resource ID of the AI Search Service for outbound PE rule')
1818
param aiSearchResourceId string
1919

20+
@description('Resource ID of the Azure Monitor Private Link Scope for telemetry')
21+
param amplsResourceId string
22+
2023
// Reference the existing AI Services account in the same resource group
2124
resource aiAccount 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = {
2225
name: accountName
@@ -100,4 +103,21 @@ resource aiSearchOutboundRule 'Microsoft.CognitiveServices/accounts/managedNetwo
100103
dependsOn: [cosmosDBOutboundRule]
101104
}
102105

106+
// Outbound PE rule for Azure Monitor Private Link Scope (AMPLS)
107+
// This allows the hosted agent to export telemetry to Application Insights privately
108+
#disable-next-line BCP081
109+
resource amplsOutboundRule 'Microsoft.CognitiveServices/accounts/managedNetworks/outboundRules@2025-10-01-preview' = {
110+
parent: managedNetwork
111+
name: 'ampls-monitor-rule'
112+
properties: {
113+
type: 'PrivateEndpoint'
114+
destination: {
115+
serviceResourceId: amplsResourceId
116+
subresourceTarget: 'azuremonitor'
117+
}
118+
category: 'UserDefined'
119+
}
120+
dependsOn: [aiSearchOutboundRule]
121+
}
122+
103123
output managedNetworkSettingsName string = managedNetwork.name

infrastructure/infrastructure-setup-terraform/18-managed-virtual-network/ai-foundry.tf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,8 @@ resource "time_sleep" "wait_outbound_rules" {
325325
azapi_resource.aiservices_outbound_rule,
326326
azapi_resource.storage_outbound_rule,
327327
azapi_resource.cosmos_outbound_rule,
328-
azapi_resource.aisearch_outbound_rule
328+
azapi_resource.aisearch_outbound_rule,
329+
azapi_resource.ampls_outbound_rule
329330
]
330331
}
331332

0 commit comments

Comments
 (0)