Convert ARM Template to Bicep and Deploy with Azure DevOps - Part 5 - Deploy
This is the fifth and the last part of the series where an ARM Template is converted to Bicep. In this article, we should continue on Phase 5 - Deploy.
Phased Migration
As a reference to Part 4, Microsoft suggests a migration in 5 phases, to convert the ARM Template into Bicep Template.
The comprehensive details provided by Microsoft Docs are available as a reference1.
Phase 5 - Deploy
In this phase, we are going to deploy the final Bicep to production.
Check the PlayGoKids repository for this article demo.
Some actions need to be verified to give us confidence that the migrated template will work:
- Prepare a rollback plan
Make sure that in the event of breaking existing components/services, you have a way of reverting the changes. Create an inventory of the components/services you use, backups if needed, to keep any downtime to a minimum if any issues arise from a deployment.
- Run the what-if operation against production
Similar to what was executed in Test, running the what-if operation in production is the safest approach to compare the state of components/services. For more details on the what-if operation, check Part 4.
- Deploy manually
Before creating pipelines, run the deployment from your local machine (with the what-if operation you achieve that), then create the Azure DevOps or GitHub Actions pipeline.
- Run smoke tests
Check whether what was deployed to components/services is functioning properly, by validating the solutions deployed.
Azure DevOps
Assuming that all checkpoints above were validated, let's automate the release of the converted Bicep explored during this series.
Looking at the folders/files on the repository:
deploy
folder contains the Bicep definitionweb
folder contains a dummy web appazure-pipelines.yml
is the main pipeline to be used on Azure DevOpstemplate-build.yml
is the template to build the dummy web app and copy filestemplate-deploy.yml
is the template to deploy the Bicep resources and the dummy web app
By opening web\Program.cs
we can verify the dummy web app that will be used in our Smoke Test:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
The magic lies in the YML files. The orchestrator is available on azure-pipelines.yml
:
trigger:
- main
variables:
buildConfiguration: 'Release'
stages:
- stage: 'Build'
displayName: 'Build the solution'
jobs:
- template: template-build.yml
parameters:
appName: 'web'
csproj: '**/web.csproj'
- stage: 'DeployDev'
displayName: 'Deploy to Dev'
condition: succeeded()
jobs:
- template: template-deploy.yml
parameters:
appName: 'web'
resourceLocation: 'australiaeast'
env: 'dev'
envFullName: 'Development'
variableGroupName: 'sample-dev'
- stage: 'DeployTest'
displayName: 'Deploy to Test'
condition: succeeded()
dependsOn:
- DeployDev
jobs:
- template: template-deploy.yml
parameters:
appName: 'web'
resourceLocation: 'australiaeast'
env: 'tst'
envFullName: 'Test'
variableGroupName: 'sample-tst'
- stage: 'DeployUat'
displayName: 'Deploy to Uat'
condition: succeeded()
dependsOn:
- DeployTest
jobs:
- template: template-deploy.yml
parameters:
appName: 'web'
resourceLocation: 'australiaeast'
env: 'uat'
envFullName: 'UAT'
variableGroupName: 'sample-uat'
- stage: 'DeployProd'
displayName: 'Deploy to Prod'
condition: succeeded()
dependsOn:
- DeployUat
jobs:
- template: template-deploy.yml
parameters:
appName: 'web'
resourceLocation: 'australiaeast'
env: 'prd'
envFullName: 'Production'
variableGroupName: 'sample-prd'
The pipeline basically orchestrates the build and deployment in different stages, using the condition suceeded()
and dependsOn
for the release of the different environments Dev, Test, Uat and Prod.
The template-build.yml
is used in the first stage:
parameters:
- name: appName
type: string
- name: csproj
type: string
jobs:
- job: 'Build'
displayName: 'Build job'
pool:
vmImage: 'ubuntu-latest'
steps:
# Copy the deployment files
- task: CopyFiles@2
displayName: Copy deployment files
inputs:
SourceFolder: '2022-07-25/deploy'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/drop/deploy'
# Use dotnet version
- task: UseDotNet@2
displayName: 'Use .NET 6 sdk'
inputs:
packageType: sdk
version: '6.x'
# Build and create the zip package
- task: DotNetCoreCLI@2
displayName: 'Building ${{ parameters.appName }}'
inputs:
command: 'publish'
publishWebProjects: false
projects: ${{ parameters.csproj }}
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)/drop/${{ parameters.appName }}'
modifyOutputPath: false
zipAfterPublish: true
# Publish artifacts
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)/drop'
ArtifactName: 'drop'
The steps include copying deployment files, using dotnet version .NET 6
, building the dummy web app and publishing the artifacts.
Once the build template is executed, then template-deploy.yml
is executed in the following stages:
parameters:
- name: variableGroupName
type: string
- name: appName
type: string
- name: resourceLocation
type: string
- name: env
type: string
values:
- dev
- tst
- uat
- prd
- name: envFullName
type: string
values:
- Development
- Test
- UAT
- Production
jobs:
- deployment: DeployTo${{ parameters.env }}
environment: '${{ parameters.envFullName }}'
pool:
vmImage: 'ubuntu-latest'
variables:
- group: ${{ parameters.variableGroupName }}
strategy:
runOnce:
deploy:
steps:
- checkout: self
#Create Infrastructure dependencies required
- task: AzureResourceManagerTemplateDeployment@3
displayName: 'Provision Azure Resources'
inputs:
azureResourceManagerConnection: '$(ServiceConnection)'
subscriptionId: '$(SubscriptionId)'
resourceGroupName: '$(ResourceGroupName)'
location: '${{parameters.resourceLocation}}'
csmFile: '$(Pipeline.Workspace)/drop/deploy/azuredeploy.bicep'
csmParametersFile: '$(Pipeline.Workspace)/drop/deploy/azuredeploy.parameters.${{parameters.env}}.json'
overrideParameters: '-resourceGroupName "$(ResourceGroupName)"'
#Deploy .Net code to Azure Web app
- task: AzureWebApp@1
displayName: 'Deploy ${{ parameters.appName }} to Azure Web app'
inputs:
azureSubscription: '$(Subscription)'
appType: 'webApp'
appName: 'sample-web-${{parameters.env}}'
package: '$(Pipeline.Workspace)/**/*.zip'
runtimeStack: 'DOTNETCORE|6.0'
It is worth mentioning that this pipeline uses Variable Groups in Azure DevOps. Make sure you have the following variable groups:
and variables:
The Bicep definition is deployed using the task AzureResourceManagerTemplateDeployment
, and the dummy web app on AzureWebApp
.
When executing the pipeline, the stages will be triggered sequentially.
And the solution should be running on the deployed environments:
This is the conclusion of this series. Hope you have learned to convert ARM Templates to Bicep, and getting to automate with Azure DevOps.
If you were helped, please star this repository.
Full Series
Phase 5 - Deploy
- References: Migrate to Bicep↩