Deploy Azure Bicep (ARM Templates) to Multiple Environments using Azure DevOps Pipelines

This post looks at deploying resources defined using Azure Bicep to multiple environments using Azure DevOps Pipeliens

Previously I’ve posted on developing Azure Bicep code using Visual Studio Code and on how to use an Azure DevOps Pipeline to deploy bicep code to Azure. In this post we’re going to go one step further and look at deploying resources defined using Azure Bicep to multiple environments. Our goal is to fully automate this process, so we’re going to leverage Azure DevOps Pipelines to define a build and release process.

Let’s summarise the goals:

  • Azure resources to be defined using Azure Bicep
  • Build and Release process to be defined in yaml in Azure DevOps Pipelines
  • Build process to be triggered whenever the bicep code changes
  • Build process should output the compiled ARM template as an artifact
  • Release process needs to support multiple environments
  • Each environment should use a different resource group
  • Release process should gate the release to particular environments based on a mix of branch and approval gates

You might think these goals are too hard to achieve using a single build and release process but the good news is that most of the heavy lifting is already done by Azure DevOps.

Here’s a quick summary of what we needs to setup:

  1. Azure Bicep file – this will define the resource(s) that we’re going to deploy to each environment
  2. Azure DevOps Environments – this will define the different environments we’re going to deploy to. At this stage this is limited to defining the approvals and checks that will be done before code/resources are deployed to an environment
  3. Azure DevOps Variable Groups – this will define variables that are used across all build and deployment steps, as well as variables that are specific to individual environments
  4. Azure DevOps Pipeline – this will define the actual build and release process for the bicep code.

Defining Resources Using Azure Bicep

Let’s start by defining a very simple Azure Bicep (services.bicep) that defines a storage account.

/* 
******************  
    Literals
****************** 
*/

// Resource type prefixes
var storageAccountPrefix = 'st'

/* 
******************  
    Parameters
****************** 
*/

// ** General
param applicationName string 
param location string 
param env string 

/* 
******************  
    Resources
****************** 
*/

var appNameEnvLocationSuffix  = '${applicationName}${env}'

// Storage Account

var storageAccountName  = '${storageAccountPrefix}${appNameEnvLocationSuffix}' 

resource attachmentStorage 'Microsoft.Storage/[email protected]' = {
    name: storageAccountName
    location: location
    sku: {
        name: 'Standard_LRS'
        tier: 'Standard'
    }
    kind: 'StorageV2'
    properties: {
        accessTier: 'Hot'
        minimumTlsVersion: 'TLS1_2'
        supportsHttpsTrafficOnly: true
        allowBlobPublicAccess: true
        networkAcls: {
            bypass: 'AzureServices'
            defaultAction: 'Allow'
            ipRules: []
        }
    }
}

A couple of things to note about this bicep file

  • It defines a constant, storageAccountPrefix, that is used to define the full name of the generated resource. This prefix comes from the list of recommended resource-type prefixes that the Microsoft documentation lists
  • There are three parameters defined: applicationName, location and env. Location is used to define the region that the resource will be created (alternatively you could use resourceGroup().location to use the same location as the resource group where this resource is being created). The applicationName and env parameters are combined with the storageAccountPrefix to define a unique name for the resource being created.
  • All three parameters are required, meaning that they will need to be supplied when deploying the generated ARM template. To simplify the process you may want to define default values for applicationName and location. It’s important that you supply the env value during deployment to ensure the resources in each resource group are unique across your subscription.

Setting Up Multiple Environments

Next we’ll define the different environments in Azure DevOps. We’ll keep things relatively simple and define three environments:

  1. Development – from the develop branch
  2. Testing – from the release branch
  3. Production – from the release branch but requiring approval

To create these environments, click on the Environments node under Pipelines from the navigation tree on the left side of the Azure DevOps portal for the project. Click the New environment button and enter a Name and Description for the each environment.

Branch Control

For each environment we need to limit deployments so that only code from the appropriate branch can be deployed to the environment. To setup a branch control check for an environment you first need to open the environment by selecting it from the list of Environments. From the dropdown menu in the top right corner, select Approvals and checks. Next, click the Add check (+) button. Select Branch control and then click the Next button.

In the Branch control dialog we need to supply the name of the branch that we want to limit deployments from. For example for the Development environment we would restrict the Allowed branches to the develop branch (specified here as refs/heads/develop).

Note here that we’ve also included refs/tags/* in the list of Allowed branches. This is required so that we can use the bicep template from the Pipeline Templates repository. I’d love to know if there’s a way to restrict this check to only a specific repository, since adding refs/tags to the Allowed branches will mean that any tagged branch in my repository will also be approved. Let me know in the comments if you know of a workaround for this.

The Testing and Production environments are both going to be restricted to the Release branch. However, we’re also going to enforce a check on the branch to Verify branch protection.

What this means is that the Release branch will be checked to ensure branch policies are in place. For example the Release branch requires at least one reviewer and a linked work item.

Approvals

The only difference between the Testing and Production environments is that deployment to the Production environment requires a manual approval. This makes sense in most cases considering you may need to co-ordinate this with a marketing announcement or a notification to existing customers.

Setting up an approval gate starts similar to adding branch control. Open the environment and go to Approvals and checks. Click the Add check button and select Approvals. In the Approvals dialog, enter the list of users that can approve the deployment, adjust any of the other properties and then click Create.

We typically set a very short approval timeout period to avoid the scenario where multiple deployments get queued up behind each other. If we create another deployment before the first has been approve, we prefer to only have the latter deployment pushed to the Production environment. Of course, this is something your team should discuss and agree on what strategy you want to employ.

Environment Variable Groups

Since we’re going to be deploying the same set of resources to each of the environments, we’re going to need a way to specify build and release variables, some that are common across all stages of the pipeline, and some that are specific to each environment. To make it easy to manage the variables used in the pipeline we’re going to use variable groups which can be defined within the Library tab within Azure DevOps Pipelines.

Common Build Variables

We’ll create a variable group called Common Build Variables and we’ll add two properties, ResourceGroupLocation and AzureSubscriptionConnectionName.

As you can probably deduce, the ResourceGroupLocation will be the region where all the resources will be created. For simplicity this will assumed to be the same across all environments. The AzureSubscriptionConnectionName we’ll come back to but needless to say, it’s the same connection for all stages in the pipeline.

Environment Specific Variables

For each environment we’re going to define a variable group that is named Common.[enviornment]. These variable groups will contain variables that are specific to each environment. In this case, we’re going to define the EnvironmentName, the EnvironmentCode and ResourceGroupName

We’ll show these variable in action shortly but it’s important to remember that any variable that needs to vary, based on which Enviornment it’s being deployed to, should be defined in the appropriate variable group.

Build and Release Process

The build and release process is going to be defined as a yaml pipeline in Azure DevOps.

Service Connections

Before we can jump in to write some yaml, we need to setup a couple of service connections.

  • Pipeline-Templates – this is a github service connection so that the build process can download the appropriate pipeline template to assist with the compilation of the bicep file.
  • Azure-Subscription – this is a link to the Azure subscription where the resource groups will be created and subsequently the resources created.

I’m not going to step through process of creating these connections, since you can simply follow the prompts provided in the Azure portal. However, it’s important to take note of the name of the service connections.

Build and Deploy Process

Here’s the full build and release pipeline, which we’ll step through in more detail below.

trigger:
  branches:
    include:
    - '*'  # must quote since "*" is a YAML reserved character; we want a string
  paths:
    include:
    - azure/services.bicep
    - pipelines/azure-services.yml
  
resources:
  repositories:
    - repository: pipelinetemplates
      type: github
      name: builttoroam/pipeline_templates
      ref: refs/tags/v0.7.0
      endpoint: Pipeline-Templates
  
pool:
  vmImage: 'windows-latest'
  
variables:
  - group: 'Common Build Variables'
  - name: application_name
    value: inspect
  - name: bicep_filepath
    value: 'azure/services.bicep'
  - name: arm_template_filepath
    value: '$(Pipeline.Workspace)/BicepArtifacts/services.json'
  
stages:
- stage: Compile_Bicep
  pool:
    vmImage: 'windows-latest'

  jobs:
  - job: Bicep
    steps:
      - template: azure/steps/bicep/[email protected]
        parameters:
          name: Bicep
          bicep_file_path: '$(System.DefaultWorkingDirectory)/$(bicep_filepath)'
          arm_path_variable: ArmFilePath

      - task: [email protected]2
        displayName: 'Copying bicep file to artifacts folder'
        inputs:
          contents: '$(Bicep.ArmFilePath)'
          targetFolder: '$(build.artifactStagingDirectory)'
          flattenFolders: true
          overWrite: true

      - task: [email protected]1
        displayName: 'Publish artifacts'
        inputs:
          pathtoPublish: '$(build.artifactStagingDirectory)' 
          artifactName: 'BicepArtifacts' 
          publishLocation: Container


- template:  templates/deploy-arm.yml
  parameters:
    stage_name: 'Deploy_Development'
    depends_on: 'Compile_Bicep'
    deploy_environment: 'Development'

- template:  templates/deploy-arm.yml
  parameters:
    stage_name: 'Deploy_Testing'
    depends_on: 'Deploy_Development'
    deploy_environment: 'Testing'
  
- template:  templates/deploy-arm.yml
  parameters:
    stage_name: 'Deploy_Production'
    depends_on: 'Deploy_Testing'
    deploy_environment: 'Production'

Trigger – we’ve setup this process to kick off whenever code is committed to the develop environment.

Resources – this process leverages the templates from PipelineTemplates, which requires the definition of a resource pointing to the appropriate tagged release in the pipeline templates github repository.

Stages – there’s one build stage, followed by three release (aka deploy) stages. The steps for the build stage leverage the bicep template from pipeline templates, in order to generate the ARM template. The ARM template is then executed in each of the environments and deployed to the appropriate resource group.

The three deploy stages all used the same template, that we’ll see in a minute, coupled with an environment specific parameter value. For example the first stage passes in a parameter ‘Development’.

Deploy Stage Template

As I mentioned, the three deployment stages are identical, except for some parameter values that are defined for each environment. In this case the template referenced for each stage includes creating the resource group and then planting it.

parameters:
- name: stage_name
  type: string
  default: 'Deploy_ARM_Resources'

- name: depends_on
  type: string
  default: ''

  # deploy_environment - Environment code
- name: deploy_environment
  type: string

stages:
- stage: ${{ parameters.stage_name }}
  dependsOn: ${{ parameters.depends_on }}
  variables:
  - group: 'Common.${{ parameters.deploy_environment }}'
  
  pool:
    vmImage: 'windows-latest'

  jobs:
  - deployment: 'Deploy${{ parameters.stage_name }}'
    displayName: 'Deploy ARM Resources to ${{ parameters.deploy_environment }}' 
    environment: ${{ parameters.deploy_environment }}
    strategy:
      runOnce:
        deploy:
          steps:
          - task: [email protected]2
            name: ${{ parameters.stage_name }}
            inputs:
              targetType: 'inline'
              workingDirectory: $(Pipeline.Workspace)
              script: |
                  $envParam = '${{ parameters.deploy_environment }}'
                  Write-Host "Deployment deploy environment parameter: $envParam"

                  $envName = '$(EnvironmentName)'
                  Write-Host "Deployment environment name variable: $envName"

          - task: [email protected]2
            displayName: 'Create resource group - $(ResourceGroupName)'
            inputs:
              azureSubscription: $(AzureSubscriptionConnectionName)
              scriptType: ps
              scriptLocation: inlineScript
              inlineScript: |
                Write-Host "Creating RG: $(ResourceGroupName)"
                az group create -n $(ResourceGroupName) -l $(ResourceGroupLocation)
                Write-Host "Created RG: $(ResourceGroupName)"

          - task: [email protected]2
            displayName: 'Deploying ARM template to $(ResourceGroupName)'
            inputs:
              azureSubscription: $(AzureSubscriptionConnectionName)
              action: 'Create Or Update Resource Group' 
              resourceGroupName: $(ResourceGroupName)
              location: $(ResourceGroupLocation) 
              templateLocation: 'Linked artifact'
              csmFile: '$(arm_template_filepath)' # Required when  TemplateLocation == Linked Artifact        
              overrideParameters: '-location $(ResourceGroupLocation) -env $(EnvironmentCode) -applicationName $(application_name)'

Throughout this pipeline, there are various variables referenced. The important thing to note is that the variables need to exist for each environment, or are environment independent.

The Common Build Variables group was imported in the build and release process yaml file. The ResourceGroupLocation is the only variable from this group that’s used within this template.

The variable group for each environment is imported within the deploy stage template. The environment name, which is passed in as the deploy_environment parameter, is concatenated with “Common.” in order to import the correct variable group. The imported variables can be referenced the same way as other locally defined variables.

The deployment pipeline template has three steps: The first simply outputs variables so that it’s clear what environment is being built. Then there’s a task for creating the resource group, and then lastly a task for deploying the ARM template.

Running the Pipeline End to End

In the post we’ve defined three different environment and configured Azure DevOps to have different variable groups for each of the environments. Here you can see an execution of the pipeline with the build stage, followed by three deployment stages.

In this case note the Testing deployment failed because the resources were being deployed from the wrong branch (develop instead of release). Unfortunately because this one stage failed, the entire pipeline was marked as failed.

Pipeline Templates: Using Project Bicep with Azure DevOps Pipeline to Build and Deploy an ARM Templates

This post covers how you can use a .bicep file in your Azure DevOps pipeline to deploy resources to Azure via an ARM template

It’s great to see that Microsoft listens to the pain that developers and devops professionals go through in working with Azure. Specifically that ARM templates, whilst very powerful, are insanely verbose and cumbersome to work with. Hence Project Bicep which is at it’s core a DSL for generating ARM templates. In this post I’m not going to go into much details on how to work with .bicep files but rather on how you can use a .bicep file in your Azure DevOps pipeline to deploy resources to Azure.

If you are interested in learning more about Project Bicep and the .bicep file format, here are some posts that provide some introductory material:

In order to deploy a .bicep file to Azure, you first need to use the bicep command line to generate the corresponding ARM template. You can then deploy the ARM template to Azure. Rather than having to add multiple steps to your build pipeline, wouldn’t it be nice to have a single step that you can use that simply takes a .bicep file, some parameters and deploys it to Azure. Enter the bicep-run template that is part of the 0.7.0 release of Pipeline Templates.

If you haven’t worked with any of the templates from the Pipeline Templates project, here’s the quick getting started:

Add the following to the top of your pipeline – this defines an external repository called all_templates that can be referenced in your pipeline.

resources:
  repositories:
    - repository: all_templates
      type: github
      name: builttoroam/pipeline_templates
      ref: refs/tags/v0.7.0
      endpoint: github_connection

Next, we’re going to use the bicep-run template to deploy our .bicep file to Azure.

      - template: ../../steps/bicep/bicep-run.yml
        parameters:
          name: BicepRun
          az_service_connection: $(service_connection)
          az_resource_group_name: $(resource_group_name)
          az_resource_location: $(resource_location)
          bicep_file_path: '$(bicep_filepath)'
          arm_parameters: '-parameter1name $(parameter1value) -parameter2name $(parameter2value)'

The bicep-run wraps the following:

  • Downloads and caches the Project Bicep command line. It currently references the v0.1.37 release but you can override this by specifying the bicep_download_url – make sure you provide the url to the windows executable, not the setup file.
  • Runs the Bicep command line to covert the specified .bicep file (bicep_file_path parameter) into the corresponding ARM template
  • Uses the Azure Resource Group Deployment Task to deploy the ARM template into Azure. The arm_parameters are forwarded to the overrideParameters parameter on the Azure Resource Group Deployment task.

Would love feedback on anyone that takes this template for a spin – what features would you like to see added? what limitations do you currently see for Project Bicep and the ability to run using this task?

Note: The bicep-run template is designed to run on a windows image.

Xamarin DevOps Snippets (aka Pipeline Templates)

Louis Matos has put together Xamarin Month with the topic of Code Snippets – Check out Louis’ blog for the full month of snippet. In this post I’m going to cover some code snippets that use Pipeline Templates in order to setup a Azure DevOps pipeline for your Xamarin application. Azure Pipeline Templates are a way … Continue reading “Xamarin DevOps Snippets (aka Pipeline Templates)”

Louis Matos has put together Xamarin Month with the topic of Code Snippets – Check out Louis’ blog for the full month of snippet. In this post I’m going to cover some code snippets that use Pipeline Templates in order to setup a Azure DevOps pipeline for your Xamarin application.

Azure Pipeline Templates are a way to define reusable components of YAML that can be shared across different pipelines and in fact across different repositories. Noting how difficult it was to setup even a basic pipeline to build and deploy a Xamarin application, the Pipeline Templates project was born to publish reusable templates that could easily be dropped into any pipeline.

Getting Started

In order to make use of templates, the first thing you need to do is to reference the pipeline_templates GitHub repository as a resource in you YAML build pipeline.

resources:
  repositories:
    - repository: builttoroam_templates
      type: github
      name: builttoroam/pipeline_templates
      ref: refs/tags/v0.6.1
      endpoint: github_connection

It’s worth noting that the ref attribute is referencing the v0.6.1 release by specifying the tag. Alternatively you could reference any branch simply by changing the ref to a value similar to “refs/heads/nickr/bugfix” where the branch is nickr/bugfix. I would recommend referencing one of the tagged releases for stability of your build pipeline – from time to time we do make breaking changes, so if you’re referencing a branch, your build might start failing.

Build Templates

Android

To build the Android application, use the build-xamarin-android template – simply provide the necessary parameters.

stages:
- template:  azure/stages/[email protected]_templates
  parameters:
    # Stage name and whether it's enabled
    stage_name: 'Build_Android'
    build_android_enabled: true
    # Version information
    full_version_number: '1.0.$(Build.BuildId)'
    # Signing information
    secure_file_keystore_filename: '$(android_keystore_filename)'
    keystore_alias: '$(android_keystore_alias)'
    keystore_password: '$(android_keystore_password)'
    # Solution to build
    solution_filename: 'src/SnippetXamarinApp.sln'
    solution_build_configuration: 'Release'
    # Output information
    artifact_folder: 'artifacts'
    application_package: 'android.apk'

iOS

To build the iOS application, use the build-xamarin-ios template

- template:  azure/stages/[email protected]_templates
  parameters:
    # Stage name and whether it's enabled
    stage_name: 'Build_iOS' 
    build_ios_enabled: true
    # Version information
    full_version_number: '1.0.$(Build.BuildId)'
    # Solution to build
    solution_filename: 'src/SnippetXamarinApp.sln'
    solution_build_configuration: 'Release'
    # Signing information
    ios_plist_filename: 'src/SnippetXamarinApp/SnippetXamarinApp.iOS/Info.plist'
    ios_cert_password: '$(ios_signing_certificate_password)'
    ios_cert_securefiles_filename: '$(ios_signing_certificate_securefiles_filename)'
    ios_provisioning_profile_securefiles_filename: '$(ios_provisioning_profile_securefiles_filename)'
    # Output information
    artifact_folder: 'artifacts'
    application_package: 'ios.ipa'

Windows

To build the Windows application, use the build-xamarin-windows template. Technically this template should work with any UWP application.

- template:  azure/stages/[email protected]_templates
  parameters:
    # Stage name and whether it's enabled
    stage_name: 'Build_Windows'
    build_windows_enabled: true
    # Version information
    full_version_number: '1.0.$(Build.BuildId)'
    # Signing information
    windows_cert_securefiles_filename: '$(windows_signing_certificate_securefiles_filename)'
    windows_cert_password: '$(windows_signing_certificate_password)'
    # Solution to build
    solution_filename: 'src/SnippetXamarinApp.sln'
    solution_build_configuration: 'Release'
    # Output information
    artifact_folder: 'artifacts'
    application_package: 'windows.msix'

Deploy

Of course, once you’re done building your applications, you probably want to deploy the applications for testing. For this you can use the deploy-appcenter template. Note that you need to add a stage for each platform you want to deploy but you can use the same template as it knows how to deploy iOS, Android and Windows applications to AppCenter.

- template:  azure/stages/[email protected]_templates
  parameters:
    # Stage name and dependencies
    stage_name: 'Deploy_Android'
    depends_on: 'Build_Android'
    deploy_appcenter_enabled: true
    environment_name: 'AppCenter'
    # Build artifacts
    artifact_folder: 'artifacts'
    application_package: 'android.apk'
    # Signing information (for Android repack to APK)
    secure_file_keystore_filename: '$(android_keystore_filename)'
    keystore_alias: '$(android_keystore_alias)'
    keystore_password: '$(android_keystore_password)'
    # Deployment to AppCenter
    appcenter_service_connection: $(appcenter_service_connection)
    appcenter_organisation: $(appcenter_organisation)
    appcenter_applicationid: $(appcenter_android_appid)

For a more detailed walk through of using the pipeline templates for building cross platform xamarin applications, please check out this post that covers the process end to end.

Call to Action: Contributions

The Pipeline Templates project is an open source project and I would love to get feedback and contributions from the community in order to provide more templates (not just for mobile).

Specifically, if anyone has built out the tasks necessary to deploy applications to each of the three stores, it would be great to create a template similar to the AppCenter template that targets the actual stores.

If anyone is familiar with both GitHub Actions and Azure Pipelines, it would be great to get someone to give me a hand to convert the existing templates for Azure DevOps across to GitHub Actions.

Pipeline Templates: Templates for Downloading and Caching

Earlier this year I published a pre-release version of an Azure DevOps extension that wrapped the ApkTool for extracting and re-packing Android APK files. Having spent more time working with Azure Pipelines, I realised that most of what I was doing with the extension could actually be done with one or more templates. Today I … Continue reading “Pipeline Templates: Templates for Downloading and Caching”

Earlier this year I published a pre-release version of an Azure DevOps extension that wrapped the ApkTool for extracting and re-packing Android APK files. Having spent more time working with Azure Pipelines, I realised that most of what I was doing with the extension could actually be done with one or more templates. Today I started the process of creating the templates… and unfortunately got side tracked in over engineering a couple of the templates. The upshot is that the Pipeline Templates library now has the following new templates (there’s no release yet but you can reference them off the master branch if you want to use them today).

  • Download-Url-To-File – As the name suggests this template can be used to download a url (source_url) to a specified file (target_path). The target_path parameter can be an absolute or relative path, and you can specify the overwrite_existing parameter if you want to force the url to be downloaded even if the file already exists.
  • Check-File-Exists – Again, the name says what’s in the box – this template checks to see if the specified file (path_to_check) exists. The response can either be an output variable (specified using the file_exists_variable parameter) or an error can be thrown (which will break the current build)
  • Download-And-Cache – Rather than having to download files every time a build is run, the files can be cached and reloaded for each build. This template makes this easy.
  • ApkTool-Install – This downloads and caches the two files required to run the apktool (apktool.jar and apktool.bat) on Windows.

Once I get to the point where I’m able to replicate the functionality of the Apktool extension I’ll publish these updates as a new release. In meantime, if there are any other templates you’d like to see, feel free to create an issue on the Pipeline Templates GitHub repo, or drop me a comment

Pipeline Templates: Runtime Parameters in Azure DevOps Pipelines

It appears that the Runtime Parameters of Azure DevOps Pipelines has rolled out to most organisations. As such I thought it important that the Pipeline Templates are updated to use strongly typed boolean parameters. For example in the following YAML taken from the Windows Build template has a parameter, build_windows_enabled, which is typed as a … Continue reading “Pipeline Templates: Runtime Parameters in Azure DevOps Pipelines”

It appears that the Runtime Parameters of Azure DevOps Pipelines has rolled out to most organisations. As such I thought it important that the Pipeline Templates are updated to use strongly typed boolean parameters. For example in the following YAML taken from the Windows Build template has a parameter, build_windows_enabled, which is typed as a boolean.

parameters:
# stage_name - (Optional) The name of the stage, so that it can be referenced elsewhere (eg for dependsOn property). 
# Defaults to 'Build_Windows'
- name: stage_name
  type: string
  default: 'Build_Windows'
# build_windows_enabled - (Optional) Whether this stages should be executed. Note that setting this to false won't completely
# cancel the stage, it will merely skip most of the stages. The stage will appear to complete successfully, so
# any stages that depend on this stage will attempt to execute
- name: build_windows_enabled
  type: boolean
  default: true

Previously, the parameters were passed in using variables defined in the main pipeline. The issue with this is that all variables are strings, so there’s no checking that a valid value has been passed in. For example:

Now we have the option of defining runtime parameters. These are parameters that are defined in the main pipeline yml file (ie not in a template). In the following example there are three runtime parameters defined; all boolean; all with a default value of true.

When we manually run this pipeline we’re now presented with a user interface that allows the user to adjust these runtime parameters, before hitting Run.

Another useful feature in the Run dialog is the Resources section

The Resources section lists referenced templates.

Clicking through on a template allows the user to adjust what version of the template to use. This is particularly useful if you are testing a new version of a template because you can Run a build referencing a different version, without making (and committing) a change to the actual pipeline.

Pipeline Templates v0.6.0

Here’s a summary of what’s in release v0.6.0:

Breaking Changes:

  • build-xamarin-[iOS/android/windows].yml and deploy-appcenter.yml – XXX_enabled parameter (eg windows_enabled or deploy_enabled) are now strongly typed as boolean parameters. This means you need to supply either a literal value (eg true or false) or a parameter. Use runtime parameters instead of pipeline variables to ensure values are strongly typed as boolean.

Other Changes:

  • none

Pipeline Template: Applying a Launch Icon Badge to Identify Environments and Versions of your App

A question was raised this week by Sturla as to how to incorporate the Launch Icon Badge extension into the build process when making use of the templates from Pipeline Templates (Damien covers how to use this extension in a Azure DevOps pipeline in his post on the topic). By the way, a big thank … Continue reading “Pipeline Template: Applying a Launch Icon Badge to Identify Environments and Versions of your App”

A question was raised this week by Sturla as to how to incorporate the Launch Icon Badge extension into the build process when making use of the templates from Pipeline Templates (Damien covers how to use this extension in a Azure DevOps pipeline in his post on the topic). By the way, a big thank you to Sturla for testing each release of the templates and providing invaluable feedback along the way.

The purpose of the Launch Icon Badge extension is to make it really easy to add a banner to your application icon (or any other image) to indicate that your application is prerelease. As you’ll see from the yaml in the example below there are a lot of attributes you can control, meaning that you can use this for whatever you want to signify. For example you may want to signify what environment the app is targeting by changing the background colour on the banner.

To get started with the Launch Icon Badge extension you need to make sure you install the extension to your Azure DevOps Organisation. This is important, otherwise any attempt to use the LaunchIconBadge task will fail.

If you’re using one of the build templates from Pipeline Templates you simply need to extend it by adding the LaunchIconBadge task to the preBuild task list. This is shown at the end of the following YAML snippet.

stages:
- template:  azure/mobile/[email protected]_templates
  parameters:
    # Stage name and whether it's enabled
    stage_name: 'Build_Android'
    build_android_enabled: ${{variables.android_enabled}}
    # Version information
    full_version_number: '$(version_prefix).$(Build.BuildId)'
    # Signing information
    secure_file_keystore_filename: '$(android_keystore_filename)'
    keystore_alias: '$(android_keystore_alias)'
    keystore_password: '$(android_keystore_password)'
    # Solution to build
    solution_filename: $(solution_file)
    solution_build_configuration: $(solution_build_config)
    # Output information
    artifact_folder: $(artifact_android_folder)
    application_package: $(android_application_package)
    preBuild:
      - task: [email protected]1
        inputs:
          sourceFolder: '$(Build.SourcesDirectory)/src/Apps/DotNet/Uno/InspectorUno/InspectorUno/InspectorUno.Droid' # Optional. Default is: $(Build.SourcesDirectory)
          contents: '**/*.png' # Optional. Default is:  '**/*.png'
          bannerVersionNamePosition: 'bottomRight' # Options: topLeft, topRight, bottomRight, bottomLeft. Default is: 'bottomRight'
          bannerVersionNameText: 'Prerelease'  # Optional. Default is: ''
          bannerVersionNameColor: '#C5000D' # Optional. Default is: '#C5000D'
          bannerVersionNameTextColor: '#FFFFFF' # Optional. Default is: '#FFFFFF'
          bannerVersionNumberPosition: 'top' # Optional. top, bottom, none. Default is: 'none'
          bannerVersionNumberText: '$(version_prefix).$(Build.BuildId)' # Optional. Default is: ''
          bannerVersionNumberColor: '#34424F' # Optional. Default is: '#34424F'
          bannerVersionNumberTextColor: '#FFFFFF' # Optional. Default is: '#FFFFFF' 

Important Note: In v0.5.2 of the Pipeline Templates you can no longer pass in a variable to the build_android_enabled parameter using the $(variablename) syntax. As shown here you need to use the ${{variables.variablename}} syntax to ensure it’s resolved as part of inflating the templates.

When you’re attempting to get the LaunchIconBadge task to work for you, make sure that your sourceFolder and contents are configured to locate the app icons for your application. This took me a couple of iterations as I got the path wrong the first couple of times and it resulted in no images being found – check the build log for full information on what the task is doing.

Once you have every configured, your application should be built with the appropriate banner across the application icon. I configured this for the Android, iOS and Windows build for my application and this is what I now see in App Center.

Pipeline Templates v0.5.2

Here’s a summary of what’s in release v0.5.2:

Breaking Changes:

  • build-xamarin-[iOS/android/windows].yml and deploy-appcenter.yml – XXX_enabled parameter (eg windows_enabled or deploy_enabled) no longer supports passing a variable in using $(variablename) syntax. Make sure you use ${{variables.variablename}} syntax. This also means that only variables defined in the YAML file are supported – NOT variables defined in variable groups or in the UI for the pipeline. Going forward it’s recommended to use runtime parameters for getting user input when invoking a pipeline.

Other Changes:

  • none

Pipeline Templates: How to use a file for release notes?

When deploying a release to AppCenter you can specify release notes that get presented to the user when they go to download a new release. This week a question was asked as to how to specify release notes from a file when submitting a new app version to AppCenter. If you looked at the complete … Continue reading “Pipeline Templates: How to use a file for release notes?”

When deploying a release to AppCenter you can specify release notes that get presented to the user when they go to download a new release. This week a question was asked as to how to specify release notes from a file when submitting a new app version to AppCenter.

If you looked at the complete example I provided for building and deploying a Uno app to Android, iOS and Windows, you might be wondering how you can provide release notes at all since we didn’t specify any release notes when we used the AppCenter deploy template. However, if you take a look at the parameters list you can see that there is an appcenter_release_notes parameter which has a default value set. Use this parameter if you want to specify the release notes inline when invoking the template.

In the v0.5.1 release we added two new parameters that allow you to specify a file for release notes to be taken from: appcenter_release_notes_option and appcenter_release_notes_file. To specify a file for release notes, you first need to set the appcenter_release_notes_option parameter to file. Then you need to use the appcenter_release_notes_file parameter to specify the file that you want to use.

In theory this sounds easy: you add a release notes file to your repository and then simply provide a relative path to the file in the appcenter_release_notes_file parameter. This you will find does not work!!! The AppCenter deploy template does not know anything about your source code repository, since it’s designed to deploy the artifacts from a prior build stage.

Ok, so then the question is, how do we make the release notes file available to the AppCenter template? Well we pretty much answered that in the previous paragraph – you need to deploy it as an artifact from the build process.

Assuming you’re using one of the build templates from https://pipelinetemplates.com, you can easily do this by adding additional steps as part of the prePublish extension point. Here’s an example:

- template:  azure/mobile/[email protected]_templates
  parameters:
    # Stage name and whether it's enabled
    stage_name: 'Build_iOS' 
    build_ios_enabled: $(ios_enabled)
    # Version information
    full_version_number: '$(version_prefix).$(Build.BuildId)'
    # Solution to build
    solution_filename: $(solution_file)
    solution_build_configuration: $(solution_build_config)
    # Signing information
    ios_plist_filename: 'src/Apps/DotNet/Uno/InspectorUno/InspectorUno/InspectorUno.iOS/Info.plist'
    ios_cert_password: '$(ios_signing_certificate_password)'
    ios_cert_securefiles_filename: '$(ios_signing_certificate_securefiles_filename)'
    ios_provisioning_profile_securefiles_filename: '$(ios_provisioning_profile_securefiles_filename)'
    # Output information
    artifact_folder: $(artifact_ios_folder)
    application_package: $(ios_application_package)
    prePublish:
      - task: [email protected]2
        displayName: 'Copying release notes'
        inputs:
          contents: 'src/Apps/DotNet/releasenotes.txt'
          targetFolder: '$(build.artifactStagingDirectory)/$(artifact_ios_folder)'
          flattenFolders: true
          overWrite: true

- template:  azure/mobile/[email protected]_templates
  parameters:
    # Stage name and dependencies
    stage_name: 'Deploy_iOS'
    depends_on: 'Build_iOS'
    deploy_appcenter_enabled: $(ios_enabled)
    environment_name: $(appcenter_environment)
    # Build artifacts
    artifact_folder: $(artifact_ios_folder)
    application_package: $(ios_application_package)
    # Deployment to AppCenter
    appcenter_service_connection: $(appcenter_service_connection)
    appcenter_organisation: $(appcenter_organisation)
    appcenter_applicationid: $(appcenter_ios_appid)
    appcenter_distribution_group_ids: '5174f212-ea8c-4df1-b159-391200d7af5f'
    appcenter_release_notes_option: file
    appcenter_release_notes_file: 'releasenotes.txt'

Note that the CopyFiles task copies the release notes into the artifact_folder sub-folder of the staging directory. This is important because the AppCenter deploy template will look in the sub-folder for the release notes file specified using the appcenter_release_notes_file parameter.

Pipeline Templates: Stage Dependency Fix and Improved Docs

We just released v0.5.1 of the Pipeline Templates – templates for creating build pipelines for Azure DevOps. This was a minor increment but fixed an issue that emerged due to a change in the validation of pipelines by Azure DevOps. Before we get into the details of what is included in v0.5.1, the other big … Continue reading “Pipeline Templates: Stage Dependency Fix and Improved Docs”

We just released v0.5.1 of the Pipeline Templates – templates for creating build pipelines for Azure DevOps. This was a minor increment but fixed an issue that emerged due to a change in the validation of pipelines by Azure DevOps.

Before we get into the details of what is included in v0.5.1, the other big news is that Pipeline Templates has a new documentation site.

http://pipelinetemplates.com

This site uses GitHub Pages and as such is generated from the markdown files in the project itself. Currently the site has a getting started guide, followed by a summary of each of the templates. Hopefully over time this can grow as we include more examples and extend the list of templates.

Pipeline Templates v0.5.1

Here’s a summary of what’s in this release:

Breaking Changes:

  • None

Other Changes:

  • Fix: build-xamarin-[iOS/android/windows].yml and deploy-appcenter.yml – changed depends_on parameter from stageList to string to fix validation issue introduced by Azure DevOps
  • Added NuGetInstallAndRestore.yml based on a template suggest by Damien Aicheh in his post on how to Add nightly builds to your Xamarin applications using Azure DevOps
  • Updated deploy-appcenter.yml to include additional parameters for controlling how apps are deployed via AppCenter. For example you can now specify release notes as a file, and you can flag whether a release is a mandatory update or not.
  • Added Markdown files that document each of the templates. Go to https://pipelinetemplates.com to see the rendered output.

Pipeline Templates: Complete Azure Pipelines Example for a Uno Project for iOS, Android and Windows

My last post was a bit of a long one as it covered a bunch of steps for setting up the bits and pieces required for signing an application for different platforms. In this post I just wanted to provide a complete example that shows a single multi-stage (6 in total) Azure Pipelines pipeline for … Continue reading “Pipeline Templates: Complete Azure Pipelines Example for a Uno Project for iOS, Android and Windows”

My last post was a bit of a long one as it covered a bunch of steps for setting up the bits and pieces required for signing an application for different platforms. In this post I just wanted to provide a complete example that shows a single multi-stage (6 in total) Azure Pipelines pipeline for building a Uno application for iOS, Android and Windows (UWP) and releasing them to App Center.

Secure Files

In my last post I showed how to create and populate Secure Files in Azure Pipelines. Any certificate or provisioning profile you need to use in your pipeline should be added to the Secure Files section of the Library in Azure Pipeline. My list of Secure Files looks like this:

Here we can see that we have the signing certificates for iOS and Windows, and the keystore for Android. Then we have two iOS provisioning profiles, one for my XF application and the other for my Uno application.

Variable Groups

I’ve extracted most of the variables I use in my pipeline into one of two variable groups:

The Common Build Variables are those variables that can be reused across multiple projects.

The Inspector Uno Build Variables are those variables that are specific to this project. For example it includes the AppCenter ids for the iOS, Android and Windows applications. It also includes the iOS provisioning profile which is specifically tied to this application.

Pipeline

Here’s the entire pipeline:

resources:
  repositories:
    - repository: builttoroam_templates
      type: github
      name: builttoroam/pipeline_templates
      ref: refs/tags/v0.5.0
      endpoint: github_connection
  
variables:
  - group: 'Common Build Variables'
  - group: 'Inspector Uno Build Variables'
  - name: ios_enabled
    value: 'true'
  - name: windows_enabled
    value: 'true'
  - name: android_enabled
    value: 'true'
 
stages:
- template:  azure/mobile/[email protected]_templates
  parameters:
    # Stage name and whether it's enabled
    stage_name: 'Build_Android'
    build_android_enabled: $(android_enabled)
    # Version information
    full_version_number: '$(version_prefix).$(Build.BuildId)'
    # Signing information
    secure_file_keystore_filename: '$(android_keystore_filename)'
    keystore_alias: '$(android_keystore_alias)'
    keystore_password: '$(android_keystore_password)'
    # Solution to build
    solution_filename: $(solution_file)
    solution_build_configuration: $(solution_build_config)
    # Output information
    artifact_folder: $(artifact_android_folder)
    application_package: $(android_application_package)

- template:  azure/mobile/[email protected]_templates
  parameters:
    # Stage name and dependencies
    stage_name: 'Deploy_Android'
    depends_on: 'Build_Android'
    deploy_appcenter_enabled: $(android_enabled)
    environment_name: $(appcenter_environment)
    # Build artifacts
    artifact_folder: $(artifact_android_folder)
    application_package: $(android_application_package)
    # Signing information (for Android repack to APK)
    secure_file_keystore_filename: '$(android_keystore_filename)'
    keystore_alias: '$(android_keystore_alias)'
    keystore_password: '$(android_keystore_password)'
    # Deployment to AppCenter
    appcenter_service_connection: $(appcenter_service_connection)
    appcenter_organisation: $(appcenter_organisation)
    appcenter_applicationid: $(appcenter_android_appid)


- template:  azure/mobile/[email protected]_templates
  parameters:
    # Stage name and whether it's enabled
    stage_name: 'Build_Windows'
    build_windows_enabled: $(windows_enabled)
    # Version information
    full_version_number: '$(version_prefix).$(Build.BuildId)'
    # Signing information
    windows_cert_securefiles_filename: '$(windows_signing_certificate_securefiles_filename)'
    windows_cert_password: '$(windows_signing_certificate_password)'
    # Solution to build
    solution_filename: $(solution_file)
    solution_build_configuration: $(solution_build_config)
    # Output information
    artifact_folder: $(artifact_windows_folder)
    application_package: $(windows_application_package)

- template:  azure/mobile/[email protected]_templates
  parameters:
    # Stage name and dependencies
    stage_name: 'Deploy_Windows'
    depends_on: 'Build_Windows'
    deploy_appcenter_enabled: $(windows_enabled)
    environment_name: $(appcenter_environment)
    # Build artifacts
    artifact_folder: $(artifact_windows_folder)
    application_package: $(windows_application_package)
    # Deployment to AppCenter
    appcenter_service_connection: $(appcenter_service_connection)
    appcenter_organisation: $(appcenter_organisation)
    appcenter_applicationid: $(appcenter_windows_appid)

- template:  azure/mobile/[email protected]_templates
  parameters:
    # Stage name and whether it's enabled
    stage_name: 'Build_iOS' 
    build_ios_enabled: $(ios_enabled)
    # Version information
    full_version_number: '$(version_prefix).$(Build.BuildId)'
    # Solution to build
    solution_filename: $(solution_file)
    solution_build_configuration: $(solution_build_config)
    # Signing information
    ios_plist_filename: 'src/Apps/DotNet/Uno/InspectorUno/InspectorUno/InspectorUno.iOS/Info.plist'
    ios_cert_password: '$(ios_signing_certificate_password)'
    ios_cert_securefiles_filename: '$(ios_signing_certificate_securefiles_filename)'
    ios_provisioning_profile_securefiles_filename: '$(ios_provisioning_profile_securefiles_filename)'
    # Output information
    artifact_folder: $(artifact_ios_folder)
    application_package: $(ios_application_package)

- template:  azure/mobile/[email protected]_templates
  parameters:
    # Stage name and dependencies
    stage_name: 'Deploy_iOS'
    depends_on: 'Build_iOS'
    deploy_appcenter_enabled: $(ios_enabled)
    environment_name: $(appcenter_environment)
    # Build artifacts
    artifact_folder: $(artifact_ios_folder)
    application_package: $(ios_application_package)
    # Deployment to AppCenter
    appcenter_service_connection: $(appcenter_service_connection)
    appcenter_organisation: $(appcenter_organisation)
    appcenter_applicationid: $(appcenter_ios_appid)


Pipeline Templates v0.5.0

Most of the v0.5.0 release has been tidying things up, reducing the number of required parameters by making the template smarter and increasing consistency across the templates

Breaking Changes:

  • build-xamarin-android.yml – changed build_platform to solution_target_platform parameter
  • build-xamarin-windows.yml – changed build_platform to solution_target_platform parameter
  • build-xamarin-windows.yml – changed windows_appxupload_name parameter to windows_upload_name to reflect support for msix

Other Changes:

  • build-xamarin-[iOS/android/windows].yml – added depends_on parameter so that the stages can be ordered
  • build-xamarin-[iOS/android/windows].yml – artifact_name, artifact_folder and application_package are no longer required and have default values
  • build-xamarin-android.yml – supports building either aab or apk based on the application_package parameter
  • build-xamarin-windows.yml – windows_upload_name parameter isn’t required as it has a default value based on the bundle name
  • deploy-appcenter.yml – added conditions to all steps so that pipeline doesn’t break if build stage doesn’t generate any output
  • All template – documentation added to parameters and steps