Swimburger

How to deploy Blazor WASM & Azure Functions to Azure Static Web Apps using GitHub

Niels Swimberghe

Niels Swimberghe - - .NET

Follow me on Twitter, buy me a coffee

How to deploy ASP.NET Blazor WebAssembly to Azure Static Web Apps. Blazor logo pointing to a GitHub logo pointing to an Azure logo.

Update 09/28/20: Azure Static Web Apps was officially announced at September's MSIgnite. This tutorial has been updated to include support for Azure Functions.

With ASP.NET Blazor WebAssembly (WASM) you can create .NET web applications that run completely inside of the browser sandbox. The publish output of a Blazor WASM project are static files. Now that you can run .NET web applications without server side code, you can deploy these applications to various static site hosts, such as Azure Static Web Apps.

Check out how to deploy Blazor WASM to these alternative static site hosts:

This walkthrough will show you how to deploy Blazor WASM to Azure Static Web Apps communicating with the built-in Azure Functions integration. Azure Static Web Apps (in preview) is a new free service that let's you host static sites deployed from GitHub. In addition to static site hosting, the service provides additional features:

  • Generate continuous integration and deployment using GitHub Actions to build and deploy your static web application
  • Automatically create hosting environments for different Git branches and pull requests
  • Azure Functions integration
  • Custom domain names

This walkthrough will show you how to deploy to Azure Static Web Apps following these high level steps:

  1. Create Blazor WebAssembly project
  2. Create .NET Core Azure Functions project
  3. Commit the projects to a Git repository
  4. Create a new GitHub project and push the Git repository to GitHub
  5. Create a new Azure Static Web App
  6. Integrate GitHub with Azure Static Web App
  7. Commit GitHub Actions Workflow
  8. Run GitHub Actions Workflow

Prerequisites:

You can find the source code for this how-to on GitHub.

Create ASP.NET Core Blazor WebAssembly project #

Run the following commands to create a new Blazor WASM project:

To give your application a try, run dotnet run and browse to the URL in the output (by default https://localhost:5001):

Optional: You can use the dotnet publish command to publish the project and verify the output:

In the publish directory, you will find a web.config file and a wwwroot folder. The config file helps you host your application in IIS, but you don't need this file for static site hosts. Everything you need will be inside of the wwwroot folder. The wwwroot folder contains the index.html, CSS, JS, and DLL files necessary to run the Blazor application.

Create .NET Core Azure Functions project #

You'll need to install the Azure Functions Core Tools before you can create and run a .NET Core Azure Functions project. Follow the installation instructions from the Microsoft Documentation. Make sure to restart your console/terminal to make the new func commands available.

Use the following commands to create a new Azure Functions project and create an Azure Function called WeatherForecast:

Azure Static Web Apps only supports 'HTTP Trigger' functions. Azure Functions triggered by other events like a CRON schedule, queues, etc. are not supported.

Update the Azure Function file WeatherForecast.cs with the code below:

By default, the Blazor WASM project will make an ajax request to the file located at Client/wwwroot/sample-data/weather.json. The Azure Function mimics the format of that file so you can replace the ajax call to the sample file with an ajax call to the Azure Function.

For now, test out your new Azure Function by running func start and browse to the URL found in the console output. The URL should be http://localhost:7071/api/WeatherForecast. The result should look like an unformatted version of this:

In the next section, you'll integrate the Blazor Client with the Azure Functions API.

Integrating Blazor with Azure Functions #

You need to update the Blazor Client to make an ajax call to your Azure Function instead of the sample data at Client/wwwroot/sample-data/weather.json. Update the Blazor component at Client\Pages\FetchData.razor, by replacing the commented out line below, with the line not commented out:

To run the Blazor client and Azure Function, you'll need to run the following two commands in separate consoles/terminals:

  • At the root folder run dotnet run --project Client
  • At the Api folder run func start --cors "https://localhost:5001,http://localhost:5000"

The --cors argument is required to allow communication between localhost:5001 or localhost:5000 and the Azure Function at localhost:7071.

Browse to /fetchdata in your Blazor application and notice how the weather forecasts changes randomly with every refresh.

This works well locally, but the hardcoded URL http://localhost:7071/api/WeatherForecast won't work when anywhere but locally. To make this work both locally and elsewhere, update the following files:

Update Client\Program.cs:

Add Client\wwwroot\appsettings.Development.json:

Update OnInitializedAsync at Client\Pages\FetchData.razor:

Here's what changed:

  • in Startup.cs an HttpClient is configured with a BaseAddress. This HttpClient is injected into FetchData using dependency injection.
    • Locally, the base address will use the BaseAddress from appsettings.Development.json which is http://localhost:7071/.
    • In Azure Static Web Apps, the root address of the Blazor application will be used as the BaseAddress for the HttpClient because the appsettings file won't be deployed.
      This will result in ajax calls being made to https://yourapp.azurestaticapps.net/api/weatherforecast.
      Azure Static Web Apps will intercept any call made to /api and forward it to the Azure Functions project.
  • appsettings.Development.json will not be deployed to Azure Static Web Apps.
  • FetchData.razor will receive an HttpClient from the Dependency Injection container and will make ajax calls to the right address whether it is hosted locally or on Azure Static Web Apps.

Push projects to GitHub #

Azure Static Web Apps requires your application source code to be inside of a GitHub repository.
First, you need to create a local Git repository and commit your source code to the repository using these commands:

Create a new GitHub repository (public or private). Copy the commands from the empty GitHub repository page under the title: "push an existing repository from the command line". Here's what it should looks like but with a different URL:

These commands will push the repository up to GitHub.

Create Azure Static Web Application #

Navigate to the Azure Portal (portal.azure.com) and click on "Create a resource" in the navigation menu on the left.

Search for 'Static Web App' and click on the 'Static Web App (preview)' card.

On the Static Web App details page, click the 'Create'-button.

Fill out the fields on the create static web app blade:

Click on the 'Sign in with GitHub' button. This will show a GitHub dialog prompting you to give permissions to Azure. These permissions are required so that Azure can commit the GitHub actions YAML-file for CI/CD and so GitHub can deploy to Azure.

Now that you've connected GitHub to your Azure Static Web App, select your GitHub repository by filling out the 'Organization', 'Repository', and 'Branch' field.

Once you have selected your Git repository, the 'Build Details' section will show up.
In the 'Build Presets' dropdown, select Blazor. This will prefill the next fields:

  • App location: Client
    This is the folder of your Blazor WASM project.
  • Api location: Api
    This is the folder for your Azure Functions.
  • App artifact location: wwwroot
    Specify the 'wwwroot' subfolder. When GitHub Actions builds your Blazor project, it will grab the files from the published 'wwwroot' folder.

These fields will be used to create a GitHub Actions Workflow YAML-file.

Next, click the 'Review + create' button and then click the 'Create' button.
Wait for the deployment to complete and then click the 'Go to resource' button:

On the overview page of your Azure Static Web App you will find a lot of links:

  • URL: this is where your web app is hosted
  • Source: this is a link to the selected branch in your GitHub repository
  • Deployment history: this is a link to GitHub Action in your GitHub repository
  • Workflow file: this is a link to the GitHub Actions YAML file in your GitHub repository.

Click on the URL to view your new static web app. At this point, your Blazor application should've been deployed. If not wait a little and refresh the page.

GitHub Actions YAML file explained #

You might be wondering, how did Azure/GitHub know how to build and deploy my application? Let's take a look at the GitHub Actions YAML file that Azure generated and committed into our GitHub repository:

The first section 'on' instructs GitHub Actions when to run the workflow. The workflow will run every time a commit is pushed to the master-branch and every time a pull request is made against the master branch.

The if statements makes it so build_and_deploy_job will always run except when a pull request is closed.
runs-on specifies the job to run on an Ubuntu VM. The first step under steps of the job will checkout the latest code from the master branch.

The next step will use Azure's 'static-web-apps-deploy' Action. This Action will build, deploy your code, and also create environments for pull requests. This GitHub Action is hiding all of the 'magic', but more on that later.
For more information on this action and how to configure it, you can read the Microsoft documentation: "GitHub Actions workflows for Azure Static Web Apps Preview".

In the with section, you will find the parameters you configured when creating the Azure resource. The other parameters are secrets created by Azure when you created the Azure Static Web App. These secrets are used to authenticate and securely perform actions in Azure and in GitHub.

The next job will only run if a pull request is closed as specified by the if property. This will use the same Azure GitHub Action but the action parameter under the with section is set to 'close'. 
This step will delete the environment in Azure that was generated automatically when a pull request was opened against the master-branch. 

Now, you still might be wondering, how did this 'static-web-apps-deploy' GitHub Action actually know how to build the Blazor application without any hints of it being a .NET Core application?
The GitHub repository of the action readme notes it uses another open-source project by Microsoft called 'Oryx'. 

"Oryx is a build system which automatically compiles source code repos into runnable artifacts. It is used to build web apps for Azure App Service and other platforms."
- Oryx GitHub readme.md

When you poke around the project and its source code, you learn that this project tries to build any source code without giving it build instructions or other hints. Oryx will attempt to detect the type of project by scanning through your source code and based on that run the build commands.

You can see this in action by looking at the logs of your GitHub Action Workflow runs. Navigate to details of your GitHub Actions Workflows and inspect the logs:

Under Build And Deploy you will find the familiar output of the .NET Core publish command:

Oryx installs the .NET Core 3.1 SDK and then runs a dotnet publish.

If for some reason the default behavior of Oryx is undesirable, you can override the build command by adding an extra parameter app_build_command to the Static Site action or build your application in an earlier step.

Fix 404's with SPA routing rule #

You will be greeted with a 404 page if you navigate to the counter or fetch data page and then refresh the page.

You can resolve this by telling Azure Static Web Apps to reroute all requests to the index.html file if no file at the requested path was found. To do this create a routes.json file at Client\wwwroot\routes.json with the following contents:

This routes.json file has to be uploaded to the root of the Azure Static Web App and that's why it has to be in the wwwroot-folder. To learn more about the capabilities of the routes.json file, check out Microsoft's documentation

Stage, commit, and push the changes to the GitHub repository and wait for your application to be redeployed:

Browsing directly to /counter or /fetchdata or refreshing these pages should no longer result in 404 errors.

Bonus: Create a pull request #

When you create a pull request to your master branch, a new environment will automatically be created which hosts the version of your application in that pull request.

Create a new branch by running this command:

Change the title tag text inside of wwwroot/index.html to "Hello".
Run the following script to commit the change and push the branch to GitHub:

Navigate to your GitHub repository and click on the "Pull Requests tab". Click on the "New pull request" button.

Select the 'ChangeTitle' branch as the second dropdown, click 'Create pull request' and click 'Create pull request' again.

The GitHub Action Workflow will automatically run for your pull request. Once finished, Azure will leave a comment on your pull request with the URL to the new environment where your code change is hosted.
If you close the pull request the workflow will run again, but this time the workflow will delete your environment.

Summary #

Azure Static Web Apps will automatically generate a CI/CD pipeline for you using GitHub Actions. The Azure Static Web App action knows how to build your application automagically by using Oryx. Oryx scans your code to detect what type of code it is dealing with and attempts to run the right build commands for you. You can provide the build command yourself or customize the GitHub Actions YAML to build your project before passing the result to the Static Web Apps action.

Related Posts

Related Posts