Swimburger

How to deploy ASP.NET Blazor WebAssembly to Azure Static Web Apps

Niels Swimberghe

Niels Swimberghe - - DotNet

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.

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 and GitHub Pages.

This walkthrough will show you how to deploy Blazor WASM to Azure Static Web Apps. 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 (only JavaScript & TypeScript Functions for now)
  • 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. Commit the project to a Git repository
  3. Create a new GitHub project and push the Git repository to GitHub
  4. Create a new Azure Static Web App
  5. Integrate GitHub with Azure Static Web App
  6. Commit GitHub Actions Workflow
  7. Run GitHub Actions Workflow

Prerequisites:

  • .NET Core 3.1
  • Git
  • GitHub account
  • Azure subscription

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:

mkdir AzureSwaBlazorWasmDemo
cd AzureSwaBlazorWasmDemo
dotnet new blazorwasm

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

dotnet run
# info: Microsoft.Hosting.Lifetime[0]
#       Now listening on: https://localhost:5001
# info: Microsoft.Hosting.Lifetime[0]
#       Now listening on: http://localhost:5000
# info: Microsoft.Hosting.Lifetime[0]
#       Application started. Press Ctrl+C to shut down.
# info: Microsoft.Hosting.Lifetime[0]
#       Hosting environment: Development
# info: Microsoft.Hosting.Lifetime[0]
#       Content root path: C:\Users\niels\source\repos\AzureSwaBlazorWasmDemo

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

dotnet publish -c Release
# Microsoft (R) Build Engine version 16.7.0+b89cb5fde for .NET
# Copyright (C) Microsoft Corporation. All rights reserved.
# 
#   Determining projects to restore...
#   All projects are up-to-date for restore.
#   AzureSwaBlazorWasmDemo -> C:\Users\niels\source\repos\AzureSwaBlazorWasmDemo\bin\Release\netstandard2.1\AzureSwaBlazorWasmDemo.dll
#   AzureSwaBlazorWasmDemo (Blazor output) -> C:\Users\niels\source\repos\AzureSwaBlazorWasmDemo\bin\Release\netstandard2.1\wwwroot
#   AzureSwaBlazorWasmDemo -> C:\Users\niels\source\repos\AzureSwaBlazorWasmDemo\bin\Release\netstandard2.1\publish\

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.

Push Blazor project 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:

# add the gitignore file tailored for dotnet applications, this will ignore bin/obj and many other non-source code files
dotnet new gitignore
# create the git repository
git init
# track all files that are not ignore by .gitignore
git add --all
# commit all changes to the repostiroy
git commit -m "Initial commit"

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:

git remote add origin https://github.com/Swimburger/AzureSwaBlazorWasmDemo.git
git push -u origin master

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.

Screenshot of Azure left navigation pane

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

Search for Azure Static Web App

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

Azure Static Web Apps marketplace page

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

Screenshot of Create Azure Static Web App Basics tab

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.

Screenshot of selecting GitHub repository in Azure Static Web App

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.

Click on the 'Next' button. The Build tab will ask for information about your source code which will be put into the GitHub Actions YAML-file. Fill them out as follows:

  • App location: /
    This is the folder of your app's source code. If you put your project in a subfolder, you'd put the subfolder name here. In this walkthrough, the project is not in a subfolder and '/' should be used.
  • Api location: [empty]
    This is the folder for you Azure Functions. Since only JavaScript/TypeScript Azure Functions are supported, and this walkthrough doesn't use that, you should empty the field.
  • App artifact location: wwwroot
    Specify the 'wwwroot' subfolder.
Screenshot of Azure Static Web App Build tab

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:

Azure Static Web Apps Deployment

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

  • URL: this is where you 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.
Screenshot of an Azure Static Web App resource

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:

name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
      - master
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
      - master

jobs:
  build_and_deploy_job:
    if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
    runs-on: ubuntu-latest
    name: Build and Deploy Job
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true
      - name: Build And Deploy
        id: builddeploy
        uses: Azure/static-web-apps-deploy@v0.0.1-preview
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_DELIGHTFUL_MUSHROOM_03650520F }}
          repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
          action: "upload"
          ###### Repository/Build Configurations - These values can be configured to match you app requirements. ######
          # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
          app_location: "/" # App source code path
          app_artifact_location: "wwwroot" # Built app content directory - optional
          ###### End of Repository/Build Configurations ######
  close_pull_request_job:
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    runs-on: ubuntu-latest
    name: Close Pull Request Job
    steps:
      - name: Close Pull Request
        id: closepullrequest
        uses: Azure/static-web-apps-deploy@v0.0.1-preview
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_DELIGHTFUL_MUSHROOM_03650520F }}
          action: "close"

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.

on:
  push:
    branches:
      - master
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
      - master

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.

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.

jobs:
  build_and_deploy_job:
    if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
    runs-on: ubuntu-latest
    name: Build and Deploy Job
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true
      - name: Build And Deploy
        id: builddeploy
        uses: Azure/static-web-apps-deploy@v0.0.1-preview
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_DELIGHTFUL_MUSHROOM_03650520F }}
          repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
          action: "upload"
          ###### Repository/Build Configurations - These values can be configured to match you app requirements. ######
          # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
          app_location: "/" # App source code path
          app_artifact_location: "wwwroot" # Built app content directory - optional
          ###### End of Repository/Build Configurations ######

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. 

  close_pull_request_job:
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    runs-on: ubuntu-latest
    name: Close Pull Request Job
    steps:
      - name: Close Pull Request
        id: closepullrequest
        uses: Azure/static-web-apps-deploy@v0.0.1-preview
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_DELIGHTFUL_MUSHROOM_03650520F }}
          action: "close"

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:

Screenshot of Azure Static Web App GitHub Action logs

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

Starting to build app with Oryx
Azure Static Web Apps utilizes Oryx to build both static applications and Azure Functions. You can find more details on Oryx here: https://github.com/microsoft/Oryx
---Oryx build logs---


Operation performed by Microsoft Oryx, https://github.com/Microsoft/Oryx
You can report issues at https://github.com/Microsoft/Oryx/issues

Oryx Version: 0.2.20200719.1, Commit: 80603ef82312ab8b6d950149f332638c5707de74, ReleaseTagName: 20200719.1

Build Operation ID: |heeYKwd2eqw=.6c88ce35_
Repository Commit : 4b9ce0842c5c2a5b4038a4c226e47a4f002f1030

Detecting platforms...
Detected following platforms:
  dotnet: 3.1.6
Version '3.1.6' of platform 'dotnet' is not installed. Generating script to install it...


Source directory     : /github/workspace
Destination directory: /bin/staticsites/ss-oryx/app


Downloading and extracting dotnet version 3.1.302 to /tmp/oryx/platforms/dotnet/sdks/3.1.302...
Downloaded in 39 sec(s).

Verifying checksum...
Extracting contents...
Done in 43 sec(s).
Using .NET Core SDK Version: 3.1.302

Welcome to .NET Core 3.1!
---------------------
SDK Version: 3.1.302

Telemetry
---------
The .NET Core tools collect usage data in order to help us improve your experience. The data is anonymous. It is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.

Read more about .NET Core CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry

----------------
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Find out what's new: https://aka.ms/dotnet-whats-new
Learn about the installed HTTPS developer cert: https://aka.ms/aspnet-core-https
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli-docs
Write your first app: https://aka.ms/first-net-core-app
--------------------------------------------------------------------------------------
  Determining projects to restore...
  Restored /github/workspace/AzureSwaBlazorWasmDemo.csproj (in 2.25 sec).

Publishing to directory /bin/staticsites/ss-oryx/app...

Microsoft (R) Build Engine version 16.6.0+5ff7b0c9e for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
  AzureSwaBlazorWasmDemo -> /github/workspace/bin/Release/netstandard2.1/AzureSwaBlazorWasmDemo.dll
  AzureSwaBlazorWasmDemo (Blazor output) -> /github/workspace/bin/Release/netstandard2.1/wwwroot
  AzureSwaBlazorWasmDemo -> /bin/staticsites/ss-oryx/app/

Removing existing manifest file
Creating a manifest file...
Manifest file created.

Done in 69 sec(s).


---End of Oryx build logs---
Finished building app with Oryx

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.

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:

git checkout -b ChangeTitle

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:

git add --all
git commit -m "change title"
git push --set-upstream origin ChangeTitle

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

GitHub Pull Request

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.

Screenshot of GitHub pull request where Azure comments the newly generate environment URL

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 the 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