- ✅ Subscription-level deployment
- ✅ Parameters for all environments
- ✅ Module orchestration with proper dependencies
- ✅ Comprehensive outputs
- ✅ Deployment summary object
- ✅ Virtual Network (VNet) with 2 subnets
- ✅ Network Security Group with 4 rules (HTTPS, HTTP, RDP, Deny All)
- ✅ Static Public IP with DNS label
- ✅ Support for custom domain
- ✅ Log Analytics Workspace (90-day retention)
- ✅ Application Insights (workspace-based)
- ✅ Full integration and outputs
- ✅ Action Group with email notifications
- ✅ 5 Alert Rules:
- High CPU (>90% for 15 min)
- High Memory (<1GB available)
- Application Errors (>10 in 5 min)
- Slow Response (>3s avg)
- Bot Offline (no heartbeat for 10 min)
To complete the deployment, you still need to create:
Purpose: Synthetic monitoring for health checks
Resources Needed:
Microsoft.Insights/webTests- Health check test (every 5 min)Microsoft.Insights/webTests- Bot endpoint test (every 15 min)- Link to Action Group for alerts
Key Properties:
resource healthCheckTest 'Microsoft.Insights/webTests@2022-06-15' = {
name: '${prefix}-avtest-health-${environmentName}'
location: location
tags: union(tags, {
'hidden-link:${appInsightsId}': 'Resource'
})
properties: {
SyntheticMonitorId: '${prefix}-avtest-health-${environmentName}'
Name: 'Jarvis Health Check'
Enabled: true
Frequency: 300 // 5 minutes
Timeout: 30
Kind: 'ping'
RetryEnabled: true
Locations: [
{ Id: 'us-va-ash-azr' } // East US
{ Id: 'us-ca-sjc-azr' } // West US
{ Id: 'emea-nl-ams-azr' } // West Europe
{ Id: 'apac-sg-sin-azr' } // Southeast Asia
{ Id: 'apac-au-syd-azr' } // Australia East
]
Configuration: {
WebTest: '<WebTest><Items><Request Url="https://${testUrl}/healthz" /></Items></WebTest>'
}
}
}Purpose: Secure secrets and certificate storage
Resources Needed:
Microsoft.KeyVault/vaults- Key Vault with soft delete + purge protection- Access policies (will be set by identity module)
- Diagnostic settings to Log Analytics
Key Properties:
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: keyVaultName
location: location
tags: tags
properties: {
sku: {
family: 'A'
name: 'standard'
}
tenantId: subscription().tenantId
enabledForDeployment: true
enabledForTemplateDeployment: true
enabledForDiskEncryption: false
enableSoftDelete: true
softDeleteRetentionInDays: 90
enablePurgeProtection: true
enableRbacAuthorization: false
accessPolicies: [] // Set by identity module
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
ipRules: []
virtualNetworkRules: []
}
publicNetworkAccess: 'Enabled'
}
}Purpose: Boot diagnostics, logs, backups
Resources Needed:
Microsoft.Storage/storageAccounts- Standard LRS storage- Blob containers: boot-diagnostics, app-logs, backups
- Lifecycle management policy
- Soft delete enabled
Key Properties:
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
tags: tags
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
accessTier: 'Hot'
minimumTlsVersion: 'TLS1_2'
supportsHttpsTrafficOnly: true
allowBlobPublicAccess: false
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
}
encryption: {
services: {
blob: {
enabled: true
}
}
keySource: 'Microsoft.Storage'
}
}
}Purpose: Azure OpenAI + Speech Services
Resources Needed:
Microsoft.CognitiveServices/accounts- Azure OpenAI (Kind: 'OpenAI')Microsoft.CognitiveServices/accounts/deployments- GPT model deploymentMicrosoft.CognitiveServices/accounts- Speech Services (Kind: 'SpeechServices')- Diagnostic settings to Log Analytics
Important Note:
- OpenAI deployments require
@2023-05-01API version or later - Model deployment is a child resource
Purpose: Bot Framework Service for Teams
Resources Needed:
Microsoft.BotService/botServices- Bot Channels Registration- Teams channel configuration
Key Properties:
resource botService 'Microsoft.BotService/botServices@2023-09-15-preview' = {
name: botServiceName
location: 'global'
sku: {
name: 'S1'
}
kind: 'azurebot'
properties: {
displayName: 'Jarvis Meeting Bot'
endpoint: 'https://${customDomain}/api/messages'
msaAppId: botAppId
msaAppType: 'MultiTenant'
msaAppTenantId: subscription().tenantId
}
}
resource teamsChannel 'Microsoft.BotService/botServices/channels@2023-09-15-preview' = {
parent: botService
name: 'MsTeamsChannel'
location: 'global'
properties: {
channelName: 'MsTeamsChannel'
properties: {
enableCalling: true
isEnabled: true
}
}
}Purpose: Windows Server VM for hosting Jarvis
Resources Needed:
Microsoft.Compute/virtualMachines- Windows Server 2022 VM- System-assigned Managed Identity
- VM Extensions: AzureMonitorWindowsAgent, CustomScriptExtension
- Network Interface
- OS Disk (Premium SSD)
- Data Disk (Premium SSD)
Key Properties:
identity: {
type: 'SystemAssigned'
}
properties: {
hardwareProfile: {
vmSize: vmSize
}
osProfile: {
computerName: vmName
adminUsername: adminUsername
adminPassword: adminPassword
windowsConfiguration: {
enableAutomaticUpdates: true
patchSettings: {
patchMode: 'AutomaticByPlatform'
}
}
}
storageProfile: {
imageReference: {
publisher: 'MicrosoftWindowsServer'
offer: 'WindowsServer'
sku: '2022-datacenter-azure-edition'
version: 'latest'
}
osDisk: {
createOption: 'FromImage'
managedDisk: {
storageAccountType: 'Premium_LRS'
}
diskSizeGB: 127
}
dataDisks: [
{
lun: 0
createOption: 'Empty'
diskSizeGB: 128
managedDisk: {
storageAccountType: 'Premium_LRS'
}
}
]
}
diagnosticsProfile: {
bootDiagnostics: {
enabled: true
storageUri: 'https://${storageAccountName}.blob.${environment().suffixes.storage}/'
}
}
}Purpose: RBAC role assignments for Managed Identity
Resources Needed:
Microsoft.Authorization/roleAssignments(multiple)
Role Assignments:
// Key Vault Secrets User
resource kvRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(keyVaultId, vmPrincipalId, '4633458b-17de-408a-b874-0445c86b69e6')
scope: resourceId('Microsoft.KeyVault/vaults', keyVaultName)
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
principalId: vmPrincipalId
principalType: 'ServicePrincipal'
}
}
// Cognitive Services User (for OpenAI)
// Cognitive Services User (for Speech)
// Storage Blob Data Contributor
// Monitoring Metrics PublisherCreate 3 parameter files in bicep/parameters/:
prod.parameters.json:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": {
"value": "prod"
},
"location": {
"value": "eastus"
},
"prefix": {
"value": "jarvis"
},
"adminUsername": {
"value": "jarvadmin"
},
"adminPassword": {
"reference": {
"keyVault": {
"id": "/subscriptions/<sub-id>/resourceGroups/jarvis-secrets/providers/Microsoft.KeyVault/vaults/jarvis-secrets-kv"
},
"secretName": "vmAdminPassword"
}
},
"vmSize": {
"value": "Standard_D8s_v3"
},
"adminSourceIP": {
"value": "0.0.0.0/32"
},
"customDomain": {
"value": "jarvis.aicollaborator.net"
},
"openAIDeploymentName": {
"value": "gpt-4o-mini"
},
"openAIModelName": {
"value": "gpt-4o-mini"
},
"openAIModelVersion": {
"value": "2024-07-18"
},
"botAppId": {
"value": ""
}
}
}dev.parameters.json and staging.parameters.json - Similar structure with smaller VM sizes and different environment values.
param(
[Parameter(Mandatory=$true)]
[ValidateSet('dev','staging','prod')]
[string]$Environment,
[Parameter(Mandatory=$false)]
[string]$Location = 'eastus',
[Parameter(Mandatory=$false)]
[switch]$WhatIf
)
$ErrorActionPreference = 'Stop'
Write-Host "🚀 Deploying Jarvis infrastructure to: $Environment" -ForegroundColor Cyan
# Validate Bicep
Write-Host "`n✅ Validating Bicep templates..." -ForegroundColor Yellow
az bicep build --file bicep/main.bicep
# Deploy
$deploymentName = "jarvis-$Environment-$(Get-Date -Format 'yyyyMMddHHmmss')"
$params = @(
'--location', $Location,
'--template-file', 'bicep/main.bicep',
'--parameters', "bicep/parameters/$Environment.parameters.json",
'--name', $deploymentName
)
if ($WhatIf) {
$params += '--what-if'
}
Write-Host "`n🏗️ Starting deployment: $deploymentName" -ForegroundColor Yellow
az deployment sub create @params --verbose
Write-Host "`n✅ Deployment complete!" -ForegroundColor Green# Script to create Entra ID app registration for Jarvis bot
# Grants required Microsoft Graph API permissions
# Stores App ID and Secret in Key Vault# Run on VM after deployment
# Installs .NET 8.0 Runtime
# Configures Windows Service
# Sets up SSL certificate
# Configures environment variables- Create remaining Bicep modules: 2-3 hours
- Test deployment in dev: 1 hour
- Fix any issues: 1 hour
- Deploy to prod: 30 minutes
- Post-deployment configuration: 1 hour
Total: ~6 hours
- Complete remaining modules 5-11 using the guidance above
- Create parameter files for each environment
- Test deployment in dev environment
- Create PowerShell scripts for Entra app and post-deployment
- Deploy to production
All the architecture and patterns are in place. The remaining modules follow the same patterns as the completed ones!