Bicep modules: stop copy-pasting your infrastructure
The moment your Bicep grows past a toy, one giant file becomes unreadable and you start copy-pasting the same storage-account block into every deployment. Modules are how infrastructure-as-code learns the lesson every programmer already knows: factor it into reusable functions.
New to cloud? CAMPUX is a free, build-first course. Start here →
Your first Bicep file is a happy single page. Your tenth is a 900-line monster where the same virtual-network pattern appears three times with tiny differences. That is the exact smell that, in application code, tells you to write a function — and Bicep has the same answer. Microsoft's definition is one sentence: "A module is a Bicep file that another Bicep file deploys." You write a resource pattern once, in its own file, and call it wherever you need it.
Define once, call anywhere
A module is just an ordinary Bicep file — say storageAccount.bicep — with parameters for the bits that vary and outputs for the values callers need back. A parent file consumes it with the module keyword:
module stg './storageAccount.bicep' = { name: 'storageDeploy' params: { storagePrefix: 'examplestg1' location: location } } // use an output the module returned output endpoint object = stg.outputs.storageEndpoint
That is the whole model: the module declares what varies (parameters) and what it hands back (outputs), and the parent supplies the parameters and reads the outputs. The value that comes out — a storage endpoint, a resource ID — flows into the rest of your deployment. It is functions, for infrastructure.
main.bicep composes modules — network, storage, compute — instead of repeating raw resource blocks. That keeps each piece small and testable and lets you reuse the same module across dev, test, and prod (varying only the parameters). Compose infrastructure the way you compose code: small units, wired together, not one giant file.Why it matters
Microsoft names the two payoffs directly: modules "improve the readability of your Bicep files by encapsulating complex details," and you "can easily reuse modules for different deployments." Concretely:
- Readability. The parent file reads like an outline — "deploy the network, then the database, then the app" — with the messy details hidden inside each module.
- Reuse. One well-written
storageAccount.bicepserves dev, test, and prod, and every project, instead of the same block copied and quietly drifting apart in a dozen places. - Encapsulation. Fix a bug or tighten a default once in the module, and every deployment that uses it inherits the fix.
A module is a function for infrastructure: name what varies, return what matters, and stop pasting the same block into every file.
Under the hood, your modules are compiled into a single ARM template with nested deployments, and Resource Manager still works out the ordering. Modules deploy in parallel unless one depends on another's output, in which case Bicep infers the dependency for you. So you get clean, factored source files without giving up the orchestration that makes a deployment reliable.
Sharing modules across a team
Reuse gets serious when a module leaves your repo. Bicep supports two homes for shared modules: a private module registry (backed by an Azure Container Registry, so your team pulls versioned modules like packages), and template specs. There is also a public registry of Azure Verified Modules — prebuilt, pre-tested modules that follow Well-Architected best practices, which you reference by a versioned path (br/public:avm/res/storage/storage-account:...). So before you hand-write the hundredth storage-account module, check whether Microsoft already maintains a verified one.
The takeaway
Modules are the step that turns Bicep from scripting into engineering. When a file gets long or a pattern repeats, extract it: a module with clear parameters and outputs, called from a parent that now reads like a table of contents. Reuse it across environments so a fix lands everywhere at once, share it through a registry or template spec, and reach for Azure Verified Modules before reinventing common ones. "Factor the deployment into parameterized modules, compose them in a parent, and pull common ones from the registry" is the working habit of someone who has started treating infrastructure as software they maintain, which, with Bicep, is precisely what it has become.
Questions people also ask
What is a module in Bicep?
A module is a Bicep file that another Bicep file deploys. You write a resource pattern once, in its own file, with parameters for what varies and outputs for what callers need back, then call it from a parent file wherever you need that pattern.
How do you call a module in Bicep?
You use the module keyword, give the call a symbolic name, point path at the module file, and supply a params block. The parent file references outputs through that symbolic name, for example stg.outputs.storageEndpoint, once the module deploys.
What is the difference between a Bicep file and a module?
There is no syntax difference. Any Bicep file becomes a module the moment another Bicep file deploys it with the module keyword. The distinction is a role, not a file type: a module is written to be parameterized and reused, not run on its own.
How do you share Bicep modules across a team?
Publish them to a private module registry backed by an Azure Container Registry, so the team pulls versioned modules like packages, or use template specs. For common patterns, check Azure Verified Modules first, a public registry of prebuilt modules that follow Well-Architected defaults.
Do Bicep modules deploy in parallel?
Yes, unless one module depends on another's output. Bicep compiles modules into a single ARM template with nested deployments, infers dependencies from output references automatically, and Resource Manager orders and parallelizes the deployment for you.