11// ============================================================================
2- // Intune Analytics Platform - ADX Backend (Simplified)
2+ // Intune Analytics Platform - ADX Backend
33// ============================================================================
44// Deploys a Function App that exports Intune data to Azure Data Explorer.
5- // Uses app registration (client secret) for authentication.
6- // Upload function code manually via Deployment Center after deployment.
5+ //
6+ // After deployment:
7+ // 1. Add app registration credentials in Function App > Configuration:
8+ // - AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET
9+ // 2. Grant Graph API permissions to your app registration
10+ // 3. Upload function code via Deployment Center
711// ============================================================================
812
913@description ('Base name for all resources (max 11 chars)' )
@@ -14,36 +18,26 @@ param baseName string
1418param location string = resourceGroup ().location
1519
1620@description ('Azure Data Explorer cluster URI (e.g., https://mycluster.uksouth.kusto.windows.net)' )
17- param adxClusterUri string
21+ param adxClusterUri string = ''
1822
1923@description ('Azure Data Explorer database name' )
20- param adxDatabaseName string = 'IntuneAnalytics'
21-
22- @description ('App Registration - Tenant ID' )
23- param tenantId string = subscription ().tenantId
24-
25- @description ('App Registration - Client ID' )
26- param clientId string
27-
28- @secure ()
29- @description ('App Registration - Client Secret' )
30- param clientSecret string
24+ param adxDatabase string = 'IntuneAnalytics'
3125
3226// ============================================================================
3327// Variables
3428// ============================================================================
3529
36- var uniqueSuffix = uniqueString (resourceGroup ().id )
37- var functionAppName = '${baseName }-func-${ uniqueSuffix }'
38- var appServicePlanName = '${baseName }-asp -${uniqueSuffix }'
39- var storageAccountName = toLower ( '${take ( baseName , 11 )}${ take ( uniqueSuffix , 13 )}' )
30+ var suffix = uniqueString (resourceGroup ().id )
31+ var storageName = toLower ( '${take ( baseName , 11 )}${ take ( suffix , 13 )}' )
32+ var funcName = '${baseName }-func -${suffix }'
33+ var planName = '${baseName }-plan-${ suffix }'
4034
4135// ============================================================================
42- // Storage Account (required for Flex Consumption timer triggers )
36+ // Storage Account (required for Function App )
4337// ============================================================================
4438
45- resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
46- name : storageAccountName
39+ resource storage 'Microsoft.Storage/storageAccounts@2023-05-01' = {
40+ name : storageName
4741 location : location
4842 sku : { name : 'Standard_LRS' }
4943 kind : 'StorageV2'
@@ -58,53 +52,42 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
5852// App Service Plan (Flex Consumption)
5953// ============================================================================
6054
61- resource appServicePlan 'Microsoft.Web/serverfarms@2024-04-01' = {
62- name : appServicePlanName
55+ resource plan 'Microsoft.Web/serverfarms@2024-04-01' = {
56+ name : planName
6357 location : location
64- sku : {
65- name : 'FC1'
66- tier : 'FlexConsumption'
67- }
58+ sku : { name : 'FC1' , tier : 'FlexConsumption' }
6859 kind : 'functionapp'
69- properties : {
70- reserved : true
71- }
60+ properties : { reserved : true }
7261}
7362
7463// ============================================================================
7564// Function App
7665// ============================================================================
7766
78- resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
79- name : functionAppName
67+ resource func 'Microsoft.Web/sites@2024-04-01' = {
68+ name : funcName
8069 location : location
8170 kind : 'functionapp,linux'
8271 properties : {
83- serverFarmId : appServicePlan .id
72+ serverFarmId : plan .id
8473 httpsOnly : true
8574 functionAppConfig : {
8675 scaleAndConcurrency : {
8776 maximumInstanceCount : 40
8877 instanceMemoryMB : 2048
8978 }
90- runtime : {
91- name : 'python'
92- version : '3.11'
93- }
79+ runtime : { name : 'python' , version : '3.11' }
9480 }
9581 siteConfig : {
9682 appSettings : [
97- { name : 'AzureWebJobsStorage' , value : 'DefaultEndpointsProtocol=https;AccountName=${storageAccount .name };AccountKey=${storageAccount .listKeys ().keys [0 ].value };EndpointSuffix=${environment ().suffixes .storage }' }
83+ { name : 'AzureWebJobsStorage' , value : 'DefaultEndpointsProtocol=https;AccountName=${storage .name };AccountKey=${storage .listKeys ().keys [0 ].value };EndpointSuffix=${environment ().suffixes .storage }' }
9884 { name : 'FUNCTIONS_EXTENSION_VERSION' , value : '~4' }
9985 { name : 'SCM_DO_BUILD_DURING_DEPLOYMENT' , value : 'true' }
100- // Analytics backend
10186 { name : 'ANALYTICS_BACKEND' , value : 'ADX' }
10287 { name : 'ADX_CLUSTER_URI' , value : adxClusterUri }
103- { name : 'ADX_DATABASE' , value : adxDatabaseName }
104- // App registration credentials
105- { name : 'AZURE_TENANT_ID' , value : tenantId }
106- { name : 'AZURE_CLIENT_ID' , value : clientId }
107- { name : 'AZURE_CLIENT_SECRET' , value : clientSecret }
88+ { name : 'ADX_DATABASE' , value : adxDatabase }
89+ // Add these manually in portal after deployment:
90+ // AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET
10891 ]
10992 }
11093 }
@@ -114,14 +97,23 @@ resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
11497// Outputs
11598// ============================================================================
11699
117- output functionAppName string = functionApp .name
118- output functionAppUrl string = 'https://${functionApp .properties .defaultHostName }'
119- output deploymentCenterUrl string = 'https://portal.azure.com/#@${tenantId }/resource${functionApp .id }/vstscd'
100+ output functionAppName string = func .name
101+ output functionAppUrl string = 'https://${func .properties .defaultHostName }'
102+ output storageAccountName string = storage .name
103+
104+ output postDeploymentSteps string = '''
105+ After deployment, complete these steps:
106+
107+ 1. Add app registration credentials in Function App > Configuration > Application settings:
108+ - AZURE_TENANT_ID = your tenant ID
109+ - AZURE_CLIENT_ID = your app registration client ID
110+ - AZURE_CLIENT_SECRET = your app registration client secret
120111
121- output nextSteps string = '''
122- 1. Grant Graph API permissions to your app registration:
112+ 2. Grant Graph API permissions to your app registration:
123113 - DeviceManagementManagedDevices.Read.All
124114 - DeviceManagementConfiguration.Read.All
125- 2. Grant your app registration "Ingestor" role on the ADX database
126- 3. Upload function code via Deployment Center (ZIP deploy)
115+
116+ 3. Grant your app registration "Ingestor" role on the ADX database
117+
118+ 4. Upload function code via Deployment Center (ZIP deploy)
127119'''
0 commit comments