Menu

How to model Azure infrastructure creation in microservice environment with Bicep Modules ?

Background

Are you operating in a complex Azure environment which contains multiple independent microservices and you're wondering how to model environment with infrastructure as a code? This blog post is for you.

This blog post shows how to structure/model infrastructure creation effectively with Bicep Modules in a environment which contains multiple independent microservices. This presented approach assumes that whole Azure infrastructure is created via single centralized Azure DevOps pipeline. Microservice specific CI/CD pipelines are only responsible for deploying the application not Azure infrastructure.

Centralized Azure infrastructure creation pipeline enables easily re-usability of Bicep Modules and makes microservice specific pipelines more light and faster to execute.

undefined

Another option is to a create Azure infrastructure in multiple smaller pieces in a microservice specific Azure DevOps pipelines. That approach requires some extra work and basically Azure Container Registry to effectively share Bicep Modules between multiple Azure DevOps pipelines/repositories. You can read more about this from my previous blog post.

undefined

Bicep module hierarchy

I have divided Bicep modules to four hierarchy levels to enable powerful re-usability and modularity. I'll go through these hierarchy levels from down to top in the below sections.

Like said earlier this presented approach assumes that whole Azure infrastructure is created via single centralized Azure DevOps pipeline and all Bicep Modules are in the same repository.

undefined

Pre-configured Resources

Pre-configured Resources (Bicep Modules) are prepared and configured according to company's security and compliance requirements. For example pre-configured App Service Module can force that FTP state is disabled and HTTP 2.0 & HTTPS are always enabled. Each module is independent and Azure resource specific. This model also enables to you can force specific resource naming policy efficiently. Pre-configured Resource modules are the foundation for Productized Resource Collections which I explain next. 

undefined

Sample Pre-configured App Service Module

This App Service Module forces automatically that FTP state is disabled and HTTP 2.0 & HTTPS are enabled.

param location string
param tags object
param servicePrefix string
param abbreviation string
param appServicePlanId string
param applicationInsightsConnectionString string
param resourceToken string

resource appService 'Microsoft.Web/sites@2022-03-01' = {
  name: '${abbreviation}${servicePrefix}-${location}-${resourceToken}'
  location: location
  tags: tags
  properties: {
    serverFarmId: appServicePlanId
    siteConfig: {
      ftpsState: 'Disabled'
      http20Enabled: true
      netFrameworkVersion: 'v6.0'
    }
    httpsOnly: true
  }

  identity: {
    type: 'SystemAssigned'
  }

  resource appSettings 'config' = {
    name: 'appsettings'
    properties: {
      SCM_DO_BUILD_DURING_DEPLOYMENT: 'false'
      APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsightsConnectionString
      ASPNETCORE_ENVIRONMENT: 'Development'
    }
  }
}

output appServiceId string = appService.id
output defaultHostName string = appService.properties.defaultHostName

Productized Resource Collections

From Bicep Module hierarchy point of view Productized Resource Collection modules are built top of Pre-configured Resource modules. 

Productized Resource Collection modules are always created for a specific use case. If your environment has multiple microservices which contains API+Database, it's beneficial to create Productized Resource Collection module for this purpose. With this module template you can easily create new microservices to you environment in the future which increase productivity and make maintenance more straightforward. 

Typical resource collections which can be productized:

- Microservice 1: App Service API + Application Insights + App Service Plan + Azure SQL database
-
Microservice 2: App Service API + Application Insights + App Service Plan + CosmosDB database
- Publish Subscribe: Azure Service Bus + Azure Function

undefined

Sample Productized Resource Collection for Publish Subscribe type of scenario

targetScope='resourceGroup'

param location string = resourceGroup().location
param tags object
param servicePrefix string
param resourceToken string

module appServicePlan '../../preconfigured/appservice/appServicePlan.bicep' = {
  name: 'appServicePlan-resources'
  params: {
    location: location
    tags: tags
    servicePrefix: servicePrefix
    sku: 'B1'
    kind: 'app,windows'
    resourceToken: resourceToken
  }
}

module logAnalyticsWorkspace '../../preconfigured/logging/logAnalyticsWorkspace.bicep' = {
  name: 'logAnalyticsWorkspace-resources'
  params: {
    location: location
    tags: tags
    servicePrefix: servicePrefix
    resourceToken: resourceToken
  }
}

module applicationInsightsResources '../../preconfigured/logging/applicationInsights.bicep' = {
  name: 'applicationinsights-resources'
  params: {
    location: location
    tags: tags
    workspaceId: logAnalyticsWorkspace.outputs.logAnalyticsWorkspaceId
    servicePrefix: servicePrefix
    resourceToken: resourceToken
  }
}

module storageAccount '../../preconfigured/storage/storageAccount.bicep' = {
  name: 'storageAccount-resources'
  params: {
    location: location
    tags: tags
    servicePrefix: servicePrefix
    resourceToken: resourceToken
    storageAccountType: 'Standard_LRS'
  }
}

module azureFunction '../../preconfigured/functions/azureFunction.bicep' = {
  name: 'azureFunction-resources'
  dependsOn:[
    appServicePlan
    logAnalyticsWorkspace
    applicationInsightsResources
    storageAccount
  ]
  params: {
    location: location
    tags: tags
    servicePrefix: servicePrefix
    resourceToken: resourceToken
    serverfarmId: appServicePlan.outputs.appServicePlanId
    instrumentationKey: applicationInsightsResources.outputs.applicationInsightsInstrumentationKey
  }
}

module serviceBus '../../preconfigured/messaging/serviceBus.bicep' = {
  name: 'serviceBus-resources'
  dependsOn:[
    azureFunction
  ]
  params: {
    location: location
    tags: tags
    servicePrefix: servicePrefix
    resourceToken: resourceToken
    sku: 'Standard'
  }
}

Core Infra

Core Infra determines each microservice and what Productized Resource Collection or Pre-Configured Resource modules are used to create that specific microservice. Core Infra is not only for microservices and it should determine also other shared resources.

undefined

Typically Core Infra contains multiple service specific Bicep Modules. From maintenance point of view it's easier to separate each service to own Bicep module file.

├── core
│   ├── microservices
│   │    ├── invoicing.bicep
│   │    ├── ordering.bicep
│   │    ├── products.bicep
│   ├── shared
│   │    ├── configuration.bicep

Sample Core Infra

This sample determines a Products microservice with SQL database and adds also Publish Subscribe capabilities. This approach enables to utilize multiple different Productized Resource Collections easily.

targetScope='resourceGroup'

param tags object
param location string

var microserviceName = 'products'

module microservicesWithSqlDatabase '../../modules/productized/microservices/microserviceSqlDatabase.bicep' = { 
  name:'microservicesWithSqlDatabase'
  params:{
    location: location
    resourceToken: toLower(uniqueString(subscription().id, microserviceName, location))
    servicePrefix: microserviceName
    tags: tags
  }
}

module microservicesWithPubSub '../../modules/productized/messaging/publishSubscribeServicebus.bicep' = { 
  name:'microservicesWithPubSub'
  dependsOn:[
    microservicesWithSqlDatabase
  ]
  params:{
    location: location
    resourceToken: toLower(uniqueString(subscription().id, microserviceName, location))
    servicePrefix: microserviceName
    tags: tags
  }
}

Main Bicep

Main Bicep orchestrates creation of the Azure infrastructure which contains multiple microservices and shared resources.

//This Main Bicep Module orchestrates creation of the Azure infrastructure which contains multiple microservices

targetScope='subscription'

param tags object
param location string
@secure()
param sqlAdminLogin string
@secure()
param sqlAdminPassword string

var resourceGroupNames = [
  'invoicing'
  'ordering'
  'products'
  'config'
]

//This creates resource groups for each microservice
module resourceGroups './modules/preconfigured/resourceGroup/resourceGroup.bicep' = [for resourceGroup in resourceGroupNames: { 
  name: resourceGroup
    params:{
    location: location
    name: 'rg-${resourceGroup}'
    tags: tags
  }
}]

//Shared services
module configuration './modules/core/shared/configuration.bicep' = {
  dependsOn:[
    resourceGroups
  ]
  scope: resourceGroup('rg-config') 
  name: 'configuration'
    params:{
    location: location
    tags: tags
  }
}

//Invoicing Microservice
module invoicing './modules/core/microservices/invoicing.bicep' = {
  dependsOn:[
    resourceGroups
  ]
  scope: resourceGroup('rg-invoicing') 
  name: 'invoicing'
    params:{
    location: location
    tags: tags
    sqlAdminLogin:sqlAdminLogin
    sqlAdminPassword:sqlAdminPassword
  }
}

//Products Microservice
module products './modules/core/microservices/products.bicep' = {
  dependsOn:[
    resourceGroups
  ]
  scope: resourceGroup('rg-products') 
  name: 'products'
    params:{
    location: location
    tags: tags
    sqlAdminLogin:sqlAdminLogin
    sqlAdminPassword:sqlAdminPassword
  }
}

//Ordering Microservice
module ordering './modules/core/microservices/ordering.bicep' = {
  dependsOn:[
    resourceGroups
  ]
  scope: resourceGroup('rg-ordering') 
  name: 'ordering'
    params:{
    location: location
    tags: tags
    sqlAdminLogin:sqlAdminLogin
    sqlAdminPassword:sqlAdminPassword
  }
}

Full Bicep Module hierarchy

undefined

Sample Azure infrastructure creation with Bicep modules can be found from my GitHub. Each Bicep Module hierarchy level has own folder in sample solution.

├── preconfigured
│   ├── appservice
│   │    ├── appservice.bicep
│   ├── configuration
│   │    ├── keyvault.bicep
│   ├── messaging
│   │    ├── servicebus.bicep
├── productized
│   ├── microservices
│   │    ├── microserviceSqlDatabase.bicep
│   ├── messaging
│   │    ├── publishSubscribeServicebus.bicep
├── core
│   ├── microservices
│   │    ├── invoicing.bicep
│   │    ├── ordering.bicep
│   │    ├── products.bicep
│   ├── shared
│   │    ├── configuration.bicep

Comments