← Back to list

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

quizzesforyou.com · 2026-04-28 21:15 · 0 claps · 7.5 min read
#azure-devops #ci-cd-pipeline #github-workflow
Open on Medium ↗
Wiki topics: 🌐 · Web Development ☁️ · DevOps & Cloud 🔓 · Open Source

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

  1. 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.

  1. 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

[embed]BooksApi/booksapp-azure-pipelines.yml at master · Azure204-medium/BooksApi Contribute to Azure204-medium/BooksApi development by creating an account on GitHub.github.com

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

  1. Create Resource group
  2. Run the bicep to creat webapp and sql
  3. 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'
  1. 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'
  1. 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

  1. 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)'
  1. 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