Skip to content

Commit 9813b37

Browse files
committed
Streamline project for production readiness
- Simplify Bicep templates: credentials added manually after deployment - Remove incomplete CosmosDB template (dead code) - Use DRY pattern in Log Analytics template (table schemas reused) - Update .env.example with correct variable names - Clean up .bicepparam files Deployment flow is now: 1. Deploy Bicep template (just baseName required) 2. Add credentials in portal: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET 3. Grant Graph permissions + ADX Ingestor role (or DCR role for Log Analytics) 4. Upload code via Deployment Center https://claude.ai/code/session_01BhLDKYffqvD2B2V71qLeV2
1 parent 9d030ea commit 9813b37

5 files changed

Lines changed: 237 additions & 857 deletions

File tree

.env.example

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@
55
# NEVER commit .env to source control!
66
# =============================================================================
77

8-
# Azure Data Explorer
8+
# Backend choice: 'ADX' or 'LogAnalytics'
9+
ANALYTICS_BACKEND=ADX
10+
11+
# ---- ADX Backend ----
912
ADX_CLUSTER_URI=https://yourcluster.region.kusto.windows.net
1013
ADX_DATABASE=IntuneAnalytics
1114

12-
# Azure AD / Entra ID (for local development with Service Principal)
13-
TENANT_ID=your-tenant-id
14-
CLIENT_ID=your-app-registration-client-id
15-
CLIENT_SECRET=your-app-registration-client-secret
15+
# ---- Log Analytics Backend ----
16+
# LOG_ANALYTICS_DCE=https://your-dce.uksouth.ingest.monitor.azure.com
17+
# LOG_ANALYTICS_DCR_ID=dcr-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
18+
# LOG_ANALYTICS_WORKSPACE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
1619

17-
# Optional: Override function behavior
18-
# SKIP_COMPLIANCE_STATES=false
19-
# SKIP_ENDPOINT_ANALYTICS=false
20-
# MAX_DEVICES=0
21-
# DRY_RUN=false
20+
# ---- App Registration Credentials ----
21+
AZURE_TENANT_ID=your-tenant-id
22+
AZURE_CLIENT_ID=your-app-registration-client-id
23+
AZURE_CLIENT_SECRET=your-app-registration-client-secret

deployment/adx/main.bicep

Lines changed: 44 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
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
1418
param 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
'''

deployment/adx/main.bicepparam

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
using 'main.bicep'
22

3-
// Example parameter values - customize as needed
3+
// Customize these values
44
param baseName = 'intune'
55
param location = 'uksouth'
6+
7+
// Optional: Set these if you already have an ADX cluster
8+
// Otherwise leave empty and set after deployment
69
param adxClusterUri = ''
7-
param adxDatabaseName = 'IntuneAnalytics'
10+
param adxDatabase = 'IntuneAnalytics'

0 commit comments

Comments
 (0)