Skill 45 · Azure Architecture Autopilot
Subchapter 45.5
references/bicep-generator.mdMarkdown20 KBView on GitHub
Receives the finalized architecture spec from Phase 1 and generates deployable Bicep templates.
Scripts
CLIAlso bundled
GitignoreDo not hardcode API versions in Bicep code. Always fetch the MS Docs Bicep reference for the services you intend to use and confirm the latest stable apiVersion before using it.
Verify that the model name specified by the user is actually deployable in the target region before generating Bicep. Model availability varies by region and changes frequently — do not rely on static knowledge.
Verification Methods (in priority order):
az cognitiveservices account list-models --name "<FOUNDRY_NAME>" --resource-group "<RG_NAME>" -o tableIf the model is not available in the target region:
The full URL registry is in references/azure-dynamic-sources.md. Refer to this file when fetching.
Reference files are located under the .github/skills/azure-architecture-autopilot/ path.
Important: Fetch directly from the URL using web_fetch to confirm the latest stable apiVersion. Do not blindly use hardcoded versions from reference files or previous conversations.
Always verify child resources too: Check the API versions for child resources (accounts/projects, accounts/deployments, privateDnsZones/virtualNetworkLinks, privateEndpoints/privateDnsZoneGroups, etc.) from the parent resource page. Parent and child API versions may differ.
Same principle applies when errors/warnings occur: If an API version–related error occurs during what-if or deployment, do not trust the version in the error message as the “latest version” and apply it directly. Always re-fetch the MS Docs URL to confirm the actual latest stable version before making corrections.
azure-dynamic-sources.mdisHnsEnabled, allowProjectManagement, etc.) → service-gotchas.mdservice-gotchas.mdazure-common-patterns.mdai-data.mdIf unsure about stable information, re-verify with MS Docs. But there is no need to fetch every time.
When the user requests a service not covered by the v1 scope (ai-data.md):
https://learn.microsoft.com/en-us/azure/templates/microsoft.{provider}/{resourceType} and fetchhttps://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns to confirm groupId/DNS Zoneazure-common-patterns.mdaz bicep buildThe following information must be finalized upon completion of Phase 1:
- services: [Service list + SKU]
- networking: Whether private_endpoint is used
- resource_group: Resource group name
- location: Deployment location (confirmed with user in Phase 1)
- subscription_id: Azure subscription ID<project-name>/
├── main.bicep # Main orchestration — module calls and parameter passing
├── main.bicepparam # Parameter file — environment-specific values, excluding sensitive info
└── modules/
├── network.bicep # VNet, Subnet (including pe-subnet)
├── ai.bicep # AI services (configured per user requirements)
├── storage.bicep # ADLS Gen2 (isHnsEnabled: true required)
├── fabric.bicep # Microsoft Fabric Capacity (only when needed)
├── keyvault.bicep # Key Vault
├── monitoring.bicep # Application Insights, Log Analytics (only needed for Hub-based configurations)
└── private-endpoints.bicep # All PEs + Private DNS Zones + VNet Links + DNS Zone GroupsprivateEndpointNetworkPolicies: 'Disabled' requiredMicrosoft.CognitiveServices/accounts, kind: 'AIServices') — Top-level AI resource
customSubDomainName: foundryName required — Cannot be changed after creation. If omitted, the resource must be deleted and recreatedidentity: { type: 'SystemAssigned' } requiredallowProjectManagement: true requiredMicrosoft.CognitiveServices/accounts/deployments) — Performed at the Foundry resource levelMicrosoft.CognitiveServices/accounts/projects) — Must be created as a child resource
Microsoft.CognitiveServices/accounts/projects (never create as a standalone accounts resource)parent: foundryAccount in Bicepkind: 'AIServices' account → Not recognized in the portalresource foundryProject 'Microsoft.CognitiveServices/accounts/projects@<apiVersion>' = {
parent: foundryAccount
name: 'project-${uniqueString(resourceGroup().id)}'
location: location
kind: 'AIServices'
properties: {}
}Microsoft.MachineLearningServices/workspaces) should only be considered when the user explicitly requests it or when ML training/open-source models are needed. For standard AI/RAG workloads, Foundry (AIServices) is the default choice⛔ CognitiveServices Prohibited Properties:
apiProperties.statisticsEnabled — This property does not exist. Never use it. Causes ApiPropertiesInvalid error during deploymentapiProperties.qnaAzureSearchEndpointId — QnA Maker only. Do not use with Foundryproperties.apiPropertiesisHnsEnabled: true ← Never omit thisallowBlobPublicAccess: false, minimumTlsVersion: 'TLS1_2'enableRbacAuthorization: true (do not use access policy model)enableSoftDelete: true, softDeleteRetentionInDays: 90enablePurgeProtection: trueMicrosoft.Network/privateEndpoints (placed in pe-subnet)Microsoft.Network/privateDnsZones + VNet Link (registrationEnabled: false)Microsoft.Network/privateEndpoints/privateDnsZoneGroupsreferences/service-gotchas.md⚠️ Foundry/AIServices PE DNS Rules:
accountprivatelink.cognitiveservices.azure.comprivatelink.openai.azure.com⚠️ ADLS Gen2 (isHnsEnabled: true) PE Rules:
blob → privatelink.blob.core.windows.netdfs → privatelink.dfs.core.windows.net⚠️ RBAC Role Assignment — Never Omit
Any service with a Managed Identity (identity.type: 'SystemAssigned') must have RBAC role assignments created.
Having an identity without role assignments causes inter-service authentication failures.
This is not optional — it is a mandatory item.
Omission will be reported as CRITICAL in Phase 3 review.
| Source Service | Target Service | Role | Role Definition ID |
|---|---|---|---|
| Foundry | Storage | Storage Blob Data Contributor | ba92f5b4-2d11-453d-a403-e96b0029c9fe |
| Foundry | AI Search | Search Index Data Contributor | 8ebe5a00-799e-43f5-93ac-243d3dce84a7 |
| Foundry | AI Search | Search Service Contributor | 7ca78c08-252a-4471-8644-bb5ff32d4ba0 |
| App Service | Key Vault | Key Vault Secrets User | 4633458b-17de-408a-b874-0445c86b69e6 |
| AKS (kubeletIdentity) | ACR | AcrPull | 7f951dda-4ed3-4680-a7ca-43fe172d538d |
| Data Factory | Storage | Storage Blob Data Contributor | ba92f5b4-2d11-453d-a403-e96b0029c9fe |
| Data Factory | Key Vault | Key Vault Secrets User | 4633458b-17de-408a-b874-0445c86b69e6 |
| Databricks | Storage | Storage Blob Data Contributor | ba92f5b4-2d11-453d-a403-e96b0029c9fe |
AKS Special Rule: AKS uses
identityProfile.kubeletidentity.objectId, notidentity.principalId.
// RBAC Example — Foundry → Storage Blob Data Contributor
resource foundryStorageRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storageAccount.id, foundry.id, 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
scope: storageAccount
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
principalId: foundry.identity.principalId
principalType: 'ServicePrincipal'
}
}@secure() param sqlAdminPassword string in main.bicep and pass it to modules
newGuid() inside modules — the password changes on redeploymentadministrators.azureADOnlyAuthentication: true
@secure() param vpnSharedKey string — @secure() is mandatory.bicepparam — provide at deployment time or use Key Vault reference@secure() decoratorpublicNetworkAccess: 'Disabled', you must also create the corresponding PE for that service// Use uniqueString to prevent naming collisions — always required
param foundryName string = 'foundry-${uniqueString(resourceGroup().id)}'
param searchName string = 'srch-${uniqueString(resourceGroup().id)}'
param storageName string = 'st${uniqueString(resourceGroup().id)}' // No special characters allowed
param keyVaultName string = 'kv-${uniqueString(resourceGroup().id)}'⚠️ Resources requiring
customSubDomainName(Foundry, Cognitive Services, etc.) must includeuniqueString(). Static strings (e.g.,'my-rag-chatbot') may already be in use by another tenant, causing deployment failures. The same applies to Foundry Project names —'project-${uniqueString(resourceGroup().id)}'
// Required for all services when using Private Endpoints
publicNetworkAccess: 'Disabled'
networkAcls: {
defaultAction: 'Deny'
ipRules: []
virtualNetworkRules: []
}// Use implicit dependencies via resource references instead of explicit dependsOn
resource aiProject '...' = {
properties: {
hubResourceId: aiHub.id // Reference to aiHub → aiHub is automatically deployed first
}
}// Use Key Vault references for sensitive values — never store plaintext in parameter files
@secure()
param adminPassword string // Do not put plaintext values in main.bicepparam// Microsoft Foundry resource — kind: 'AIServices'
// customSubDomainName: Required, globally unique. Cannot be changed after creation — if omitted, resource must be deleted and recreated
// allowProjectManagement: true is required or Foundry Project creation will fail
// Replace apiVersion with the latest version fetched in Step 0
resource foundry 'Microsoft.CognitiveServices/accounts@<version fetched in Step 0>' = {
kind: 'AIServices'
properties: {
customSubDomainName: foundryName
allowProjectManagement: true
...
}
}Module Declaration Validation:
name: property in each module block is not duplicatedname: 'deploy-sql'name: 'name: 'deploy-sql' (duplicated name: → compilation error)Duplicate Property Prevention:
gatewayType), Firewall, AKS, etc.BCP025: The property "xxx" is declared multiple times in the az bicep build outputaz bicep build Must Be Run:
az bicep build --file main.bicep// ============================================================
// Azure [Project Name] Infrastructure — main.bicep
// Generated: [Date]
// ============================================================
targetScope = 'resourceGroup'
// ── Common Parameters ─────────────────────────────────────
param location string // Location confirmed in Phase 1 — do not hardcode
param projectPrefix string
param vnetAddressPrefix string // ← Confirm with user. Prevent conflicts with existing networks
param peSubnetPrefix string // ← PE-dedicated subnet CIDR within the VNet
// ── Network ───────────────────────────────────────────────
module network './modules/network.bicep' = {
name: 'deploy-network'
params: {
location: location
vnetAddressPrefix: vnetAddressPrefix
peSubnetPrefix: peSubnetPrefix
}
}
// ── AI/Data Services ──────────────────────────────────────
module ai './modules/ai.bicep' = {
name: 'deploy-ai'
params: {
location: location
// Add separate params if regions differ per service — verify available regions in MS Docs
}
dependsOn: [network]
}
// ── Storage ───────────────────────────────────────────────
module storage './modules/storage.bicep' = {
name: 'deploy-storage'
params: {
location: location
}
}
// ── Key Vault ─────────────────────────────────────────────
module keyVault './modules/keyvault.bicep' = {
name: 'deploy-keyvault'
params: {
location: location
}
}
// ── Private Endpoints (All Services) ──────────────────────
module privateEndpoints './modules/private-endpoints.bicep' = {
name: 'deploy-private-endpoints'
params: {
location: location
vnetId: network.outputs.vnetId
peSubnetId: network.outputs.peSubnetId
foundryId: ai.outputs.foundryId
searchId: ai.outputs.searchId
storageId: storage.outputs.storageId
keyVaultId: keyVault.outputs.keyVaultId
}
}
// ── Outputs ───────────────────────────────────────────────
output vnetId string = network.outputs.vnetId
output foundryEndpoint string = ai.outputs.foundryEndpoint
output searchEndpoint string = ai.outputs.searchEndpointusing './main.bicep'
param location = '<Location confirmed in Phase 1>'
param projectPrefix = '<Project prefix>'
// Do not put sensitive values here — use Key Vault references
// Set regions after verifying per-service availability in MS DocsWhen a .bicepparam file contains a using directive, additional --parameters flags cannot be used with az deployment.
Therefore, @secure() parameters must follow these rules:
@secure() param password string = newGuid()main.parameters.json) alongside instead of using .bicepparam.bicepparam and --parameters key=value simultaneouslyThe full checklist is in references/service-gotchas.md. Key summary:
| Item | ❌ Incorrect | ✅ Correct |
|---|---|---|
| ADLS Gen2 | isHnsEnabled omitted | isHnsEnabled: true |
| PE Subnet | Policy not set | privateEndpointNetworkPolicies: 'Disabled' |
| PE Configuration | PE only created | PE + DNS Zone + VNet Link + DNS Zone Group |
| Foundry | kind: 'OpenAI' | kind: 'AIServices' + allowProjectManagement: true |
| Foundry | customSubDomainName omitted | customSubDomainName: foundryName — cannot be changed after creation |
| Foundry Project | Not created | Must always be created as a set with the Foundry resource |
| Hub Usage | Used for standard AI | Only when explicitly requested by user or ML/open-source models needed |
| Public Network | Not configured | publicNetworkAccess: 'Disabled' |
| Storage Name | Contains hyphens | Lowercase + digits only, uniqueString() recommended |
| API version | Copied from previous value | Fetch from MS Docs (Dynamic) |
| Region | Hardcoded | Parameter + verify availability in MS Docs (Dynamic) |
When Bicep generation is complete:
references/bicep-reviewer.md guidelines