Badge claim: Power of the Shell
With a dashboard (and QR code gathering) web app running as an Azure Static Web App, linked to a separate Azure Functions app for the backend that handles all communication with Dataverse, we needed a proper setup for efficient development, collaboration and deployment.
Using Azure DevOps to host both repos, we used Azure Pipelines for CI/CD.

The backend runs .NET 10 in Linux, using the DotNetCoreCLI@2 and AzureFunctionApp@2 tasks for build and deployment.
trigger:
branches:
include:
- main
pr:
branches:
include:
- main
variables:
buildConfiguration: "Release"
azureSubscription: "Microsoft Azure Sponsorship(30b24b6e-ef03-42c4-bba5-20a33afd68e4)"
functionAppName: "itera-scope-creepers-api"
stages:
- stage: BuildAndDeploy
displayName: "Build & Deploy"
jobs:
- job: Build
displayName: "Build API Function App"
pool:
vmImage: "ubuntu-latest"
steps:
- checkout: self
- task: UseDotNet@2
displayName: "Use .NET SDK 10.x"
inputs:
packageType: "sdk"
version: "10.x"
- task: DotNetCoreCLI@2
displayName: "Restore NuGet packages"
inputs:
command: "restore"
projects: "API.csproj"
- task: DotNetCoreCLI@2
displayName: "Build"
inputs:
command: "build"
projects: "API.csproj"
arguments: "--configuration $(buildConfiguration)"
publishWebProjects: false
- task: DotNetCoreCLI@2
displayName: "Test (all *Tests.csproj projects if present)"
inputs:
command: "test"
projects: "**/*Tests.csproj"
arguments: "--configuration $(buildConfiguration)"
publishTestResults: true
- task: DotNetCoreCLI@2
displayName: "Publish function app"
inputs:
command: "publish"
projects: "API.csproj"
arguments: "--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/publish"
publishWebProjects: false
zipAfterPublish: true
- task: AzureFunctionApp@2
displayName: "Deploy Azure Function App"
inputs:
azureSubscription: "$(azureSubscription)"
appType: "functionAppLinux"
appName: "$(functionAppName)"
package: "$(Pipeline.Workspace)/**/*.zip"
The frontend is a client-side rendered React app, using Vite as the bundler and pnpm as the package manager for increased security, and is both built and deployed using the AzureStaticWebApp@0 task.
trigger:
branches:
include:
- main
pool:
vmImage: ubuntu-latest
variables:
NODE_VERSION: '22.22.0'
steps:
- task: NodeTool@0
displayName: 'Use Node.js $(NODE_VERSION)'
inputs:
versionSpec: '$(NODE_VERSION)'
- script: |
corepack enable
pnpm config set node-linker hoisted
pnpm install --frozen-lockfile
displayName: 'Install dependencies with pnpm'
- script: pnpm build
displayName: 'Build app'
- task: AzureStaticWebApp@0
displayName: 'Deploy to Azure Static Web App (itera-scope-creepers)'
inputs:
azure_static_web_apps_api_token: '$(AZURE_STATIC_WEB_APPS_API_TOKEN)'
app_location: '/'
api_location: ''
output_location: 'dist'
For local development, the SWA CLI is used to emulate the linked backend.
CI/CD FTW! 🤓




































