When teams start with Bicep, it’s tempting to keep everything in a single large .bicep file. While this works for small experiments, it quickly becomes unmanageable in real-world solutions. Changes become risky, duplication creeps in, and debugging is painful.
That’s why using Bicep modules—small, reusable templates—is the recommended approach. Modules let you break down complex infrastructure into logical components, making your code easier to maintain, test, and share across projects.
// main.bicep (all-in-one)resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {name: 'myuniquestorage'location: resourceGroup().locationsku: {name: 'Standard_LRS'}kind: 'StorageV2'}resource appInsights 'Microsoft.Insights/components@2020-02-02' = {name: 'myappinsights'location: resourceGroup().locationkind: 'web'properties: {Application_Type: 'web'}}
❌ Figure: Bad Example - Multiple resources jammed into a single main module, hard to maintain and reuse
// storageAccount.bicep (module)param storageName stringparam location stringresource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {name: storageNamelocation: locationsku: {name: 'Standard_LRS'}kind: 'StorageV2'}// appInsights.bicep (module)param appInsightsName stringparam location stringresource appInsights 'Microsoft.Insights/components@2020-02-02' = {name: appInsightsNamelocation: locationkind: 'web'properties: {Application_Type: 'web'}}// main.bicepmodule storage './storageAccount.bicep' = {name: 'storageDeployment'params: {storageName: 'myuniquestorage'location: resourceGroup().location}}module appInsights './appInsights.bicep' = {name: 'appInsightsDeployment'params: {appInsightsName: 'myappinsights'location: resourceGroup().location}}
✅ Figure: Good Example - A clean main module calling smaller reusable modules for storage and application insights
By treating your Bicep modules like building blocks, you create a clean, reusable infrastructure-as-code foundation that grows with your projects.