Use Azure Pipelines to deploy Angular application to Azure App Service

Andrey Kukharenko
8 min readMay 5, 2023

--

The story about using Azure Pipelines (YAML) to build and deploy the Angular web application to Azure App Service.

Assumptions

  1. Be familiar with Git.
  2. Be familiar with Angular wed apps development.
  3. Be familiar with Azure cloud.
  4. Have configured Azure DevOps with repository and pipelines activated for the project.
  5. Have active account for Azure with subscription.

Parts:

  1. Use Azure Pipelines to deploy .NET application to Azure App Service
  2. Use Azure Pipelines to deploy Angular application to Azure App Service
  3. Use Azure Pipelines to deploy Function App

Introduction

Sometimes we have projects with Angular frontend side and this app need to be deployed (host) on Azure App Service. This is most popular and simple way to run web application on Azure with minimal cost (because available different plans, Free and Paid (basic, premium)). There are also service Azure DevOps hat provided complete functionality to develop the application and manage the work with CI/CD process.

So basically the flow as we can see here:

  1. Azure Repos for Git repo.
  2. Azure Pipelines for build and deployment.
  3. Azure cloud to host the application.

The article provide many details about configuring Azure DevOps, Azure and etc. So we skip some steps here to do not duplicate them.

Here we will discuss the next things:

  1. Azure DevOps — in general.
  2. Azure Pipelines — as one of the functionality that used for CI/CD.

Let’s take a look in more details.

1 Azure DevOps

This is the main service in the development process, will be used for track the work, code, CI/CD, artifacts, tests. Assumption here to have the repository with the code (at least basic started web app) for sample application (Angular with Angular CLI).

For our purposes and work we need:

  1. Azure Repos — for Git repositories with the code.
  2. Azure Pipelines — for CI/CD stuff.

What’s need to be configured:

  1. Service Connections for Azure — to able to deploy to Azure with special credentials.
  2. Git Repository — to store the code.

More details provided in the Part 1 — how to configure service connection.

2 Azure Pipelines

All the steps are the same as we discussed in the Part 1:

  1. Have a Git repository in Azure Repos.
  2. Create environments for deployment — prepare the configuration for environment definitions to deploy the application, configure them (like approvals, etc.).
  3. Create the library with variable groups — to use variables in the pipeline and can be edit without code changes.
  4. Create the pipeline — prepare files in the repo and select them for new pipeline.
  5. Configure security — access to the library, pipeline permissions for environment to able to consume them.

We will provide here just the steps with differences. And only one here is template code with YAML files for pipeline.

2.1 Create the pipeline

First we need to create the files with the logic and code. After this configure pipeline and select appropriate file from the repository — azure-pilelines.yml.

2.1.1 Code for pipeline (YAML template files)

Sample pipeline for Angular web application contains few files in the repo. All the files placed in the build folder in the repository root with solution and some other folders and items.

Pipeline with stages: Build and Deploy used here.

Files in this sample (as template):

  1. azure-pipelines.yml — main file to provide the structure and workflow.
  2. pipelines/build.yml — stage to define the build steps and what the jobs need to run.
  3. pipelines/release.yml — stage for deployment stuff, define jobs and rules.
  4. pipelines/build-angular.yml — template file with common steps to build the Angular web application using node.js and npm (with Angular CLI).
  5. pipelines/deploy-webapp.yml — template file with common steps to deploy application to Azure App Service from the package (.zip archive).

All the steps to build for Angular app based on usage of Angular CLI and npm. To get more details you can read the documentation.

File azure-pipelines.yml:

# Parameters for pipeline
parameters:
- name: environment
displayName: Environment
type: string
default: BuildOnly
values:
- BuildOnly
- Development
- QA
- name: projectName
displayName: ProjectName
type: string
default: <project_name> # replace here with your project name
- name: skipLint
displayName: SkipLint
type: boolean
default: false

# no automatically triggers
trigger:
- none

variables:
- group: Deploy-Environment-Shared

# stages - list of the jobs to do step by step
stages:
# Build and Lint (and other stuff) stage
- template: pipelines/build.yml
parameters:
Environment: ${{ parameters.Environment }}
ProjectName: ${{ parameters.ProjectName }}
SkipLint: ${{ parameters.SkipLint }}

- ${{ if not(eq(parameters.Environment,'BuildOnly')) }}:
- template: pipelines/release.yml
parameters:
Environment: ${{ parameters.Environment }}
ProjectName: ${{ parameters.ProjectName }}

File pipelines/build.yml:

stages:
- stage: Build
displayName: Build${{ parameters.ProjectName }}

# Use multiple jobs, so the linter can work in parallel to the build.
# This also allows to run the Linter on Linux whereas you build can run on Windows or Mac.
jobs:
# Lint the code with Super-Linter from GitHub
- job: lint
displayName: Lint code base
condition: eq(${{ parameters.SkipLint }}, false)
pool:
vmImage: 'ubuntu-latest'
steps:
- script: docker pull github/super-linter:slim-latest
displayName: Pull GitHub Super-Linter image
- script: >-
docker run \
-e RUN_LOCAL=true \
-e VALIDATE_JSCPD=false \
-e VALIDATE_MARKDOWN=false \
-e VALIDATE_EDITORCONFIG=false \
-v $(System.DefaultWorkingDirectory):/tmp/lint \
github/super-linter
displayName: 'Run GitHub Super-Linter'
continueOnError: true

# Do main build action
- job: build
displayName: Build and Test, create artifacts
pool:
vmImage: 'ubuntu-latest'
variables:
BuildConfiguration: 'Release'
steps:
- template: build-angular.yml
parameters:
ProjectName: ${{ parameters.ProjectName }}
NodeVersion: '16.x'
AppName: '<you app name>' # replace here with your app name

File pipelines/build-angular.yml:

steps:
- checkout: self
clean: true

# Install node.js and npm
- task: NodeTool@0
displayName: 'Install Node.js'
inputs:
versionSpec: ${{ parameters.NodeVersion }}

# Install Angular CLI
- script: |
npm install -g @angular/cli
displayName: 'Install the Angular CLI globally'

# Install the packages for application
- task: Npm@1
displayName: 'Install Angular app packages'
inputs:
command: 'ci'

# Build the project
- task: Npm@1
displayName: Build the app
inputs:
command: custom
verbose: false
customCommand: 'run build'

# Archive the files after build from dist folder
- task: ArchiveFiles@2
displayName: Zip Angular dist folder
inputs:
rootFolderOrFile: 'dist/${{ parameters.AppName }}'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/${{ parameters.ProjectName }}.zip'
replaceExistingArchive: true

# Publish pipeline artifacts
- task: PublishPipelineArtifact@1
displayName: Publish Angular app dist folder artifact
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)'
artifactName: '${{ parameters.ProjectName }}'
publishLocation: 'pipeline'

File pipelines/release.yml:

stages:
# Main deployment stage per one selected environment
- stage: Deploy
displayName: Deploy${{ parameters.ProjectName }}
variables:
- group: Deploy-Environment-${{ parameters.Environment }}
jobs:
# Do actual web app deployment to Azure
- deployment: DeployAngularApp
displayName: Deploy Angular WebApp to Azure
pool:
vmImage: 'windows-latest'
environment: ${{ parameters.Environment }}
strategy:
runOnce:
deploy:
steps:
- template: deploy-webapp.yml
parameters:
Environment: ${{ parameters.Environment }}
ProjectName: ${{ parameters.ProjectName }}
PackageName: ${{ parameters.ProjectName }}

File pipelines/deploy-webapp.yml:

steps:
- checkout: self
clean: true

# Download pipelines artifacts
- task: DownloadPipelineArtifact@2
inputs:
artifactName: '${{ parameters.ProjectName }}'
buildType: 'current'
targetPath: '$(Build.ArtifactStagingDirectory)\${{ parameters.ProjectName }}'

# Deploy to Azure App Service
- task: AzureWebApp@1
displayName: 'Deploy to $(AppServiceNameFrontend)'
inputs:
azureSubscription: '$(AzureServiceConnection)'
appType: 'webApp'
appName: '$(AppServiceNameFrontend)'
package: '$(Build.ArtifactStagingDirectory)\${{ parameters.ProjectName }}\${{ parameters.PackageName }}.zip'

As we can see here all the files provide parameters and variables that help to use it in variety of projects — most of the thigs are parametrized.

Note: this template is working for only one environment deployment. To use multiple it can be updated very simple because templates is the same. Just need to create the file like release-all.yml and provide all environments to deploy with just static Environment name.

Sample of file pipelines/release-all.yml:

stages:
# Deploy to Development
- stage: DeployDevelopment
displayName: Deploy-${{ parameters.ProjectName }}-Development
dependsOn: [Build]
condition: succeeded()
variables:
- group: Deploy-Environment-Development
jobs:
# Do actual web app deployment to Azure
- deployment: DeployDevelopment
displayName: Deploy WebApp to Azure to Development
pool:
vmImage: 'windows-latest'
environment: Development
strategy:
runOnce:
deploy:
steps:
- template: deploy-webapp.yml
parameters:
Environment: Development
ProjectName: ${{ parameters.ProjectName }}
PackageName: 'EmailService.API' # replace here with your package name

# Deploy to QA
- stage: DeployQA
displayName: Deploy-${{ parameters.ProjectName }}-QA
dependsOn: [Build, DeployDevelopment]
condition: succeeded()
variables:
- group: Deploy-Environment-QA
jobs:
# Do actual web app deployment to Azure
- deployment: DeployQA
displayName: Deploy WebApp to Azure to QA
pool:
vmImage: 'windows-latest'
environment: QA
strategy:
runOnce:
deploy:
steps:
- template: deploy-webapp.yml
parameters:
Environment: QA
ProjectName: ${{ parameters.ProjectName }}
PackageName: 'EmailService.API' # replace here with your package name

This solution can be used to deploy one single artifacts to all environments. Another one — use pipeline to build only and create artifacts and use Classic Releases to do the deployment. Sometimes it’s more powerful way and more customized for CD part. So it can split the whole pipeline into 2 parts: CI (pipelines) and CD (releases).

2.1.2 Create pipeline

Need to push all the files for your project to repository. After this when creating the pipeline select Azure Repos → Select Repo → Existing Azure Pipeline YAML file.

It will open the main file and allow to review and save. Usually the name with be the same as repository so later it need to be renamed.

The sample pipeline create for Frontend

2.2 Run pipeline

To run the pipeline it need to go to selected pipeline and click on “Run pipeline” button.

Because of pipeline use parameters it will show them to user in the UI to able to select and change if needed.

When pipeline successfully validated compiled YAML it will show the stage and jobs.

After running the job we can see the list of steps in the process with all logs and statuses for each step. So it can simple to look into each of the step with more details or even download raw log file.

Results for each step in pipeline

If any environment selected as for this sample we can see the name and status on Environments tab in Build results.

Environment selected to be deployed to

2.3 Pipeline review

The visual dependencies for files provided on the next image — here we can see what are stages here and jobs. All of them in the separate files.

Files in template

On the next image provided detailed view on the actual template for pipeline for building Angular applications.

Pipeline details for Angular App

All this steps is main and needed but in your personal projects it can be configured to add more steps and things if needed. This is mostly about minimal setup that can be used for all projects.

Conclusion

Using Azure Pipelines is a great way to implement CI/CD to your projects and even very simple. Here in the article we saw how can we implement the pipeline for Angular application using a repository in Azure DevOps (Repos), configuration (parameters, variables), usage of templates (YAML files with shared logic), environments, libraries.

Links

  1. https://angular.io/docs — Angular official documentation.
  2. https://angular.io/cli
  3. https://learn.microsoft.com/en-us/azure/app-service/
  4. https://learn.microsoft.com/en-us/azure/devops/pipelines/?view=azure-devops
  5. https://learn.microsoft.com/en-us/azure/devops/pipelines/get-started/pipelines-get-started?view=azure-devops
  6. https://learn.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops
  7. https://henriquesd.medium.com/deploying-an-angular-application-with-azure-pipelines-7c820116085f — sample for Angular web application built with Azure Pipeline.

--

--