3. Building a software solution – Automate building the environment

Welcome back, I am continuing my journey of building a complete software solution. To remind you, I am building a sports league management solution. Granted, I’m not running any sports leagues, so, any domain knowledge from that side would be excellent, if anyone has any input. What I do have experience with is being a parent and fighting with websites just to figure out when games and practices are. Or, the chain emails that they forwarded around that have 10 different dates and times in them. There has to be a better and easier way to at least coordinate practices and games. Perhaps in the future, I’ll get into stat tracking and analytics.

After the last post, I realized that I wanted to properly version and have an effective CI/CD solution for the infrastructure. So, I deleted all of the After the last post, I realized that I wanted to have correct version control and an effective CI/CD solution for the infrastructure. So, I deleted all of the resources created. We are going to automate and build the environment using Azure DevOps and Azure Resource Manager (ARM) Templates. To start, we go to Azure DevOps and create a new build pipeline.

DevOps will now ask you where the code is being stored, I choose GitHub and select the appropriate repository:

When it asks you to “Configure your pipeline choose “Starter pipeline”. You will then be presented a YAML file that loos like this:

# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml

- master

  vmImage: 'ubuntu-latest'

- script: echo Hello, world!
  displayName: 'Run a one-line script'

- script: |
    echo Add other tasks to build, test, and deploy your project.
    echo See https://aka.ms/yaml
  displayName: 'Run a multi-line script'

The first thing I want to do is change that trigger statement. I am currently working on building my dev environment. I want this to trigger on commits to my develop branch. I am using the GitFlow branching scheme if you want to use that. What this means is that I’ll have a develop branch that I use for my dev environment. I’ll also create a build pipeline that triggers on my master branch for PROD. To push to master, I’ll do a GitFlow release. I follow this branching mechanism because it also helps me by allowing me to use GitVersion for semantic versioning automatically. I am going to be building that into my pipeline as well. I make use of GitVersion in my pipeline, so it is necessary to add it from the Visual Studio Marketplace.

You can edit the YAML file in the browser, or after you save it, pull it locally from your repository to edit it. If you choose to edit it in the browser, there is an assistant that can help you add items to your YAML from the menu of items available:

Ok, so, after changing the trigger to the develop branch, I then go and delete everything after steps: and then look for the “GitVersion Task” in the Tasks menu. The task asks you for some information, and there is a lot that can be done with GitVersion, just not for this project. I mostly just want it to get the semantic version details from my git repo. So I accept the defaults and add it to my project. My pipeline now looks like this:

- master

  vmImage: 'ubuntu-latest'

- task: GitVersion@5
    runtime: 'core'

Now that I have GitVersion installed, I want my build version the same as the version of the code in the repository. I found a fantastic article where the author accomplished this. Check it out at https://www.neovolve.com/2019/05/03/gitversion-and-github-with-azure-devops-build-yaml/. They added a script task like this:

- script: echo %Action%%BuildVersion%
  displayName: 'Set build version'
    Action: '##vso[build.updatebuildnumber]'
    BuildVersion: $(GitVersion.SemVer)

In their article, they were doing NuGet versioning, and I knew from the Visual Studio Marketplace page that by adding the GitVersion to my project, I would have access to the $(GitVersion.SemVer) variable. So I changed their example, which sets the build version to my semantic versioning provided by GitFlow.

And then select New variable:

As the screen shows, DevOps can store encrypted secrets so that you should NEVER put a username and password, or certificate, or anything that should be secure in your source code. Later, when we add a SQL Server and DB to our project variables will store secrets. Now I’ll add my variables:

I add the following variables:

SubscriptionIDThis is the GUID of my subscription

Now, the only way I could figure out how to create a resource group was to use a Powershell script. Since I want this to run inside of my Azure subscription I chose the Azure CLI item from the task list:

I then chose my subscription from the drop-down, I chose Powershell Core (because everything I do on my Mac is using Powershell Core). Next, you can save the script in your repo and select it, but I chose to put my script inline. Here is the entire step in the pipeline with the script:

- task: AzureCLI@2
  displayName: 'Create Resource Group if one does not exist'
    azureSubscription: 'My Subscription Name'
    scriptType: 'pscore'
    scriptLocation: 'inlineScript'
    inlineScript: |
      az login --identity
      $GroupExists = $(az group exists --name $env:ResourceGroupName --subscription $env:SubscriptionID)
      if ($GroupExists -eq "False") {
          az group create --location $env:ResourceGroupLocation --name $env:ResourceGroupName --subscription $env:SubscriptionID

Ok, let’s break this down. The Powershell script I wrote is in the inlineScript: section. The first line is az login --identity There are several options here. You can pass in a username and password, or a certificate. If you want to see all of your options look here. I chose identity because I have authenticated my DevOps environment to my Azure Tenant. It has the necessary roles to create my items. Next, I get the result of the Azure CLI command az group exists --name $env:ResourceGroupName --subscription $env:SubscriptionID This command will return the boolean string value of either “True” or “False.” You also see that I have added some of the environment variables to the Azure CLI command. The exists command will tell me if the Resource Group exists. If it does not, I then run the az group create to create the resource group. If you want to know more about what Azure CLI options for resource groups, check out the documentation.

So, now, our build pipeline creates the resource group if it is needed. I am not going to create all of the resources in this pipeline completely. I plan to put the actual template deployment in the release pipeline. The reason for this is that if I want to roll back my environment, I want to have that available as an option. So, instead, I am going to publish all of the items in my repo as a pipeline artifact using the Publish Pipeline Artifacts step:

I named my artifact Templates-Develop with the step looking like this:

- task: PublishPipelineArtifact@1
    targetPath: '$(Pipeline.Workspace)'
    artifact: 'Templates-Develop'
    publishLocation: 'pipeline'

The Publish Pipeline Artifacts takes my entire repository and saves the files for the release pipeline. In my next post, I’ll go over how to build an ARM Template and release it.