Switching positions in Azure DevOps (Angular + API): How can the Angular configuration be modified for the designated slot?

Setting up the Angular app can be quite tricky. In our Azure Yaml pipeline publishing job, we tackle the challenge by executing a "token" replacement to tailor the configuration for the specific target environment within a tokenized artifact.

Subsequently, we deploy this tokenized artifact (with replaced tokens) to the designated environment:

    - task: AzureWebApp@1
      inputs:
        azureSubscription: 'xxxx'
        appType: 'webApp'
        appName: [depends on $(environment)]
        package: '$(Pipeline.Workspace)/xxx'

This approach allows us to have distinct configurations for DEV, TEST, and STAGING (which serves as a Production slot).

However, when it comes time to switch from STAGING to PROD, an issue arises. The Angular app ends up in PROD with values intended for STAGING, creating a dilemma. I am seeking advice on how best to proceed.

Here are some potential solutions:

  • During the STAGING deployment, generate the PROD files with a different naming convention (e.g., main.*.js.PROD). Then, after the PROD swap, rename the files. However...

    • The files may still be in use, preventing them from being renamed.
    • Renaming could introduce downtime, and it is unclear if YAML supports renaming already deployed files.
  • Prior to deploying to PROD, first deploy to STAGING using PROD variables before executing the swap. But...

    • This method elongates the deployment process since STAGING must be redeployed even if it already exists.
    • It underutilizes the benefits of slots.
  • Skip the STAGING swap and opt for a full deployment directly to PROD. Yet...

    • This results in the lengthiest deployment time.
    • No advantage of utilizing slots in this scenario.

Edit: Thanks to @prawin's suggestion, here's another idea to consider:

  • Implement dynamic server-side configuration
    • This appears promising when the backend and frontend share a host. However, what about cases where they are decoupled? In such instances, the backend URL would also need to be configured accordingly based on the environment.

After further exploration and experimentation, additional approaches emerge:

  • Utilize FTP or the Kudu API to deploy only the updated file.

  • Create and transfer the PROD configuration to STAGING prior to performing the swap. This option seems most appealing to me.

Are there any other factors to consider? What represents the "standard" procedure for handling this situation?

Answer №1

UPDATE (17/05/2022): I have retained the original proposal for reference purposes (see below), but it is important to note that it may fail silently if the file being replaced is currently in use, which is quite common.

As a solution to this issue, we have implemented a new strategy:

  1. We first deploy to a STAGING environment with STAGING variables for proper testing (automated end-to-end).
  2. Once satisfied with the outcome, we redeploy to STAGING using PROD variables. This stage is referred to as "PRE-PROD".
  3. Upon completion of validation in PRE-PROD, the deployment is then swapped to the PROD environment.
- stage: STAGING
  jobs:
    - template: release-to.yml
      parameters:
        environment: staging
        variables: staging

- stage: Release_PRE_PROD
  dependsOn: Release_STAGING
  jobs:
    - template: release-to.yml
      parameters:
        environment: staging
        variables: production

- stage: Swap_PROD
  dependsOn: Release_PRE_PROD
  #Your swap steps

The release-to.yml file handles the necessary variable loads and replacements.

Hope this explanation proves useful!

Before 17/05/2022 (for archival purposes): Following experimentation with different approaches, we settled on the following procedure:

  1. Create a PROD-specific file during the build stage. This file will be treated as a separate artifact. The goal is to have distinct artifacts for both the "build" and the "PROD angular configuration," such as:
      - publish: $(Build.artifactstagingdirectory)/Config
        displayName: 'Publish config'
        artifact: ConfigPROD

      - publish: $(Build.artifactstagingdirectory)/Build
        displayName: 'Publish build'
        artifact: BUILD
  1. After deploying the BUILD artifact to STAGING, retrieve the ConfigPROD artifact to STAGING via FTP. This will overwrite the existing STAGING configuration.
    - task: DownloadPipelineArtifact@2
      displayName: 'Download config artifact'
      inputs:
        buildType: 'specific'
        project: [your devops project name]
        definition: [your build definition name]
        artifactName: 'ConfigPROD'
        path: '$(Pipeline.Workspace)/config'

    - task: FtpUpload@2
      displayName: 'Deploy configPROD'
      inputs:
        credentialsOption: 'inputs'
        serverUrl: [your ftp server url]
        username: [your ftp username]
        password: [your ftp password]
        rootDirectory: '$(Pipeline.Workspace)/config'
        filePatterns: '**' 
        remoteDirectory: [your remote directory] 
        clean: false 
        cleanContents: false
  1. Execute the STAGING to PROD swap process.
    - task: AzureAppServiceManage@0
      displayName: 'Swap STAGING and PROD'
      inputs:
        azureSubscription: [your azure subscription]
        Action: 'Swap Slots'
        WebAppName: [your app name]
        ResourceGroupName: [your rg name]
        SourceSlot: 'staging'
  • If you are unsure how to locate your FTP credentials in Azure, follow this path: [AzurePortal]/[AppServicePage]/Deployment Center/Deployment Credentials

  • You can verify [your remote directory] using Kudu or the console under "Advanced Tools." For example, the Angular wwwroot folder resides within site/wwwroot, resulting in my remote directory being '/site/wwwroot/wwwroot'

While not a perfect solution, this method has proven to be effective without any additional downtime.

Thank you for your suggestions.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Having trouble grasping the concept of Interfaces and dealing with FormGroup problems in Angular?

Apologies if my question is a duplicate, I have found several solutions for the same issue on Stack Overflow, but unfortunately, I struggle to understand them in technical terms. Problem 1 src/app/models/dataModel.ts:2:5 2 id: number; ~~ The exp ...

Encountering an error while trying to run NPM install

I have attempted to uninstall and reinstall angular cli by using the following commands: sudo npm uninstall -g @angular/cli sudo npm install -g @angular/cli However, every time I run npm install, I encounter the following error: npm ERR! Unexpected toke ...

Challenges with promises in Angular Firebase Realtime DB

Having some difficulties with Angular and Firebase Realtime DB. In my database service, I created a function like this: getAllProject() { return firebase.database().ref('project').once('value').then((snapshot) => { if (snapsh ...

The distinctUntilChanged function encounters an issue when no comparison function is given

considering the following code: /** * Generating a stream of car objects from an array. */ from([ { name: 'Porsche', model: '911' }, { name: 'Porsche', model: '911' }, { name: 'Ferrari', model: &apo ...

When integrating the @azure/msal-angular import into the Angular application, the screen unexpectedly goes blank,

Starting a new Angular app and everything is rendering as expected at localhost:4200 until the following change is made: @NgModule({ declarations: [ AppComponent, HeaderBannerComponent, MainContentComponent, FooterContentinfoComponent ...

Sending detailed exception information from asp.net core to the client (such as Angular)

Trying to retrieve exception text from the backend (ASP.NET Core) in an Angular application has been a challenge. Examples have shown the controller action's return type as either JsonResult or ActionResult. In such cases, we can utilize the followi ...

Accessing JSON object from a URL via a web API using Angular 2 and TypeScript

`Hello, I am in need of some assistance in retrieving JSON data from a web API using Visual Studio 2015 .net Core, Angular 2 & Typescript. The Angular2 folders are located in /wwwroot/libs. Currently, I am utilizing Angular 2's http.get() method. Ho ...

Angular - Implementing *ngIf based on URL parameters

Is it possible to display an element based on specific queryParams included in the URL? For example: ngOnInit() { this.route.queryParams.subscribe(params => { console.log(params); }); } If I want to achieve something similar to this: ...

Updating color of an element in SVG and Angular2+ according to the background

In my svg element, I have a text element positioned after two rect elements. <svg id="floor-plan" width="300" height="100"> <rect width="300" height="100" fill="white"/> <rect width="50" height="50" fill="green"/> <text x="10" y="10" ...

Subscription Code Incrementally Triggering Upon Each Component Load

Within the initialization of my component, I have the following code: public Subscription: Subscription; ngOnInit() { this.subscription = this.myService.currentData.subscribe( dataReceived => { this.data = dataReceived; this.useDa ...

Top method for dynamically generating a recursive treeview from data fetched from an API

I am currently learning Angular 2 and working on creating an expandable tree-view that pulls data from a potentially large third-party API. The underlying structure of the API is structured like this: - Home (id: 1053) - - Rugby League (id: 1054) - - - Su ...

How to access the Parent ViewContainerRef within a projected child component in Angular 5

I have a unique application structure where the App component contains dynamically created components. The Parent component utilizes an <ng-content> element for projecting child components inside itself. App Component: @Component({ selector: &apo ...

Sort the elements within the *ngFor loop according to the category upon clicking the button in Angular

Currently, I have a collection of items that I am iterating through using *ngFor. Above this list, there are category buttons available as shown in the HTML snippet below. My goal is to enable filtering of the list based on the category of the button click ...

Exploring PrimeNG's method for expanding and collapsing groups

I'm attempting to incorporate two buttons that can be used to either expand or collapse all the groups in my code utilizing primeNG. Below is the functioning code: PLUNKER <p-dataTable [value]="data" sortField="room" rowGroupMode="subheader" grou ...

Is it necessary to manually unsubscribe from observables in the main Angular component?

I'm facing a dilemma with my Observable in the root Angular (6.x) component, AppComponent. Typically, I would unsubscribe from any open Subscription when calling destroy() using the lifecycle hook, ngOnDestroy. However, since the AppComponent serv ...

BrowserSync initiates without any access URLs specified

Recently, I set up a scaffolding project using Yeoman (ng-fullstack) and opted for the client side options only. The installation went smoothly, but when I try to run "gulp", all tasks are executed without any errors and it launches http://localhost:3000. ...

What is the best way to retrieve HTML content using an Angular method?

Okay, so the title might not be the greatest...but I couldn't think of anything better: I want to emphasize search keywords in the result list...that's why I'm having trouble with this problem. CSS: .highlightText{ font-weight: bold; } In ...

Transitioning from angular 7 to the latest version 12

Upgrading from Angular 7 to 12 has presented a series of issues for me. The main problem seems to be with Angular Material. I am looking for a solution to this. ./src/app/material.module.ts:13:89-110 - Encounter Error: 'MatAutocompleteModule' ( ...

Implementing JavaScript Code in TypeScript

Every JavaScript code should also be valid in TypeScript, but when attempting to run the following code snippet below, an error is triggered. Could someone convert this JavaScript code into TypeScript? Error: 20:9 - TS2531 Error: Object is possibly 'z ...

Migration of Angular dynamic forms project - The error "input" does not have an initializer or a constructor, and another issue with Type T | undefined

Angular dynamic forms project migration - encountering Type T | undefined error In my quest to find a sample project demonstrating the creation of Angular forms using JSON datasets, I stumbled upon this repository: https://github.com/dkreider/advanced-dyn ...