DEV Community

Cover image for Migrate Public Endpoint Web App Between App Service Plans using Devops
Arindam Mitra
Arindam Mitra

Posted on

Migrate Public Endpoint Web App Between App Service Plans using Devops

Greetings to my fellow Technology Advocates and Specialists.

In this Session, I will demonstrate how to Migrate Public Endpoint Web App Between App Service Plans.

AUTOMATION OBJECTIVES:-
# TOPICS
1. Validate if Resource Group exists.
2. Validate if Source App Service Plan Exists.
3. Validate if Destination App Service Plan Exists.
4. Validate if App Service Exists.
5. Validate Webspace.
6. If all the above validation is successful, Web App will then be migrated to Destination App Service Plan.
IMPORTANT NOTE:-

The YAML Pipeline is tested on WINDOWS BUILD AGENT Only!!!

REQUIREMENTS:-
  1. Azure Subscription.
  2. Azure DevOps Organisation and Project.
  3. Service Principal with Required RBAC ( Contributor) applied on Subscription or Resource Group(s).
  4. Azure Resource Manager Service Connection in Azure DevOps.
  5. One Demo Resource Group.
  6. One Source App Service Plan.
  7. One Destination App Service Plan.
  8. One Web App with Public Endpoint.
LIST OF AZURE RESOURCES DEPLOYED FOR THIS AUTOMATION:-
Azure Resources: 1) Source App Service Plan, 2) Destination App Service Plan, and 3) Web App.
Image description
Web App is running on Source App Service Plan.
Image description
There is no Web App running on Destination App Service Plan.
Image description
CODE REPOSITORY:-

GitHub logo arindam0310018 / 29-Mar-2023-DevOps__Migrate-WebApp-Between-AppService-Plans

Migrate Public Endpoint Web App Between App Service Plans using Devops

Migrate Public Endpoint Web App Between App Service Plans using Devops

Greetings to my fellow Technology Advocates and Specialists.

In this Session, I will demonstrate how to Migrate Public Endpoint Web App Between App Service Plans.

AUTOMATION OBJECTIVES:-
# TOPICS
1. Validate if Resource Group exists.
2. Validate if Source App Service Plan Exists.
3. Validate if Destination App Service Plan Exists.
4. Validate if App Service Exists.
5. Validate Webspace.
6. If all the above validation is successful, Web App will then be migrated to Destination App Service Plan.
IMPORTANT NOTE:-
The YAML Pipeline is tested on WINDOWS BUILD AGENT Only!!!
REQUIREMENTS:-
  1. Azure Subscription.
  2. Azure DevOps Organisation and Project.
  3. Service Principal with Required RBAC ( Contributor) applied on Subscription or Resource Group(s).
  4. Azure Resource Manager Service Connection in Azure DevOps.
  5. One Demo Resource Group.
  6. One Source App Service Plan.
  7. One Destination App Service Plan.
  8. One Web App…
HOW DOES MY CODE PLACEHOLDER LOOKS LIKE:-
Image description
PIPELINE CODE SNIPPET:-
AZURE DEVOPS YAML PIPELINE (azure-pipelines-migrate-publicendpoint-webapp-between-AppService-Plans-v1.0.yml):-
trigger:
  none

######################
#DECLARE PARAMETERS:-
######################
parameters:
- name: SubscriptionID
  displayName: Subscription ID details follows below:-
  type: string
  default: 210e66cb-55cf-424e-8daa-6cad804ab604
  values:
  - 210e66cb-55cf-424e-8daa-6cad804ab604

- name: RGName
  displayName: Please Provide the Resource Group Name:-
  type: object
  default: 

- name: AppServiceSourcePlanName
  displayName: Please Provide the Source App Service Plan Name:-
  type: object
  default: 

- name: AppServiceDestinationPlanName
  displayName: Please Provide the Destination App Service Plan Name:-
  type: object
  default:

- name: WebAppName
  displayName: Please Provide the App Service Name:-
  type: object
  default:

######################
#DECLARE VARIABLES:-
######################
variables:
  ServiceConnection: amcloud-cicd-service-connection
  BuildAgent: windows-latest
  envName: NonProd

#########################
# Declare Build Agents:-
#########################
pool:
  vmImage: $(BuildAgent)

###################
# Declare Stages:-
###################

stages:

- stage: Validate_ResourceGroup_AppServicePlans_WebApp
  jobs:
  - job: Validate_ResourceGroup_AppServicePlans_WebApp 
    displayName: Validate Resource Group App Service Plans and WebApp
    steps:
    - task: AzureCLI@2
      displayName: Set Azure Account
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          az --version
          az account set --subscription ${{ parameters.SubscriptionID }}
          az account show  
    - task: AzureCLI@2
      displayName: Validate Resource Group
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $i = az group exists -n ${{ parameters.RGName }}
            if ($i -eq "true") {
              echo "#####################################################"
              echo "Resource Group ${{ parameters.RGName }} exists!!!"
              echo "#####################################################"              
            }
            else {
              echo "#############################################################"
              echo "Resource Group ${{ parameters.RGName }} DOES NOT exists!!!"
              echo "#############################################################"
              exit 1
            }
    - task: AzureCLI@2
      displayName: Validate Source App Service Plan
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $j = az appservice plan list --query "[?name=='${{ parameters.AppServiceSourcePlanName }}'].name" -o tsv
            if ($j -eq "${{ parameters.AppServiceSourcePlanName }}") {
              echo "##############################################################################"
              echo "Source App Service Plan ${{ parameters.AppServiceSourcePlanName }} exists!!!"
              echo "##############################################################################"              
            }
            else {
               echo "#######################################################################################"
              echo "Source App Service Plan ${{ parameters.AppServiceSourcePlanName }} DOES NOT exists!!!"
              echo "########################################################################################"
              exit 1
            }
    - task: AzureCLI@2
      displayName: Validate Destination App Service Plan
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $k = az appservice plan list --query "[?name=='${{ parameters.AppServiceDestinationPlanName }}'].name" -o tsv
            if ($k -eq "${{ parameters.AppServiceDestinationPlanName }}") {
              echo "#########################################################################################"
              echo "Destination App Service Plan ${{ parameters.AppServiceDestinationPlanName }} exists!!!"
              echo "#########################################################################################"              
            }
            else {
               echo "#################################################################################################"
              echo "Destination App Service Plan ${{ parameters.AppServiceDestinationPlanName }} DOES NOT exists!!!"
              echo "##################################################################################################"
              exit 1
            }        
    - task: AzureCLI@2
      displayName: Validate Web App
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $l = az webapp list --query "[?name=='${{ parameters.WebAppName }}'].name" -o tsv
            if ($l -eq "${{ parameters.WebAppName }}") {
              echo "#####################################################"
              echo "Web App ${{ parameters.WebAppName }} exists!!!"
              echo "#####################################################"              
            }
            else {
               echo "#############################################################"
              echo "Web App ${{ parameters.WebAppName }} DOES NOT exists!!!"
              echo "##############################################################"
              exit 1
            }
    - task: AzureCLI@2
      displayName: Validate Webspace
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $srcplanwebspace = az appservice plan show --name ${{ parameters.AppServiceSourcePlanName }} --resource-group ${{ parameters.RGName }} --query ["properties.webSpace"] -o tsv
          $destplanwebspace = az appservice plan show --name ${{ parameters.AppServiceDestinationPlanName }} --resource-group ${{ parameters.RGName }} --query ["properties.webSpace"] -o tsv
            if ($srcplanwebspace -eq $destplanwebspace) {
              echo "################################################"
              echo "Webspace of both App Service Plans matches!!!"
              echo "################################################"              
            }
            else {
              echo "##############################################################################################################################"
              echo "Webspace of both App Service Plans DOES NOT match and hence App Service ${{ parameters.WebAppName }} cannot be Migrated !!!"
              echo "##############################################################################################################################"
              exit 1
            }

- stage: Migrate_WebApp_Between_AppServicePlans
  condition: |
     and(succeeded(), 
       eq(variables['build.sourceBranch'], 'refs/heads/main')
     )
  jobs:
  - deployment: 
    displayName: Migrate WebApp Between App Service Plans
    environment: '$(envName)'
    pool:
      vmImage: $(BuildAgent)
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureCLI@2
            displayName: Set Azure Account
            inputs:
              azureSubscription: $(ServiceConnection)
              scriptType: ps
              scriptLocation: inlineScript
              inlineScript: |
                az --version
                az account set --subscription ${{ parameters.SubscriptionID }}
                az account show
          - task: AzureCLI@2
            displayName: Migrate WebApp 
            inputs:
              azureSubscription: $(ServiceConnection)
              scriptType: ps
              scriptLocation: inlineScript
              inlineScript: |
                az webapp update -g ${{ parameters.RGName }} -n ${{ parameters.WebAppName }} --set serverFarmId=/subscriptions/${{ parameters.SubscriptionID }}/resourceGroups/${{ parameters.RGName }}/providers/Microsoft.Web/serverfarms/${{ parameters.AppServiceDestinationPlanName }}
                echo "###########################################################################################################################################################################################################"
                echo "WebApp ${{ parameters.WebAppName }} migrated from Source App Service Plan ${{ parameters.AppServiceSourcePlanName }} to Destination App Service Plan ${{ parameters.AppServiceDestinationPlanName }}!!!"
                echo "###########################################################################################################################################################################################################"
Enter fullscreen mode Exit fullscreen mode

Now, let me explain each part of YAML Pipeline for better understanding.

PART #1:-
BELOW FOLLOWS PIPELINE RUNTIME VARIABLES CODE SNIPPET:-
######################
#DECLARE PARAMETERS:-
######################
parameters:
- name: SubscriptionID
  displayName: Subscription ID details follows below:-
  type: string
  default: 210e66cb-55cf-424e-8daa-6cad804ab604
  values:
  - 210e66cb-55cf-424e-8daa-6cad804ab604

- name: RGName
  displayName: Please Provide the Resource Group Name:-
  type: object
  default: 

- name: AppServiceSourcePlanName
  displayName: Please Provide the Source App Service Plan Name:-
  type: object
  default: 

- name: AppServiceDestinationPlanName
  displayName: Please Provide the Destination App Service Plan Name:-
  type: object
  default:

- name: WebAppName
  displayName: Please Provide the App Service Name:-
  type: object
  default:
Enter fullscreen mode Exit fullscreen mode
THIS IS HOW IT LOOKS:-
Image description
PART #2:-
BELOW FOLLOWS PIPELINE VARIABLES CODE SNIPPET:-
######################
#DECLARE VARIABLES:-
######################
variables:
  ServiceConnection: amcloud-cicd-service-connection
  BuildAgent: windows-latest
  envName: NonProd
Enter fullscreen mode Exit fullscreen mode
NOTE:-
Please change the values of the variables accordingly.
The entire YAML pipeline is build using Runtime Parameters and Variables. No Values are Hardcoded.
PART #3:-
This is a 2 Stage Pipeline.
Stage #1 of the Pipeline "Validate_ResourceGroup_AppServicePlans_WebApp" has 6 Pipeline Tasks.
Stage #2 of the Pipeline "Migrate_WebApp_Between_AppServicePlans" has 2 Pipeline Tasks.
STAGE #1 - PIPELINE TASK #1:-
Set Azure Account.
- stage: Validate_ResourceGroup_AppServicePlans_WebApp
  jobs:
  - job: Validate_ResourceGroup_AppServicePlans_WebApp 
    displayName: Validate Resource Group App Service Plans and WebApp
    steps:
    - task: AzureCLI@2
      displayName: Set Azure Account
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          az --version
          az account set --subscription ${{ parameters.SubscriptionID }}
          az account show
Enter fullscreen mode Exit fullscreen mode
STAGE #1 - PIPELINE TASK #2:-
Validate if Resource Group exists. If not, Pipeline will fail.
- task: AzureCLI@2
      displayName: Validate Resource Group
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $i = az group exists -n ${{ parameters.RGName }}
            if ($i -eq "true") {
              echo "#####################################################"
              echo "Resource Group ${{ parameters.RGName }} exists!!!"
              echo "#####################################################"              
            }
            else {
              echo "#############################################################"
              echo "Resource Group ${{ parameters.RGName }} DOES NOT exists!!!"
              echo "#############################################################"
              exit 1
            }
Enter fullscreen mode Exit fullscreen mode
STAGE #1 - PIPELINE TASK #3:-
Validate if Source App Service Plan exists. If not, Pipeline will fail.
- task: AzureCLI@2
      displayName: Validate Source App Service Plan
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $j = az appservice plan list --query "[?name=='${{ parameters.AppServiceSourcePlanName }}'].name" -o tsv
            if ($j -eq "${{ parameters.AppServiceSourcePlanName }}") {
              echo "##############################################################################"
              echo "Source App Service Plan ${{ parameters.AppServiceSourcePlanName }} exists!!!"
              echo "##############################################################################"              
            }
            else {
               echo "#######################################################################################"
              echo "Source App Service Plan ${{ parameters.AppServiceSourcePlanName }} DOES NOT exists!!!"
              echo "########################################################################################"
              exit 1
            }
Enter fullscreen mode Exit fullscreen mode
STAGE #1 - PIPELINE TASK #4:-
Validate if Destination App Service Plan exists. If not, Pipeline will fail.
- task: AzureCLI@2
      displayName: Validate Destination App Service Plan
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $k = az appservice plan list --query "[?name=='${{ parameters.AppServiceDestinationPlanName }}'].name" -o tsv
            if ($k -eq "${{ parameters.AppServiceDestinationPlanName }}") {
              echo "#########################################################################################"
              echo "Destination App Service Plan ${{ parameters.AppServiceDestinationPlanName }} exists!!!"
              echo "#########################################################################################"              
            }
            else {
               echo "#################################################################################################"
              echo "Destination App Service Plan ${{ parameters.AppServiceDestinationPlanName }} DOES NOT exists!!!"
              echo "##################################################################################################"
              exit 1
            } 
Enter fullscreen mode Exit fullscreen mode
STAGE #1 - PIPELINE TASK #5:-
Validate if Web App exists. If not, Pipeline will fail.
- task: AzureCLI@2
      displayName: Validate Web App
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $l = az webapp list --query "[?name=='${{ parameters.WebAppName }}'].name" -o tsv
            if ($l -eq "${{ parameters.WebAppName }}") {
              echo "#####################################################"
              echo "Web App ${{ parameters.WebAppName }} exists!!!"
              echo "#####################################################"              
            }
            else {
               echo "#############################################################"
              echo "Web App ${{ parameters.WebAppName }} DOES NOT exists!!!"
              echo "##############################################################"
              exit 1
            }
Enter fullscreen mode Exit fullscreen mode
STAGE #1 - PIPELINE TASK #6:-
Validate Webspace. If not, Pipeline will fail.
- task: AzureCLI@2
      displayName: Validate Webspace
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $srcplanwebspace = az appservice plan show --name ${{ parameters.AppServiceSourcePlanName }} --resource-group ${{ parameters.RGName }} --query ["properties.webSpace"] -o tsv
          $destplanwebspace = az appservice plan show --name ${{ parameters.AppServiceDestinationPlanName }} --resource-group ${{ parameters.RGName }} --query ["properties.webSpace"] -o tsv
            if ($srcplanwebspace -eq $destplanwebspace) {
              echo "################################################"
              echo "Webspace of both App Service Plans matches!!!"
              echo "################################################"              
            }
            else {
              echo "##############################################################################################################################"
              echo "Webspace of both App Service Plans DOES NOT match and hence App Service ${{ parameters.WebAppName }} cannot be Migrated !!!"
              echo "##############################################################################################################################"
              exit 1
            }
Enter fullscreen mode Exit fullscreen mode
IMPORTANT TO NOTE: WHY WE NEED TO VALIDATE WEBSPACE ?
Validating webspace ensures whether we can migrate the Web App between the mentioned App Service Plans (Source and Destination).
Azure deploys each new App Service plan into a deployment unit, internally called a webspace. Each region can have many webspaces, but your app can only move between plans that are created in the same webspace.
It is not possible to specify the webspace when we are creating a plan, but it’s possible to ensure that a plan is created in the same webspace as an existing plan. In brief, all plans created with the same resource group, region and operating system are deployed into the same webspace.
Webspace Information of Source App Service Plan.
Image description
Image description
Webspace Information of Destination App Service Plan.
Image description
Image description
STAGE #2 - PIPELINE TASK #1:-
Stage #2 will only execute if both the below conditions are met.
Condition 1: If the Previous Stage executed successfully.
Condition 2: If the Source Branch from where the Pipeline is executed is the "Main" Branch.
Once both the conditions are, Stage #2 will start executing but will wait for Pipeline Approval first.
Image description
Image description
Image description
Post Pipeline Approval, Stage #2, Pipeline Task # will execute which is "Settings Azure Account"
- stage: Migrate_WebApp_Between_AppServicePlans
  condition: |
     and(succeeded(), 
       eq(variables['build.sourceBranch'], 'refs/heads/main')
     )
  jobs:
  - deployment: 
    displayName: Migrate WebApp Between App Service Plans
    environment: '$(envName)'
    pool:
      vmImage: $(BuildAgent)
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureCLI@2
            displayName: Set Azure Account
            inputs:
              azureSubscription: $(ServiceConnection)
              scriptType: ps
              scriptLocation: inlineScript
              inlineScript: |
                az --version
                az account set --subscription ${{ parameters.SubscriptionID }}
                az account show
Enter fullscreen mode Exit fullscreen mode
STAGE #2 - PIPELINE TASK #2:-
Migrate Web App from Source to Destination App Service Plan.
The "serverFarmId" parameter in "az webapp update" command was found in the "Web App Resource JSON".
Image description
Image description
- task: AzureCLI@2
            displayName: Migrate WebApp 
            inputs:
              azureSubscription: $(ServiceConnection)
              scriptType: ps
              scriptLocation: inlineScript
              inlineScript: |
                az webapp update -g ${{ parameters.RGName }} -n ${{ parameters.WebAppName }} --set serverFarmId=/subscriptions/${{ parameters.SubscriptionID }}/resourceGroups/${{ parameters.RGName }}/providers/Microsoft.Web/serverfarms/${{ parameters.AppServiceDestinationPlanName }}
                echo "###########################################################################################################################################################################################################"
                echo "WebApp ${{ parameters.WebAppName }} migrated from Source App Service Plan ${{ parameters.AppServiceSourcePlanName }} to Destination App Service Plan ${{ parameters.AppServiceDestinationPlanName }}!!!"
                echo "###########################################################################################################################################################################################################"
Enter fullscreen mode Exit fullscreen mode

NOW ITS TIME TO TEST!!!

TEST CASES:-
1. Pipeline executed successfully.
Image description
2. Web App is successfully migrated to Destination App Service Plan.
Image description
Image description
3. Source App Service Plan is now empty.
Image description

Hope You Enjoyed the Session!!!

Stay Safe | Keep Learning | Spread Knowledge

Top comments (0)