Skip to main content

bicep-azd-patterns

Bicep infrastructure templates and patterns for Azure Developer CLI (azd) projects using Azure Verified Modules (AVM). Use when writing main.bicep, calling AVM modules directly, configuring Container Apps in Bicep, managing parameters, outputs, tags, RBAC assignments, or aligning environment variables between Bicep and application code. Triggers on Bicep, main.bicep, parameters.json, azd tags, RBAC, Container Apps Bicep, AVM, module, output.

설치로 이동

소스 정보

저장소
aiappsgbb/template
최근 소스 활동
2026년 2월 26일 21:12
감지된 SKILL.md 언어
영어
스타
0
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
bicep-azd-patterns
description
Bicep infrastructure templates and patterns for Azure Developer CLI (azd) projects using Azure Verified Modules (AVM). Use when writing main.bicep, calling AVM modules directly, configuring Container Apps in Bicep, managing parameters, outputs, tags, RBAC assignments, or aligning environment variables between Bicep and application code. Triggers on Bicep, main.bicep, parameters.json, azd tags, RBAC, Container Apps Bicep, AVM, module, output.
# Bicep Templates for Azure Developer CLI (azd) Patterns for writing Bicep infrastructure using **Azure Verified Modules (AVM)** directly from the public Bicep registry (`br/public:avm/...`). No wrapper modules in `infra/core/` — call AVM modules directly in `main.bicep`. ## main.bicep Organization (7 sections) ```bicep targetScope = 'resourceGroup' // ── 1. METADATA ────────────────────────────────────────────── metadata description = 'Main infrastructure template for [Project Name]' // ── 2. PARAMETERS ──────────────────────────────────────────── @description('Environment name') @minLength(1) @maxLength(64) param environmentName string @description('Primary location for all resources') param location string @description('Principal ID for local RBAC (empty in CI)') param principalId string = '' // Per-service image + exists params (see Container Image Preservation) @description('Container image for the api service') param apiImageName string = '' @description('Whether the api Container App already exists') param apiExists bool = false // ── 3. VARIABLES ───────────────────────────────────────────── var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName } // ── 4. SHARED INFRASTRUCTURE ───────────────────────────────── module managedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = { name: 'managedIdentity' scope: rg params: { name: '${abbrs.managedIdentityUserAssignedIdentities}${resourceToken}' location: location tags: tags } } module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.12.0' = { /* ... */ } module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = { /* ... */ } module keyVault 'br/public:avm/res/key-vault/vault:0.13.3' = { /* ... */ } // ── 5. HOSTING INFRASTRUCTURE ──────────────────────────────── module containerRegistry 'br/public:avm/res/container-registry/registry:0.9.3' = { /* ... */ } module containerAppsEnvironment 'br/public:avm/res/app/managed-environment:0.11.3' = { /* ... */ } // ── 6. APPLICATION MODULES ─────────────────────────────────── module api 'br/public:avm/res/app/container-app:0.18.1' = { name: 'api' scope: rg params: { name: '${abbrs.appContainerApps}api-${resourceToken}' tags: union(tags, { 'azd-service-name': 'api' }) // ← REQUIRED for azd environmentResourceId: containerAppsEnvironment.outputs.resourceId containers: [ { name: 'main' image: !empty(apiImageName) ? apiImageName : 'mcr.microsoft.com/k8se/quickstart:latest' resources: { cpu: json('0.5'), memory: '1Gi' } env: [ { name: 'AZURE_CLIENT_ID', value: managedIdentity.outputs.clientId } ] } ] ingressExternal: true ingressTargetPort: 8000 } } // ── 7. OUTPUTS ─────────────────────────────────────────────── output AZURE_LOCATION string = location output AZURE_RESOURCE_GROUP string = resourceGroup().name output AZURE_CLIENT_ID string = managedIdentity.outputs.clientId output SERVICE_API_ENDPOINT_URL string = api.outputs.fqdn output SERVICE_API_NAME string = api.outputs.name ``` ## Parameter Validation ```bicep @description('Environment name') @minLength(1) @maxLength(64) param environmentName string @description('SKU tier') @allowed(['basic', 'standard', 'premium']) param skuTier string = 'basic' @description('Container image (managed by azd deploy)') param containerImage string = '' ``` Always add `@description` to every parameter. Use `@allowed`, `@minLength`, `@maxLength`, `@minValue`, `@maxValue` where applicable. ## main.parameters.json Every parameter without a default in main.bicep **must** appear here: ```json { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "environmentName": { "value": "${AZURE_ENV_NAME}" }, "location": { "value": "${AZURE_LOCATION}" }, "principalId": { "value": "${AZURE_PRINCIPAL_ID}" }, "apiImageName": { "value": "${SERVICE_API_IMAGE_NAME=}" }, "apiExists": { "value": "${SERVICE_API_RESOURCE_EXISTS=false}" } } } ``` - `${AZURE_ENV_NAME}` and `${AZURE_LOCATION}` are auto-populated by azd. - `${SERVICE_<NAME>_IMAGE_NAME=}` — the `=` provides an empty default for first deploy. - `${SERVICE_<NAME>_RESOURCE_EXISTS=false}` — defaults to `false` for first deploy. ## Container Image Preservation (IMAGE_NAME / RESOURCE_EXISTS) For each service with `host: containerapp` in azure.yaml, azd manages two variables to prevent re-provision from overwriting the deployed image: | Variable | Set By | Purpose | |----------|--------|---------| | `SERVICE_<NAME>_IMAGE_NAME` | `azd deploy` | Currently deployed image tag | | `SERVICE_<NAME>_RESOURCE_EXISTS` | `azd provision` | Whether resource already exists | ### Bicep usage ```bicep param apiImageName string = '' param apiExists bool = false module api 'br/public:avm/res/app/container-app:0.18.1' = { name: 'api' scope: rg params: { name: '${abbrs.appContainerApps}api-${resourceToken}' tags: union(tags, { 'azd-service-name': 'api' }) environmentResourceId: containerAppsEnvironment.outputs.resourceId containers: [ { name: 'main' image: !empty(apiImageName) ? apiImageName : 'mcr.microsoft.com/k8se/quickstart:latest' resources: { cpu: json('0.5'), memory: '1Gi' } } ] ingressExternal: true ingressTargetPort: 8000 } } ``` ### What breaks without this | Scenario | Without these params | With them | |----------|---------------------|-----------| | First `azd provision` | ✅ Works | ✅ Works | | Re-run `azd provision` | ❌ Overwrites deployed image | ✅ Preserves image | | `azd up` (provision + deploy) | ⚠️ Temporary downtime | ✅ No downtime | ## Tags ### Resource Group ```bicep var tags = { 'azd-env-name': environmentName // REQUIRED — azd uses this to find resources } ``` ### Service Resources (Container Apps, Functions, App Service) ```bicep tags: union(tags, { 'azd-service-name': 'api' // MUST match service key in azure.yaml }) ``` ## Output Naming Convention ```bicep // Environment info output AZURE_LOCATION string = location output AZURE_RESOURCE_GROUP string = resourceGroup().name // Identity output AZURE_CLIENT_ID string = managedIdentity.outputs.clientId // Per-service endpoints (pattern: SERVICE_<NAME>_<PROPERTY>) output SERVICE_API_ENDPOINT_URL string = api.outputs.fqdn output SERVICE_API_NAME string = api.outputs.name output SERVICE_WEB_ENDPOINT_URL string = web.outputs.fqdn // Infrastructure endpoints (consumed by app config) output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.uri output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.outputs.loginServer ``` Outputs automatically become azd environment variables. ## RBAC Assignments Always guard with `if (!empty(principalId))`: ```bicep resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(principalId)) { name: guid(resource.id, principalId, roleId) scope: resource properties: { roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleId) principalId: principalId principalType: 'ServicePrincipal' } } ``` ### Common Role IDs | Role | ID | |------|----| | Cognitive Services User | `a97b65f3-24c7-4388-baec-2e87135dc908` | | Key Vault Secrets User | `4633458b-17de-408a-b874-0445c86b69e6` | | Storage Blob Data Contributor | `ba92f5b4-2d11-453d-a403-e96b0029c9fe` | | Search Service Contributor | `7ca78c08-252a-4471-8644-bb5ff32d4ba0` | | AcrPull | `7f951dda-4ed3-4680-a7ca-43fe172d538d` | **Never use `Contributor` or `Owner`** when a specific role suffices. ## AVM Module Reference All infrastructure uses Azure Verified Modules directly from `br/public:avm/...`: | Resource | AVM Module | Version | |----------|-----------|--------| | Managed Identity | `br/public:avm/res/managed-identity/user-assigned-identity` | 0.4.1 | | Log Analytics | `br/public:avm/res/operational-insights/workspace` | 0.12.0 | | App Insights | `br/public:avm/res/insights/component` | 0.6.0 | | Key Vault | `br/public:avm/res/key-vault/vault` | 0.13.3 | | Container Registry | `br/public:avm/res/container-registry/registry` | 0.9.3 | | Container Apps Env | `br/public:avm/res/app/managed-environment` | 0.11.3 | | Container App | `br/public:avm/res/app/container-app` | 0.18.1 | | Azure OpenAI | `br/public:avm/res/cognitive-services/account` | 0.10.1 | | AI Search | `br/public:avm/res/search/search-service` | 0.11.1 | | Cosmos DB | `br/public:avm/res/document-db/database-account` | 0.16.0 | | Storage Account | `br/public:avm/res/storage/storage-account` | 0.27.0 | Docs: [Azure Verified Modules](https://azure.github.io/Azure-Verified-Modules/) ## Module Template Use AVM modules directly — no wrapper modules needed: ```bicep // Direct AVM call in main.bicep module svc 'br/public:avm/res/<provider>/<resource>:<version>' = { name: 'svc' scope: rg params: { name: '${abbrs.prefix}${resourceToken}' location: location tags: tags roleAssignments: !empty(managedIdentity.outputs.principalId) ? [ { principalId: managedIdentity.outputs.principalId roleDefinitionIdOrName: 'Role Name' principalType: principalType } ] : [] } } // AVM outputs follow: .outputs.resourceId, .outputs.name output SVC_ID string = svc.outputs.resourceId output SVC_NAME string = svc.outputs.name ``` ## Environment Variable Alignment Container Apps env vars **must match** the application's configuration class. With AVM Container App module, env vars go inside the `containers` array: ```bicep // Bicep — AVM Container App module module api 'br/public:avm/res/app/container-app:0.18.1' = { params: { containers: [ { name: 'main' image: apiImage env: [ { name: 'AZURE_CLIENT_ID', value: managedIdentity.outputs.clientId } { name: 'AZURE_OPENAI_ENDPOINT', value: azureOpenAi.outputs.endpoint } { name: 'APPLICATION_INSIGHTS_CONNECTION_STRING', value: applicationInsights.outputs.connectionString } ] } ] } } ``` ```python # Python app config class Settings(BaseSettings): azure_client_id: str | None = None azure_openai_endpoint: str application_insights_connection_string: str ``` ## Shared Subscription Considerations - Use `uniqueString(subscription().id, environmentName, location)` for globally unique names (Key Vault, Storage, ACR, Cognitive Services) - Include `environmentName` in resource group name for isolation - Handle soft-delete conflicts (Key Vault 90-day, Cognitive Services 48-hour retention) - Use quota-friendly defaults (basic SKUs, minimal CPU/memory) ## Common Pitfalls | Pitfall | Fix | |---------|-----| | Missing `azd-env-name` tag | azd can't find resources → add to `tags` variable | | Missing `azd-service-name` tag | `azd deploy` fails → add `union(tags, {'azd-service-name': '...'})` | | Missing IMAGE_NAME/EXISTS params | Re-provision overwrites deployed image → add both params |
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기