Using Bicep Modules to Build Enterprise-Grade Azure Infrastructure

Published: (June 12, 2026 at 05:48 AM EDT)
5 min read
Source: DevOps.com

Source: DevOps.com

Infrastructure as code (IaC) is no longer optional in modern Azure environments. Teams need repeatable deployments, secure defaults, predictable architecture and strong governance. Azure Bicep has become the preferred IaC language for Azure because it’s declarative, simple, modular and deeply integrated with the Azure platform.

This article breaks down how to design Bicep modules the right way for enterprise deployments. These patterns come from real-world use cases such as banking, fintech, multitenant SaaS and regulated workloads.

Why Bicep is the Standard for Azure IaC

Teams that move from ARM and Terraform to Bicep typically do so because Bicep offers:

  • Cleaner Syntax: No more massive JSON ARM templates.

  • Native Azure Integration

  • IntelliSense

  • Type-checking

  • Automatic API version updates

  • First-Class Modularity: Modules can describe reusable components like:

  • App Services

  • AKS clusters

  • Front Door Premium

  • Key Vault

  • VNet + subnets

  • WAF policies

  • Private endpoints

  • Better CI/CD experience: Easier validation, what-if deployment and GitHub Actions integration.

How to Structure Bicep Code for Large Azure Environments

A typical enterprise Bicep structure looks like this:

/bicep

  /modules

      aks/

         main.bicep

      appservice/

         main.bicep

      frontdoor/

         main.bicep

      keyvault/

         main.bicep

      network/

         vnet.bicep

         subnet.bicep

      storage/

         main.bicep

  /environment

      dev/

         main.bicep

         params.json

      qa/

         main.bicep

         params.json

      prod/

         main.bicep

         params.json

Key Points

  • Modules live separately and never store environment-specific values.

  • Environment folders contain:

  • main.bicep (composition file)

  • params.json (per-environment values)

This ensures consistency across dev → qa → prod.

Designing a Bicep Module Correctly

A module should follow five rules:

**1. It should deploy one resource (or a tightly-related set). **

Examples:

  • A single App Service

  • A single AKS cluster

  • A VNet with subnets

**2. It must not contain environment-specific values. **

These belong in parameter files.

**3. It should expose outputs. **

Useful for chaining modules:

output appServiceId string = appService.id

output principalId string = appService.identity.principalId

**4. It must include secure parameter types. **

@secure()

param adminPassword string

**5. It should include defaults but allow overrides. **

param sku string = ‘P1v3’

param httpsOnly bool = true

Example of an Enterprise Bicep Module (App Service + Custom Domain)

Here is a production-ready example you can reuse.

modules/appservice/main.bicep

param name string

param location string

param skuName string = ‘P1v3’

param httpsOnly bool = true

param customDomain string

param certificateThumbprint string

resource appService ‘Microsoft.Web/sites@2023-01-01’ = {

  name: name

  location: location

  properties: {

    httpsOnly: httpsOnly

  }

  sku: {

    name: skuName

    tier: ‘PremiumV3’

  }

}

resource binding ‘Microsoft.Web/sites/hostNameBindings@2023-01-01’ = {

  name: ‘${name}/${customDomain}’

  properties: {

    customHostNameDnsRecordType: ‘CName’

    sslState: ‘SniEnabled’

    thumbprint: certificateThumbprint

  }

}

output id string = appService.id

output defaultHostname string = appService.properties.defaultHostName

This module:

  • Deploys a premium App Service

  • Enforces HTTPS

  • Adds custom domain with SNI certificate binding

  • Exports outputs for Front Door or API Management

Composing Multiple Modules With an Environment File

Example: prod/main.bicep

param location string = ‘westeurope’

param appName string = ‘prod-myapp’

param domain string = ‘api.company.com’

param certThumbprint string

module appService ‘./modules/appservice/main.bicep’ = {

  name: ‘prodAppService’

  params: {

    name: appName

    location: location

    skuName: ‘P2v3’

    customDomain: domain

    certificateThumbprint: certThumbprint

  }

}

module frontdoor ‘./modules/frontdoor/main.bicep’ = {

  name: ‘prodFrontDoor’

  params: {

    backendHostname: appService.outputs.defaultHostname

    backendId: appService.outputs.id

  }

}

Why this matters:

  • Front Door depends on App Service output

  • Environment parameters flow through modules cleanly

  • No duplication of logic

  • Clear separation of concerns

Adding CI/CD With GitHub Actions

A recommended pipeline: Validate → what-if → deploy

name: Deploy Bicep

on:

  push:

    branches:

      – main

jobs:

  deploy:

    runs-on: ubuntu-latest

    steps:

    – uses: actions/checkout@v4

    – uses: azure/login@v1

      with:

        client-id: ${{ secrets.AZURE_CLIENT_ID }}

        tenant-id: ${{ secrets.AZURE_TENANT_ID }}

        subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    – name: Validate

      run: |

        az deployment sub validate \

          –template-file environment/prod/main.bicep

    – name: What-If

      run: |

        az deployment sub what-if \

          –template-file environment/prod/main.bicep

    – name: Deploy

      run: |

        az deployment sub create \

          –template-file environment/prod/main.bicep

This gives:

  • Predictable deployments

  • No manual approvals

  • Auditability

  • Cloud-native authentication via OIDC (no secrets)

Governance and Enforcement Using Azure Policy

Azure Policy can enforce IaC best practices, for example:

  • Allow only Bicep deployments (tagging rules)

  • Enforce HTTPS-only App Services

  • Enforce diagnostic logs

  • Prevent public IP creation

  • Require private endpoints

These policies make sure all deployments — Bicep or otherwise — follow standards.

Final Thoughts

Bicep is ideal for building large Azure environments when done correctly. By using a module-based approach, separating environment values, integrating CI/CD and combining everything with Azure Policy, you get:

  • Standardized deployments

  • Reusable patterns

  • Lower operational overhead

  • Strong governance

  • Easier AKS, App Service and Front Door automation

These practices are exactly what senior-level architects and MVP reviewers look for, because they demonstrate real-world engineering maturity.

                                                                                                                                                                                                                                                                                                                                                                                                                                                                            -                                                                                                                                                                                                                                                         

—>

0 views
Back to Blog

Related posts

Read more »