Azure Static Web Apps minimum viable build
06 Feb 2023
Table of Contents
- GitHub repo with example
- Install PowerShell and modules
- Connect PowerShell to Azure
- Create a Static Web App resource
- Validate the SWA live site has not been built
- Building an SWA live site from code stored in GitHub
- SWA seems to have opinions about YAML file locations
- Building an SWA live site from code stored in Azure DevOps Repos
- Future research
Below is work I did to figure out “what’s really going on” with Azure Static Web Apps and CI/CD pipelines.
The most surprising things I learned are:
- There’s nothing special about the interlink between your Git repository-hosting system and Azure SWA. When Azure SWA “sets up” build and deployment pipelines for you as you configure a new project, it’s just copying a
.yml
file into your Git-tracked codebase (your repo) and putting a deployment token into the secrets manager hosted with the copy of your Git repository stored in the cloud provider you chose.- Your dev folks absolutely can configure repo-to-SWA flows themselves, by hand, in their Git-hosting-plus-pipelines tool (e.g. GitHub, Azure DevOps) instead of making it part of the SWA-resource-provisioning process.
This is kind of cool because if they were planning to run unit/integration/regression/end-to-end automated tests against built/deployed websites, they were probably going to hand-write some GitHub Action / Azure DevOps Pipeline YAML files by hand anyway, so … nifty, the “hey SWA, please build my website” code can just get all rolled up into that.
I remember even reading that the URL built by a GitHub Action (remember, SWA offers preview live URLs for every branch or commit or something like that) is accessible as a variable in the YAML, so people who know what they’re doing in GitHub Actions should be able to pretty easily write user interface tests that simply point to “whatever URL was just built” when throwing in Selenium / Cypress / etc. tests. - It’s just a convenience that the GUI-based SWA-resource-provisioning process asks you about the repo you’ll be deploying from.
- Your dev folks absolutely can configure repo-to-SWA flows themselves, by hand, in their Git-hosting-plus-pipelines tool (e.g. GitHub, Azure DevOps) instead of making it part of the SWA-resource-provisioning process.
- By default (I haven’t played with locking this down), Azure SWA doesn’t really care where build instructions come from, as long as the computer emitting the instructions has a copy of the SWA resource’s deployment key.
- Security: Guard that key!
- Accidents: If you put the same key into 2 repos (and associated CI/CD pipeline) and then someone commits to one and then someone else commits to the other, your live site hosted by Azure SWA will display content from whichever repo+pipeline’s commit got to it last.
GitHub repo with example
See also the GitHub example “The smallest GitHub website you can make that will build an Azure Static Web App” that you can copy into your own repo and try yourself if you have an Azure environment where you can create a Static Web App, such as A Cloud Guru.
Install PowerShell and modules
Once I had PowerShell 7 installed onto my computer, I had to do a 1-time install of the Azure modules:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force
Connect PowerShell to Azure
Each time my A Cloud Guru sandbox dies and I provision another one, I have to redo this PowerShell command:
Connect-AzAccount
To double-check I’m really logged into A Cloud Guru, I run one of these 3 commands:
Get-AzContext # The best verification.
Get-AzSubscription # If I need a Tenant ID and Subscription ID, here they are.
Get-AzTenant # I forgot why I ever looked at this one but anyway it will show me some stuff too.
Create a Static Web App resource
Before I create anything, the following PowerShell command returns a null value, which is to be expected:
Get-AzStaticwebApp
Once I’m sure I’m connected to the correct Azure, I can fetch the Resource Group that A Cloud Guru put into the sandbox it spun up for me with Az Powershell and use that info to create an Azure resource called my-first-swa
of type Static Web App.
$my_resource_group_name = (Get-AzResourceGroup).ResourceGroupName
$my_swa_name = 'my-first-swa'
$my_static_web_app = New-AzStaticWebApp -ResourceGroupName $my_resource_group_name -Name $my_swa_name -Location 'Central US' -SkuName 'Standard' -RepositoryUrl $null
It takes about a minute to run.
I should probably be setting RepositoryUrl
and a bunch of other settings, but at the moment I’m too lazy to get all set up properly with Terraform and whatnot, and New-AzStaticWebApp
seems to have an unsolved bug in it with respect to RepositoryUrl
, and this is as good as I’ve gotten so far.
Validate the SWA live site has not been built
If I run this PowerShell script once it’s done, I can see the URL that my website exists at _(it’ll be something along the lines of adjective-noun-hex.integer.azurestaticapps.net
):
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).defaultHostname
Since I didn’t set -RepositoryUrl
, -Branch
, OutputLocation
, AppLocation
, or ApiLocation
as part of New-AzStaticWebApp
, the following commands will all return null values at first:
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).repositoryUrl
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).branch
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).outputLocation
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).appLocation
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).apiLocation
And this one returns the word “None”:
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).provider
Building an SWA live site from code stored in GitHub
Interestingly, though, dealing with this bug made me realize that when I’m looking at https://portal.azure.com/#@azurelabs.linuxacademy.com/resource/subscriptions/my_subscription_id/resourceGroups/my_resource_group_name/providers/Microsoft.Web/staticSites/my-first-swa/staticsite
, the Manage deployment token tab up at the top lets me grab a value that I can punch into a GitHub repository’s secrets – and my Static Web App couldn’t care less what repository in the whole wide world I’m accessing its API from, as long as I’m using this secret.
Once I let something like a GitHub Action blindly reach out into the internet with a repository secret stored at https://github.com/my-username/my-repo-name/settings/secrets/actions
named AZURE_STATIC_WEB_APPS_API_TOKEN_YAY_I_DID_IT
and a /.github/workflows/its-my-cicd.yml
file along these lines (which took my single-HTML-file “app” just over a minute)…
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
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@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_YAY_I_DID_IT }}
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 your app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
app_location: "/src/web" # App source code path
api_location: "/src/api" # Api source code path - optional
output_location: "/output" # 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@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_YAY_I_DID_IT }}
action: "close"
…then:
https://adjective-noun-hex.integer.azurestaticapps.net
flipped from a placeholder page (“Your Azure Static Web App is live and waiting for your content” / “Your app is now live, but we don’t have your content updates. Check the deployment status in the GitHub Actions tab in your repository. Learn more about deployment from the Static Web App deployment docs.”) to a big H1 tag with “Hello World” in it.- Refreshing
https://portal.azure.com/#@azurelabs.linuxacademy.com/resource/subscriptions/my_subscription_id/resourceGroups/my_resource_group_name/providers/Microsoft.Web/staticSites/my-first-swa/staticsite
made more things show up. - The following PowerShell commands started returning non-null values:
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).repositoryUrl # https://github.com/my-username/my-repo-name (Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).branch # main (Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).provider # GitHub
Azure SWA will respond to anything with a deployment token
So … Azure doesn’t actually really need any access to your GitHub repo, unless you want it to help you create that YAML file. And yeah, I tried this from a private GitHub repo Azure had absolutely no credentials to. GitHub needed credentials for talking to my Azure Static Web App, but if I’m not letting Azure help me write my code I store in GitHub, then Azure doesn’t need to know anything about how to authenticate into my GitHub account.
(Letting Azure write code and set secrets in my GitHub is totally how I got that YAML file from a previous attempt, FWIW. I didn’t know a thing about GitHub Actions. All I did was change 2 occurrences of the phrase “secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ADJECTIVE_NOUN_HEX
” to “AZURE_STATIC_WEB_APPS_API_TOKEN_YAY_I_DID_IT
”. I also deleted the old AZURE_STATIC_WEB_APPS_API_TOKEN_ADJECTIVE_NOUN_HEX
secret Azure had created within https://github.com/my-username/my-repo-name/settings/secrets/actions
and added my own secret.)
Note that app_location
of /src/web
, api_location
of /src/api
, and output_location
of /output
are marked in the YAML that Azure generated as optional … but … I mean, I’d argue not really anymore, since I didn’t bother to set them on the Azure side. I guess I haven’t tried a build, but I strongly suspect it wouldn’t haved worked without these being set on the GitHub side since I never did get around to setting them on the Azure side.
(My Git repository’s only other file, at the moment, besides the GitHub action definition, is /src/web/index.html
and looks like this:)
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>A web page</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
Really really
I renamed my GitHub repository by adding “-2
” to the end of its name. I force-reran its Action (the one that knows how to talk to my Azure Static Web App via the SWA’s deployment secret).
GitHub Actions thinks it ran, but https://portal.azure.com/#@azurelabs.linuxacademy.com/resource/subscriptions/my_subscription_id/resourceGroups/my_resource_group_name/providers/Microsoft.Web/staticSites/my-first-swa/staticsite
didn’t show any changes to where it thought the repository lived, either in the UI or in the “JSON View.”
I realized I hadn’t actually changed anything about my “app,” so I edited index.html
to say “Hello Goodbye” and GitHub Actions run again. Once again, it was convinced it’d talked to SWA.
Interestingly, this time the “Source,” “Deployment history,” and “Edit workflow” links had “my-repo-name-2
” in them instead of “my-repo-name
” – “JSON View” had the “-2
” in it as well this time – maybe it’s asynchronous and I jumped the gun. Or maybe SWA needed to actually see a change to the codebase, not just get a mysterious force-rerun from GitHub Actions.
Anyway, I could also see at https://adjective-noun-hex.integer.azurestaticapps.net
that the H1 tag now says “Hello Goodbye.”
So. That’s interesting. It seems any given Azure Static Web App resource isn’t too picky which CI/CD pipeline asks it to run a build & deploy process a given codebase, as long as that pipeline knows the SWA’s deployment token.
There’s no way around using deployment tokens
As of early March 2023, there’s no way to tell a GitHub Action or Azure DevOps Pipeline that you’d like to build a codebase and deploy it into a Static Web App using, say:
- the Static Web App resource’s name and
- an alternative Azure authentication approach such as service principal credentials.
The closest anyone’s come to deploying a built codebase into Static Web Apps “using” a service principal is to add extra steps to a GitHub Action or Azure DevOps Pipeline that:
- use the service principal to log into the Azure CLI, and
- use that authentication to run
az staticwebapp secrets list
against the SWA’s resource name, and - store the resulting deployment token into the Action/Pipeline’s runtime secrets cache (much the same way you could’ve just copied/pasted the deployment token into the repository’s longterm secrets store by hand).
Aaron Powell has one example but I haven’t quite gotten it to work. Chris Reddington got it working as well, but concluded that it seemed like more trouble and vulnerability than it’s worth, compared to just copy-pasting a deployment token into your repository’s secrets manager. I’m particularly inclined to agree with Chris after playing around and noticing that Microsoft didn’t include the Microsoft.Web/staticSites/listSecrets/action
operation (necessary for az staticwebapp secrets list
) in Azure’s “Website Contributor” built-in role the way they added Microsoft.Web/sites/*
operations to Website Contributor, meaning you either have to create and maintain a custom Azure role for Microsoft.Web/staticSites/listSecrets/action
or give the Service Principal you’re feeding your GitHub Action / Azure DevOps Pipeline an insanely broad built-in Azure role. No, thank you.
Microsoft, could you address issue 312 for enterprise customers, please?
SWA seems to have opinions about YAML file locations
The only thing that doesn’t seem to quite come out correct with the steps I’ve done so far is the “Edit workflow” link in the SWA portal webpage. It’s trying to tell me to go edit https://github.com/my-username/my-repo-name-2/tree/main/.github/workflows/azure-static-web-apps-adjective-noun-hex.yml
(note the filename is not its-my-cicd.yml
but instead is named after the Azure SWA’s production URL).
I’ll have to learn why and if it can be “fixed” and how. Or whether I’m supposed to just avoid using its-my-cicd.yml
-type filenames and conform to Azure’s naming standards. Etc. etc. etc. But that’s a pretty good start.
Building an SWA live site from code stored in Azure DevOps Repos
The other thing I learned recently is that when you use the clicky-click web portal to set up a Static Web App, choosing Azure DevOps as your deployment puts the following file named /azure-static-web-apps-adjective-noun-hex.yml
into your repo:
name: Azure Static Web Apps CI/CD
pr:
branches:
include:
- main
trigger:
branches:
include:
- main
jobs:
- job: build_and_deploy_job
displayName: Build and Deploy Job
condition: or(eq(variables['Build.Reason'], 'Manual'),or(eq(variables['Build.Reason'], 'PullRequest'),eq(variables['Build.Reason'], 'IndividualCI')))
pool:
vmImage: ubuntu-latest
variables:
- group: Azure-Static-Web-Apps-adjective2-noun2-hex2-variable-group
steps:
- checkout: self
submodules: true
- task: AzureStaticWebApp@0
inputs:
azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN_ADJECTIVE2_NOUN2_HEX2)
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
app_location: "/src/web" # App source code path
api_location: "/src/api" # Api source code path - optional
output_location: "/output" # Built app content directory - optional
###### End of Repository/Build Configurations ######
Azure SWA really will respond to anything with a deployment token
OK so I went to https://dev.azure.com/my-ado-org-name/my-ado-project-name/_library?itemType=VariableGroups&view=VariableGroupView&variableGroupId=1&path=azure-static-web-apps-adjective2-noun2-hex2-variable-group
, which I’d connected to my second SWA, and added an extra AZURE_STATIC_WEB_APPS_API_TOKEN_YAY_I_DID_IT
variable with the deployment token for my first SWA.
Then I changed the .yml
to azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN_YAY_I_DID_IT)
and committed.
Okay, cool, we went back to “Hello World” (as was found in my ADO repo) in my-first-swa
’s live website and the “JSON View” and “Source” and “Deployment history” and “Edit workflow” links auto-updated, as did:
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).repositoryUrl # https://dev.azure.com/my-ado-org-name/my-ado-project-name/_git/my-ado-project-name
(Get-AzStaticwebApp -ResourceGroupName ((Get-AzResourceGroup).ResourceGroupName) -Name my-first-swa).provider # DevOps
lol, proof Azure Static Web Apps will build a website off of any old CI/CD pipeline that’s gotten ahold of its deployment token. Eeep – be careful with those tokens!
Future research
Finally, here seem to be the differences between choosing to host your Git in GitHub vs. Azure DevOps Repos:
- GitHub lets you do branch preview websites w/o having to write the CI/CD for it yourself. I’m really excited about the possibilities of great testing maturity from this, without a lot of effort.
- Azure DevOps, given the right subscription model, seems to have slightly easier-to-use GUI interfaces for building out automated test suites for build/deploy. (Not that I’ve actually figured out how to get any of them working, though, so I can’t really say they’re easier.)
- For my research into letting the GitHub action provision-SWA-if-not-exists-yet:
Although I don’t want to justecho
this or anything (I’d want to dump it into a GitHub Action environment variable or something), it seems the following PowerShell gets me my API key. (I’d probably use theaz
CLI commands, though, when inside a GitHub Action, not PowerShell syntax):((Get-AzStaticWebAppSecret -ResourceGroupName $my_resource_group_name -Name $my_swa_name).ToJsonString() | ConvertFrom-Json).properties.apiKey
- The only problem is … how does the repo connect to the
az
CLI with authority to create SWA resources, but only of a certain name? Like, can you even provision permissions that have to do with things that don’t exist yet?
- The only problem is … how does the repo connect to the