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
- —build
- -src
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.