Retrieving Azure Static Web App Deployment Token with Bicep & GitHub Actions
For those of you who are using Bicep and GitHub Actions together to deploy your Static Web Apps infrastructure and code, this is a handy way to obtain the deployment token without saving it as a secret to your repository. One less piece of configuration is always nice!
Start by defining your Static Web App Bicep module (taken from this website’s config):
// file: swa.bicep
import { sourceCodeUrl } from './constants.bicep'
@description('Name of the Static Web App resource')
param name string
@description('Azure location of the Static Web App resource')
@allowed(['westus2', 'centralus', 'eastus2', 'westeurope', 'eastasia']) // Mandated by resource availability
param location string = 'eastasia'
resource swa 'Microsoft.Web/staticSites@2025-03-01' = {
name: name
location: location
sku: {
name: 'Standard'
}
tags: {
source_code_url: sourceCodeUrl
purpose: 'Hosts LHKN website'
location: location
}
properties: {
allowConfigFileUpdates: true
stagingEnvironmentPolicy: 'Disabled'
buildProperties: {
appLocation: 'app'
appBuildCommand: 'npm run build'
outputLocation: 'app/dist'
}
}
}
@secure()
@description('API token which can be used to deploy Static Web App from GitHub')
output apiToken string = listSecrets(swa.id, '2019-08-01').properties.apiKey
@description('Static Web App properties')
output properties resourceOutput<'Microsoft.Web/staticSites@2025-03-01'>.properties = swa.properties
Reminder: You should always ensure that the apiToken output is decorated with the secure function so that Bicep will never print it to stdout. There’s an exception to this when we need to use the value inside GitHub Actions, read on to see how that is handled securely.
Now, inside your main Bicep script, forward the apiToken output:
// file: main.bicep
// ... params and other resources omitted
module staticWebApp 'swa.bicep' = {
name: guid(resourceGroup().name, 'swa.bicep')
params: {
location: 'eastasia' // Closest static web app region
name: 'swa-${appName}'
}
}
// ... outputs and other resources omitted
@description('Static Web App API token. Ensure this is masked when used inside GitHub actions.')
#disable-next-line outputs-should-not-contain-secrets
output apiToken string = staticWebApp.outputs.apiToken
As you can see, we don’t mark the apiToken output as secure inside our main script. That’s because we’re using the GitHub Action azure/bicep-deploy@v2 and it has support for masking outputs so that they don’t show up in the deployment logs.
So let’s define the step:
- name: Deploy Azure resources
id: deploy-azure-resources
uses: Azure/bicep-deploy@v2
with:
type: deployment
operation: create
scope: resourceGroup
resource-group-name: ${{ env.RESOURCE_GROUP_NAME }}
name: ${{ env.RESOURCE_GROUP_NAME }}-${{ github.run_id }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
template-file: arm/main.bicep
masked-outputs: apiToken
parameters: |
discordBotToken: ${{ secrets.DISCORD_BOT_TOKEN }}
Two important things to note here:
- This action was given an id. This allows us to reference it later on when we deploy our code to our Static Web App resource.
- The
masked-outputsaccepts a comma separated list of output names to mask. In this case we only need to maskapiToken.
Now when we build and deploy our code using the azure/static-web-apps-deloy@v1 action, we can simply reference this step’s outputs:
- name: Deploy to Static Web Apps
uses: Azure/static-web-apps-deploy@v1
with:
action: upload
azure_static_web_apps_api_token: ${{ steps.deploy-azure-resources.outputs.apiToken }}
app_location: app
output_location: dist
app_build_command: npm run build
skip_api_build: true
And we’re all done! You don’t need to store the api token in your repository and removes 1 tiny manual step.