# Writing Bicep Templates

> Provides Bicep coding standards for Azure infrastructure in this repository. Use when writing or modifying Bicep files, configuring Container Apps, setting up RBAC, or working with Azure resources.

- Skill: `tomevault-io/writing-bicep-templates` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/writing-bicep-templates`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/writing-bicep-templates/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/writing-bicep-templates

---


# Bicep Coding Standards

**Goal**: Create consistent, secure Azure infrastructure

## Naming Convention

Use `resourceToken` from `uniqueString()`:

```bicep
var token = toLower(uniqueString(subscription().id, environmentName, location))
name: '${abbrs.appContainerApps}web-${token}'  // ca-web-abc123
```

**Exception**: ACR requires alphanumeric only: `cr${resourceToken}`

## Parameters

Always add `@description()` and use `@allowed()` for constrained values:

```bicep
@description('Environment (dev, prod)')
param environmentName string

@description('Azure region')
@allowed(['eastus2', 'westus2'])
param location string = 'eastus2'
```

## Outputs

Expose key identifiers for `azd` and other modules:

```bicep
output containerAppName string = containerApp.name
output webEndpoint string = 'https://${containerApp.properties.configuration.ingress.fqdn}'
output identityPrincipalId string = containerApp.identity.principalId
```

## Managed Identity

Use a user-assigned MI for ACR pull and OBO (avoids circular dependencies). Create it in the infrastructure module so its `principalId` is available before the Container App:

```bicep
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = {
  name: '${abbrs.managedIdentityUserAssignedIdentities}web-${resourceToken}'
  location: location
  properties: { isolationScope: 'Regional' }
}
output managedIdentityPrincipalId string = managedIdentity.properties.principalId
```

Attach to Container App with `identity: { type: 'UserAssigned', userAssignedIdentities: { '${miId}': {} } }`. Use MI for ACR pull via `registries: [{ server: acr.loginServer, identity: miId }]`.

## RBAC Assignments

Use `guid()` for names + specify `principalType`:

```bicep
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(resource.id, principalId, roleId)
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleId)
    principalId: principalId
    principalType: 'ServicePrincipal'
  }
}
```

## Container Apps

Key settings: System identity + scale-to-zero + HTTPS only:

```bicep
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
  identity: { type: 'UserAssigned', userAssignedIdentities: { '${userAssignedIdentityId}': {} } }
  properties: {
    configuration: {
      ingress: {
        external: true
        targetPort: 8080
        allowInsecure: false
      }
    }
    template: {
      scale: { minReplicas: 0, maxReplicas: 3 }
    }
  }
}
```

## ACR Pull Pattern

Use user-assigned MI for ACR pull (no admin credentials or secrets):

```bicep
registries: [{
  server: containerRegistry.properties.loginServer
  identity: userAssignedIdentityId  // MI with AcrPull role
}]
```

## Validation

```powershell
az bicep build --file main.bicep
az deployment group what-if --template-file main.bicep
```

---

## Project-Specific: Module Hierarchy

```text
main.bicep (subscription scope)
├─ Resource group
├─ main-infrastructure.bicep (ACR + Container Apps Env + Log Analytics + User-Assigned MI)
├─ entra-app.bicep (SPA app + conditional OBO backend app with FIC + admin consent)
├─ main-app.bicep (Container App with MI-based ACR pull)
└─ RBAC (Cognitive Services User role via postprovision CLI)
```

## Project-Specific: Container App Configuration

```bicep
resource containerApp 'Microsoft.App/containerApps@2024-03-01' = {
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: { '${userAssignedIdentityId}': {} }
  }
  properties: {
    managedEnvironmentId: containerAppsEnvironmentId
    configuration: {
      ingress: {
        external: true
        targetPort: 8080
        allowInsecure: false
      }
      registries: [{
        server: containerRegistry.properties.loginServer
        identity: userAssignedIdentityId  // MI-based pull, no secrets
      }]
    }
    template: {
      containers: [{
        name: 'web'
        image: containerImage
        env: containerEnv  // Base env + conditional OBO env
        resources: { cpu: json('0.5'), memory: '1Gi' }
      }]
      scale: { minReplicas: 0, maxReplicas: 3 }
    }
  }
}

output fqdn string = containerApp.properties.configuration.ingress.fqdn
output identityPrincipalId string = containerApp.identity.principalId
```

## Related Skills

- **deploying-to-azure** - Deployment commands and hook workflow
- **writing-csharp-code** - Backend configuration for Container Apps
- **troubleshooting-authentication** - RBAC and managed identity debugging

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/microsoft-foundry) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

