Menu

Bicep – language for deploying Azure resources (IaC)

Deploying resources to Azure for multiple environments (Dev, Stg, UAT, etc.) can be a painful and time-consuming activity, especially if you don’t have a dedicated DevOps team to carry out these tasks. Even though you have the DevOps team, you will have the dependency on them to get your application out of the door in time. Some organizations have dedicated DevOps teams, and some don’t, as developers you will have to manage your resources.

This is where IaC (Infrastructure as Code) can be handy (DevOps use the same 😉 ).  You can use ARM (Azure Resource Manager) Templates, Terraform and the new (not new!!) hot topic – Azure Bicep.

look!! it’s not rocket science 😉 – we can learn it quickly and get up to speed.

Let’s get started, I like to keep my pipelines separated – build vs release. I don’t like to mix and create resources as part of the build. I think it’s good practice to create resources when you are about to release your solution and not while you are building the solution.

below is my folder structure

  • mySolution
    • -src
      • —front-end-app
      • —back-apps (APIs)
    • -pipelines
      • —build
        • ——react-build.yml
        • ——function-build.yml
        • ——api-build.yml
      • —IaC
        • ——iac.yml
        • ——main.bicep

 

Our focus is in the IaC folder, you have the “iac.yml” to bundle up the .bicep files and publish them to Azure artifacts. Then on the release pipelines you can get the artifact for IaC, I hope it make scene.

iac.yml

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: Build
  jobs:
  - job: Build
    steps:

    - script: echo Hello, world!
      displayName: 'Run Build steps'

    - task: CopyFiles@2
      displayName: 'Include templates in the artifact'
      inputs:
        SourceFolder: 'pipelines/IaC'
        Contents: |
          main.bicep
        TargetFolder: '$(Build.ArtifactStagingDirectory)'

    - task: PublishBuildArtifacts@1
      displayName: 'Publish artifact'
      inputs:
        PathtoPublish: '$(Build.ArtifactStagingDirectory)'
        ArtifactName: 'drop'
        publishLocation: 'Container'

main.bicep

@description('env name as a input parameter')
param env string

@description('The application name.')
param applicationName string = 'anpr'

@description('The storage account location.')
param location string = resourceGroup().location


// *******************************************************************************
// * Create StorageAccounts
// *******************************************************************************
@description('The name of the storage account')
param storageAccountName string = 'sa${applicationName}${env}'

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
  }
}

as part of the release pipe line, we can pass in the “myresourcegroupname”, “env”, “location” which controls the groupname, environment name and location. its part of the release pipeline variables. (Ex : myresourcegroupname = ‘myawesomeapp’, env= ‘dev’, location =’australiasoutheast’)

Add a new step to your release – Azure CLI task, select the “inline script”

az group create --name 'rg-$(myresourcegroupname)-$(env)' --location $(location)
az deployment group create --resource-group 'rg-$(myresourcegroupname)-$(env)' --template-file main.bicep --parameters env=$(env)

if you want you can have 2 tasks to separate them.

Now we have the simple .bicep file working and creating the Azure resources, how do you create the dependencies between them ? for example let say you have a C# Web App that requires the App Service Plan, you can chain them pretty easily like below.

// *******************************************************************************
// * Create Web API Apps
// *******************************************************************************
@description('The name of the functionHostingPlanName')
var appServicePlanName = 'asp-${applicationName}-web-${env}'

@description('The name of the webSiteTradingOutletAPIName')
param webSiteTradingOutletAPIName string = 'app-${applicationName}-web-api-${env}'

resource appServicePlan 'Microsoft.Web/serverfarms@2020-06-01' = {
  name: appServicePlanName
  location: location
  properties: {
    reserved: true
  }
  sku: {
    name: 'F1' 
  }
  kind: 'linux'
}

resource appService 'Microsoft.Web/sites@2020-06-01' = {
  name: webSiteTradingOutletAPIName
  location: location
  properties: {
    serverFarmId: appServicePlan.id
    siteConfig: {
      linuxFxVersion: 'node|14-lts'
    }
  }
}

It’s almost like you are coding it. pretty easy, I would say.

Leave a comment