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 
  • Automatic API version updates 
  • First-Class Modularity: Modules can describe reusable components like: 
  • 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: 

 

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’ = { 

  namename 

  locationlocation 

  properties: { 

    httpsOnlyhttpsOnly 

  } 

  sku: { 

    nameskuName 

    tier‘PremiumV3’ 

  } 

} 

 

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

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

  properties: { 

    customHostNameDnsRecordType‘CName’ 

    sslState‘SniEnabled’ 

    thumbprintcertificateThumbprint 

  } 

} 

 

output id string = appService.id 

output defaultHostname string = appService.properties.defaultHostName 

 

This module: 

  • Deploys a premium App Service 
  • 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: { 

    nameappName 

    locationlocation 

    skuName‘P2v3’ 

    customDomaindomain 

    certificateThumbprintcertThumbprint 

  } 

} 

 

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

  name‘prodFrontDoor’ 

  params: { 

    backendHostnameappService.outputs.defaultHostname 

    backendIdappService.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 

nameDeploy Bicep 

 

on: 

  push: 

    branches: 

      – main 

 

jobs: 

  deploy: 

    runs-onubuntu-latest 

 

    steps: 

    – usesactions/checkout@v4 

       

    – usesazure/login@v1 

      with: 

        client-id${{ secrets.AZURE_CLIENT_ID }} 

        tenant-id${{ secrets.AZURE_TENANT_ID }} 

        subscription-id${{ secrets.AZURE_SUBSCRIPTION_ID }} 

 

    – nameValidate 

      run| 

        az deployment sub validate \ 

          –template-file environment/prod/main.bicep 

 

    – nameWhat-If 

      run| 

        az deployment sub what-if \ 

          –template-file environment/prod/main.bicep 

 

    – nameDeploy 

      run| 

        az deployment sub create \ 

          –template-file environment/prod/main.bicep 

 

 

This gives: 

  • Predictable deployments 
  • 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 
  • Lower operational overhead 
  • 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. 

Share.
Leave A Reply