Photo by Roman Synkevych on Unsplash

Part 2: Using GitHub Action to deploy Blazor WebAssembly App to Azure Storage Static Website

David Lee

--

In part 1, we looked at creating a bicep file which defines the resources to create our Azure environment. In part 2, we will create a GitHub Action to automate the creation of our Azure environments and deploy our Blazor WASM App to the Storage Account.

Here, we plan to have two environments — one for Production and one for Development. This means we will need to have a way to create configurations with the use of GitHub Action secrets. There are two ways we can leverage GitHub secrets depending on which plan you are on with GitHub. With the Enterprise version (or if you have a public repository), we can create two actual environments and name them dev and prod. Here, you can click on Environments and start by creating an environment, then the individual secrets per environment.

A screenshot showing how we can create 2 environments for prod and dev

If not i.e. you are on a free plan and your project is private, then you have GitHub secrets. You can configure your secrets by going to the Settings of GitHub. As a technique, you may consider prefixing your secrets with environment naming convention. From a management perspective, we can see it is easier with the Environments options, and hey, it is a choice.

Screenshot of secrets management if you are NOT using environment secrets

For the purpose of our project, let’s assume we are going with an open source project which gives us the option to use the Environment option.

We will start by adding an Azure Credentials that can be used to login to our Azure Subscription. The format of the Azure Credentials is shown below which requires a Client Id, Client Secret, Subscription Id and Tenant Id. Note that we should have already created a service principal in our Azure AD tenant with the appropriate role assigned on a resource group. In my case, I assigned the Contributor role to my service principal on my resource group because I needed to create a whole bunch of resources.

A screenshot showing how to configure azure credentials.

Next, we will need to configure a managed user which is used as part of the deployment script. If we recall in part 1, we already mentioned passing in the variable managedUserId which in our case, we define as MANAGED_USER_ID. The rest of the variables would be a suffix for our resource names and a resource group name defined as SUFFIX and RESOURCE_GROUP respectively. We will have the following configurations as seen in the screenshot below.

Screenshot of environment secrets used in GitHub Action

We will work on our GitHub action next which requires a yaml file. This file, by convention, resides in the .github/workflows/ folder. We will name our file app.yaml. The first line we will write is to define that our GitHub Action will run on push which the code is simply “on: push”. This means that regardless of the branch, whether main or dev, our GitHub action will execute.

As part of the GitHub action, we want to load the appropriate environment variables based on the branch. In our case, we want to load the prod environment variables if this is main branch and use dev environment for other branch. Let’s review the code.

GitHub allows us to create the notion of a job which will execute our pipeline as an independent workflow. Here, we have a notion of a “setup” job. Here, we have selected the job to run on a windows-latest agent. Of course, our job will happily run on a linux based agent (on ubuntu and even macOS) as well. For more information, see: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners.

The actual code runs in the context of a step. In our step, we can see that I have a if-else condition to assert the branch. Then using the github syntax, I am setting an variable that can be used throughout the job by virtue of an output in the syntax required. You can see that at the end of the special syntax is a key value pair of name=value. For example, build_env=prod. From a job level, in order to share this variable, I am using the output node of the job to output build_env as a variable where this is set by the step output. Now, we are ready to load the environment variable as we have detected our “environment”.

In this next build_deploy job, we have a “needs” node which tells the job that it has a dependency on the setup job. Once the dependency job has completed, it is ready to run. Here, we can see in the “environment” node that we are taking the needs.setup.outputs to reference the setup job for the build_env variable which in turn, defines our environment to load. Pretty cool!

The next few steps are to build the blazor app. Notice that we will need to make sure .NET 5 is loaded before we can run any dotnet commands which will not happen yet, but will, in just a few more steps.

Next, we will need to login via Azure CLI. Notice that we have also enable AzPsSession so we can execute Azure PowerShell. For more information on the options, you can reference: https://github.com/marketplace/actions/azure-login. We are using the Azure credentials we have configured earlier in our environment setup.

The next step is the build-and-deploy step. Here, we are using the az deployment group Azure CLI command. Please pay special attention here to the fact that we are directly invoking the bicep file via the template-file parameter. This is a neat feature because we don’t have to convert our bicep file to an ARM Template which in my opinion, is pretty awesome! Finally, the output of the deployment is converted to an object and then using the same technique, saved as a step variable.

The next step is to install azcopy which is required for us to copy files to our storage account. Our last step is to execute a PowerShell script to build and deploy our Blazor app to our storage account.

If this is your first time learning about azcopy, it is definitely a great tool to have in your arsenal as it is the most effective tool in copying and even syncing files from your local to your storage account.

Note that on line 5, we are creating a brand new blazor app. In real-life, you would probably be checking in your Blazor app code and hence, you don’t really need this. Line 6 is about publishing your Blazor app code to a output folder. On line 10, we are generating a SAS key which can be used to copy the files to our storage account using azcopy. A few key points here.

  1. We are copying files from the wwwroot folder. This is all that is needed.
  2. We are copying files into the $web folder which is where your static content will be served from.
  3. We are using --delete-destination flag which ensures we have a clean folder each time we do a deployment.

In real life, you may have a few more things to configure such as the appsettings.json file which can contain Azure AD B2B or B2C setup or or URLs to your APIs that you are going to be calling from your Blazor client app. These step can be pushed to your wwwroot folder before the azcopy.

The final result would look like the following below where we have a complete environment in a little less than 8 minutes. Pretty awesome!

Screenshot of the GitHub Action execution

The source code of what we discussed here is on https://github.com/seekdavidlee/az-blazor-wasm-storage-static-website. This is an actual working copy and I encourage you to fork it for your own repo to get a jump start on deploying your Blazor app onto your Azure environment.

I do hope you have enjoyed this reading and learned something new today.

--

--