Az #4— Az 400 — Create Pipelines using Azure Pipelines vs GitHub Actions to deploy a full stack…
In this article, lets create a pipleline / workflow to deploy a fullstack React, .net and SQL project using
Az #4— Az 400 — Create Pipelines using Azure Pipelines vs GitHub Actions to deploy a full stack project
In this article, lets create a pipleline / workflow to deploy a fullstack React, .net and SQL project using
- Azure Devops
- Github Workflows

Azure Devops
Using the github token create a service connection in Azure Devops.


Create Pipeline
In ADO, create a pipline by selecting the .net api repo snf pipeline as .net FW

clear the steps and start afresh.
lets split the whole activity into 3 stages
- Build 2. Create Azure Infrastructure 3. Deploy the build
We need the basic understanding of the workspace to correctly design the pipeline
lets take an Azure Hosted Agent
Agent Machine (temporary) │ ├── Source Code (downloaded here) ├── Build Output (compiled here) ├── Artifacts (final output to publish)
1. $(Build.SourcesDirectory)
Where code is downloaded
Example:
D:\a\1\s
What is inside?
.csproj,.sln, React code, etc.
It cant be used for output
2. $(Build.BinariesDirectory)
Where compiled files can go (optional use)
D:\a\1\b
What is inside?
.dll, .exe- compiled outputs
Not always used explicitly (dotnet handles internally)
3.$(Build.ArtifactStagingDirectory) (MOST IMPORTANT)
This is where the arifact will be published
D:\a\1\a
Think of this as: “Final package folder”
IaC steps
lets create azure resources webapp and Azure Sql using the bicep file we created earlier, as this will massively help to spin up and tear down Infra and save the cost.
- Create Resource group in azure.
Create a variable group to hold the variables ( Most of them will be moved to Keyvault later )

1. Build stage
In this stage, the .net app will be built, then the react build and copy the build to wwwroot and publish it.
Include the variables here
variables:
...
...
- group : BooksApp-variables
as the repo is in github include them explictly here
resources:
repositories:
- repository: reactRepo
type: github
name: Azure204-medium/Books-React
#endpoint is the service connection name
endpoint: Azure204-medium
- repository: apiRepo
type: github
name: Azure204-medium/BooksApi
endpoint: Azure204-medium
checkout the code
stages:
- stage: Build
displayName: 'Create Build'
jobs:
- job: BuildApp
displayName: 'Build App'
steps:
- checkout: apiRepo
....
....
restore, build and publish with release configuration
- task: DotNetCoreCLI@2
displayName: 'restore nuget packages'
inputs:
command: 'restore'
projects: '$(solution)'
feedsToUse: 'select'
- task: DotNetCoreCLI@2
displayName: 'Build .net'
inputs:
command: 'build'
projects: '**/*.csproj'
- task: DotNetCoreCLI@2
Now checkout React build, install node, npm
# node install
- task: UseNode@1
displayName: 'Install node.js'
inputs:
version: '18.x'
- script: node -v
displayName: 'Check Node version'
- script: dir
displayName: 'Check folder structure'
# React install
- task: Npm@1
displayName: 'Install npm'
inputs:
command: 'install'
workingDir: '$(Build.SourcesDirectory)/Books-React'
# React build
- script: npm run build
displayName: 'build react'
workingDirectory: '$(Build.SourcesDirectory)/Books-React'
This is the important stage, we need copy the React build into the .net wwwroot, then zip and publish the artifact
- task: CopyFiles@2
displayName: 'copy to wwwroot'
inputs:
SourceFolder: '$(Build.SourcesDirectory)/Books-React/build'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/publish/SimpleBooksApi/wwwroot'
- script: dir
displayName: 'After copy Check folder structure'
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/publish/'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/drop/app.zip'
replaceExistingArchive: true
verbose: true
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)/drop'
ArtifactName: 'drop'
the final artical will look like this


If you download and unzip you will see this

2. Create Azure Infra
lets create Infra stage with the bicep we created earlier
- Create Resource group
- Run the bicep to creat webapp and sql
- update the connection string.
Resource Group
- stage: Infra
dependsOn: Build
displayName: 'Az Infra'
jobs:
- job: InfraJob
displayName: 'Create Az Infra Job'
steps:
- task: AzureCLI@3
displayName: 'Create Az RG'
inputs:
connectionType: 'azureRM'
azureSubscription: 'Azure subscription 1(a81f821e-5466-4898-8671-bf41188f39b3)'
scriptType: 'batch'
scriptLocation: 'inlineScript'
inlineScript: 'az group create -g $(booksResourceGroup) -l uksouth'
- create the webapp and sql with the bicep file created earlier
- task: AzureCLI@3
displayName: 'Create webapp and sql'
inputs:
connectionType: 'azureRM'
azureSubscription: 'Azure subscription 1(a81f821e-5466-4898-8671-bf41188f39b3)'
scriptType: 'batch'
scriptLocation: 'inlineScript'
inlineScript: 'az deployment group create --resource-group $(booksResourceGroup) --template-file $(Build.SourcesDirectory)/Iac/main.bicep --parameters $(Build.SourcesDirectory)/Iac/parameters/parameters.dev.json'
- Update the Sql connection string in the web app, ( it will be moved to Keyvault later )
- task: AzureCLI@3
displayName: 'Update connection string'
inputs:
connectionType: 'azureRM'
azureSubscription: 'Azure subscription 1(a81f821e-5466-4898-8671-bf41188f39b3)'
scriptType: 'batch'
scriptLocation: 'inlineScript'
inlineScript: 'az webapp config connection-string set -n books-webapp-dev -g books-dev-rg --connection-string-type SQLAzure --settings BooksDB="Server=tcp:books-sql-db-dev.database.windows.net,1433;Initial Catalog=books-sql-server-dev;Persist Security Info=False;User ID=sqlserveradmin;Password=Admin12345678!;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;'
3. Deploy to Az Webapp
- download the artifact
- stage: deploy
displayName: 'Deploy to Az'
dependsOn: Infra
jobs:
- job: deployJob
displayName: deploy to Az
steps:
- task: DownloadBuildArtifacts@1
inputs:
buildType: 'current'
downloadType: 'single'
artifactName: 'drop'
downloadPath: '$(System.ArtifactsDirectory)'
- Deploy the build to the Azure webapp
- task: AzureRmWebAppDeployment@5
inputs:
ConnectionType: 'AzureRM'
azureSubscription: 'Azure subscription 1(a81f821e-5466-4898-8671-bf41188f39b3)'
appType: 'webApp'
WebAppName: '$(booksWebApp)'
package: '$(System.ArtifactsDirectory)/drop/*.zip'
All done.



Github Workflow
lets create github workflow and recreate, the underlying concept remain the same ( no predfined folders but we create them )
Runner Machine (temporary)
│
├── Source Code (downloaded)
├── Build Output (compiled)
├── Artifacts (uploaded)
lets create the variables here

There are no stages in github workflow
In ADO, the repos were given in resources but workflow works differently no resources
# for .net api
- name: Checkout Api Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
#for react app
- name: Checkout react repo
uses: actions/checkout@v4
with:
repository: Azure204-medium/Books-React
path: react
fetch-depth: 0
With path: react
GitHub will create a folder like this:
$GITHUB_WORKSPACE
│
├── (API repo files)
│
└── react/ this comes from path
├── package.json
├── src/
path: react = Put this repo inside a folder called react
In workflow, all steps are command and actions
.net api
# Install the .NET Core workload
- name: Install .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
# Build api
- name: Build Api
run: dotnet build --configuration $env:Configuration
# Publish api
- name: Publish Api
run: dotnet publish -c $env:Configuration -o publish
Note as it is powershell command variables has to be accessed like $env:Configuration
React build
# Install node
- name: Setup Node.js environment
uses: actions/setup-node@v6.4.0
with:
node-version: 18
# create react build
- name: Build react
run: |
cd react
npm install
npm run build
env:
CI: false
Note: env : CI: true will make warnings considered as errors lets keep it false for time being.
Copy the build to wwwroot
#Copy react build to wwwroot
- name: Copy react build to wwwroot
run: |
xcopy react\build publish\wwwroot /E /I /Y
xcopy = Windows command to copy files and folders
source = react\build
destination = publish\wwwroot
/E — Copy all subfolders, including empty ones
/I — If destination doesn’t exist → treat it as a folder
If destination doesn’t exist → treat it as a folder
Without this:
❓ xcopy may ask: file or folder?
This avoids prompt (important in pipelines)
Create artifact ( staging directory) and upload
publish → artifact → upload
In ADO, there is Build.ArtifactStagingDirectory but in workflow we need to create the artifact folder and copy the build
# Copy artifacts
- name: Create and copy artfiacts
run: |
mkdir artifact
xcopy publish artifact /E /I /Y
upload artifact with name drop
# upload artifact
- name: Upload a Build Artifact
uses: actions/upload-artifact@v7.0.1
with:
name: drop
path: artifact
Create Infra Job
In this job, lets create a resource group, deploy bicep and update the connection string in the webapp
fetch repo to get the bicep files
infra:
name: Creat Az infra
runs-on: windows-latest
needs: build
steps:
- name: Checkout Api Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
Service principal
create a service principal and grant “Contributor access” to the subscription for the workflow to connect to Azure

save the result json in the repo variables.
Az login with the credentials from the secret
- name: Azure Login
uses: Azure/login@v3
with:
# Paste output of `az ad sp create-for-rbac` as value of secret variable: AZURE_CREDENTIALS
creds: ${{secrets.AZURE_CREDENTIALS}}
Create resource group
# Create resource group
- name: Create resource group
uses: Azure/cli@v3
with:
# Specify the script here
inlineScript: |
az group create -g ${{vars.booksResourceGroup}} -l uksouth
${{vars.booksResourceGroup}} — variable from the variable group
Depoly bicep
# Deploy bicep
- name: Deploy bicep
uses: Azure/cli@v3
with:
inlineScript: |
az deployment group create --resource-group ${{vars.booksResourceGroup}} \
--template-file $GITHUB_WORKSPACE/Iac/main.bicep \
--parameters @$GITHUB_WORKSPACE/Iac/parameters/parameters.dev.json
$(Build.SourcesDirectory)→$GITHUB_WORKSPACE
1. What does @ mean?
--parameters @file.json
@ means:
“Read parameters from a file”
2. Without @ (what happens?)
--parameters file.js
Azure CLI thinks: "file.json" is a parameter name/value
It will FAIL or behave incorrectly
3. With @ (correct)
--parameters @parameters.dev.json
Azure CLI understands: “Load parameters from this JSON file”
4. Simple analogy
SyntaxMeaning--parameters key=valueinline parameter--parameters @file.jsonload from file
Update the connection string
# update connection string
- name: Update Connection string
uses: Azure/cli@v3
with:
inlineScript: |
az webapp config connection-string set -n books-webapp-dev -g ${{vars.booksResourceGroup}} \
--connection-string-type SQLAzure --settings BooksDB="Server=tcp:books-sql-db-dev.database.windows.net,1433;Initial Catalog=books-sql-server-dev;Persist Security Info=False;User ID=sqlserveradmin;Password=Admin12345678!;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;
Deploy the app
This is the final job, just 2 steps, login into Azure and deploy the code into the webapp
Login
Deploy:
name: Deploy to webapp
needs: infra
runs-on: ubuntu-latest
steps:
- name: Download a Build Artifact
uses: actions/download-artifact@v4
with:
# Artifact name
name: drop
# Destination path
path: artifact
# Azure login
- name: Azure Login
uses: Azure/login@v3
with:
# Paste output of `az ad sp create-for-rbac` as value of secret variable: AZURE_CREDENTIALS
creds: ${{secrets.AZURE_CREDENTIALS}}
now deploy to the Azure webapp
- name: Azure WebApp
uses: Azure/webapps-deploy@v2.2.19
with:
# Name of the Azure Web App
app-name: ${{vars.BOOKSWEBAPP}}
package: artifact

메타데이터
- post_id
- e1809154a9f2
- slug
- az-4-az-400-create-pipelines-using-azure-pipelines-vs-github-actions-to-deploy-a-full-stack-e1809154a9f2
- url
- https://medium.com/@quizzesforyou/az-4-az-400-create-pipelines-using-azure-pipelines-vs-github-actions-to-deploy-a-full-stack-e1809154a9f2
- canonical_url
- https://medium.com/@quizzesforyou/az-4-az-400-create-pipelines-using-azure-pipelines-vs-github-actions-to-deploy-a-full-stack-e1809154a9f2
- author_url
- https://medium.com/@quizzesforyou
- status
- ok
- fetched_at
- 2026-06-09 15:37:30