Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Azure DevOps Services
This tutorial shows you how to use Azure Pipelines for continuous integration and continuous delivery (CI/CD) to build and deploy a Python web app to Azure App Service on Linux. Your pipeline automatically builds and deploys your Python web app to App Service whenever there's a commit to your app code repository.
In this tutorial, you:
- Create a Python web app and upload it to Azure App Service.
- Connect your Azure DevOps project to Azure.
- Create an Azure Pipelines Python-specific build and deployment pipeline for your app.
- Run the pipeline to build, test, and deploy to your Azure App Service web app.
- Set a trigger to run the pipeline whenever you commit to your repository.
To understand more about Azure Pipelines concepts, watch the following video:
Prerequisites
Product | Requirements |
---|---|
Azure DevOps | - An Azure DevOps project. - An ability to run pipelines on Microsoft-hosted agents. You can either purchase a parallel job or you can request a free tier. - Basic knowledge of YAML and Azure Pipelines. For more information, see Create your first pipeline. - Permissions: - To create a pipeline: you must be in the Contributors group and the group needs to have Create build pipeline permission set to Allow. Members of the Project Administrators group can manage pipelines. - To create service connections: You must have the Administrator or Creator role for service connections. |
GitHub | - A GitHub account. - A GitHub service connection to authorize Azure Pipelines. |
Azure | An Azure subscription. |
Product | Requirements |
---|---|
Azure DevOps | - An Azure DevOps project. - A self-hosted agent. To create one, see Self-hosted agents. - Basic knowledge of YAML and Azure Pipelines. For more information, see Create your first pipeline. - Permissions: - To create a pipeline: you must be in the Contributors group and the group needs to have Create build pipeline permission set to Allow. Members of the Project Administrators group can manage pipelines. - To create service connections: You must have the Administrator or Creator role for service connections. |
GitHub | - A GitHub account. - A GitHub service connection to authorize Azure Pipelines. |
Azure | An Azure subscription. |
Configure a self-hosted agent
Downloading Python versions isn't supported on self-hosted build agents. To use your own self-hosted agent, you need to configure the agent to run Python.
To avoid compatibility issues, match the Python version with the runtime version in your Azure App Services web app, 3.11
in this case. You must preinstall the Python version. Use the full installer to get a pip-compatible version of Python.
The desired Python version needs to be added to the tool cache on the self-hosted agent so the pipeline task can use it. Normally, the tool cache is located under the _work/_tool directory of the agent. Alternatively, you can override the path with the environment variable AGENT_TOOLSDIRECTORY
. Under the tools directory, create the following directory structure based on your Python version:
$AGENT_TOOLSDIRECTORY/
Python/
{version number}/
{platform}/
{tool files}
{platform}.complete
The version number should follow the format of 1.2.3. The platform should either be x86 or x64. The tool files should be the unzipped Python version files. The {platform}.complete
should be a 0-byte file that looks like x86.complete
or x64.complete
and just signifies the tool is properly installed in the cache.
For example, to use Python 3.11 on a 64-bit Windows machine, create the following directory structure:
$AGENT_TOOLSDIRECTORY/
Python/
3.11.4/
x64/
{python files}
x64.complete
If the machine hosting your agent already has the Python version you want to use, you can copy the files to the tool cache. If you don't have the Python version, you can download it from the Python website.
Prepare the sample app
Fork the sample repository at https://github.com/Microsoft/python-sample-vscode-flask-tutorial to your GitHub account.
Clone your fork to your local machine by using
git clone <your-forked-repository-url>.git
.Go to your local clone by using
cd python-sample-vscode-flask-tutorial
, and build and run the app locally to make sure it works.python -m venv .env source .env/Scripts/activate pip install --upgrade pip pip install -r ./requirements.txt export FLASK_APP=hello_app.webapp flask run
To test the app, go to http://localhost:5000 in a browser window, and verify that you see the title Visual Studio Flask Tutorial.
Close the browser window and stop the Flask server by using Ctrl+C.
Create and deploy the App Service web app
Create your Azure App Service web app by using Cloud Shell in the Azure portal. To use Cloud Shell, sign in to the Azure portal and select the Cloud Shell button on the toolbar.
The Cloud Shell appears along the bottom of the browser. Make sure Bash is selected as the environment in the dropdown menu. You can maximize the Cloud Shell window to give yourself more room.
Tip
To paste into Cloud Shell, use Ctrl+Shift+V or right-click and select Paste from the context menu.
Create and deploy the web app
In Cloud Shell, clone your forked repository to Azure with the following command, replacing
<your-forked-repository-url>
with the URL of your forked repository.git clone <your-forked-repository-url>
Change directory to the cloned repository folder.
cd python-sample-vscode-flask-tutorial
Run the az webapp up command to provision the App Service web app and do the first deployment. Use the
--name <your-web-app-name>
parameter to assign a name that's unique across Azure, such as a personal or company name along with an app identifier, like--name <your-name>-flaskpipelines
. Runningaz webapp up
with no parameters assigns a randomly generated web app name that's unique in Azure.az webapp up --name <your-web-app-name>
The az webapp up
command recognizes the app as a Python app, and takes the following actions:
- Creates a default resource group.
- Creates a default App Service plan.
- Creates a web app with the assigned name. The app
URL
is<your-web-app-name>.azurewebsites.net
. - Deploys all files from the current working directory to a ZIP archive, with build automation enabled.
- Caches the parameters locally in the .azure/config file so you don't need to specify them again when deploying from the project folder with
az webapp up
or otheraz webapp
commands. The commands use the cached values automatically by default.
You can override the default actions with your own values by using the command parameters. For more information, see az webapp up.
The az webapp up
command produces the following JSON output for the sample web app:
{
"URL": <your-web-app-url>,
"appserviceplan": <your-app-service-plan-name>,
"location": <your-azure-region>,
"name": <your-web-app-name>,
"os": "Linux",
"resourcegroup": <your-resource-group>,
"runtime_version": "python|3.11",
"runtime_version_detected": "-",
"sku": <sku>,
"src_path": <repository-source-path>
}
Record the URL
, resourcegroup
, and runtime_version
values to use later in this tutorial.
Set the startup command
The python-sample-vscode-flask-tutorial app has a startup.txt file that contains the specific startup command for the web app. Set the web app startup-file
configuration property to startup.txt
by entering the following command, using your resource group and web app names.
az webapp config set --resource-group <your-resource-group> --name <your-web-app-name> --startup-file startup.txt
When the command completes, the JSON output shows all the configuration settings for your web app.
To see the running app, open a browser and go to the URL
shown in the az webapp up
command output. If you see a generic page, wait a few seconds for the App Service to start, then refresh the page. Verify that you see the title Visual Studio Flask Tutorial.
Connect your Azure DevOps project to your Azure subscription
To use Azure Pipelines to deploy to your Azure App Service web app, you need to connect your Azure DevOps project to your Azure resources.
Create a service principal
A service principal is an identity created for applications, hosted services, and automated tools to access Azure resources. This access is restricted to the roles assigned to the service principal, giving you control over which resources can be accessed at which level.
To create a service principal, run the following command in Bash Cloud Shell. Replace <service-principal-name>
with a name for your service principal, <your-subscription-id>
with your Azure subscription ID, and <your-resource-group>
with the resource group for the web app.
az ad sp create-for-rbac --display-name <service-principal-name> --role contributor --scopes /subscriptions/<your-subscription-id>/resourceGroups/<your-resource-group>
The command returns the following JSON object:
{
"appId": "<client GUID>",
"displayName": "<service-principal-name">,
"password": "<password-string>",
"tenant": "<tenant GUID>"
...
}
Make a note of the appId
, password
, and tenantId
values to use for creating a service connection in the next section.
Create a service connection
A service connection provides authenticated access from Azure Pipelines to external and remote services. To deploy to your Azure App Service web app, create a service connection to the resource group for your web app.
On your Azure DevOps project page, select Project settings.
From Project Settings, select Pipelines > Service connections.
On the Service connections page, select New service connection or Create service connection if this service connection is the first in the project.
On the New service connection screen, select Azure Resource Manager and then select Next.
On the New Azure service connection screen, select your Identity type. This example uses App registration (automatic), which is recommended. For more information about authentication methods, see Connect to Azure by using an Azure Resource Manager service connection.
For Credential, select Workload identity federation (automatic).
Complete the following fields:
- Scope level: Select Subscription.
- Subscription: Select your Azure subscription.
- Resource group: Select the resource group that contains your web app.
- Service Connection Name: Enter a descriptive name for the connection.
- Grant access permissions to all pipelines: Select this check box to grant access to all pipelines in the project.
Select Save.
On your Azure DevOps project page, select Project settings.
From Project Settings, select Pipelines > Service connections.
On the Service connections page, select New service connection or Create service connection if this service connection is the first in the project.
On the New service connection screen, select Azure Resource Manager and then select Next.
Select Service principal (manual) and then select Next.
On the New Azure service connection screen, complete the following fields:
- Environment: Select Azure Cloud.
- Scope Level: Select Subscription.
- Subscription Id: Enter your Azure subscription ID.
- Subscription Name: Enter your Azure subscription name.
In the Authentication section, complete the following fields:
- Service Principal Id: Enter the
appId
value returned by theaz ad sp create-for-rbac
command. - Credential: Select Service principal key.
- Service principal key: Enter the
password
value returned by theaz ad sp create-for-rbac
command. - Tenant Id: Enter the
tenant
value returned by theaz ad sp create-for-rbac
command. - Select Verify to verify the connection.
- Service Principal Id: Enter the
In the Details section, under Service Connection Name, enter a name for the service connection.
Select the check box for Grant access permissions to all pipelines.
Select Verify and save.
The new connection appears in the Service connections list, and is ready to use in your pipeline.
Create a pipeline
Create a pipeline to build and deploy your Python web app to Azure App Service.
On the left navigation menu for your project, select Pipelines.
On the Pipelines page, select New pipeline, or Create pipeline if this pipeline is the first in the project.
On the Where is your code screen, select GitHub. You might be prompted to sign into GitHub.
On the Select a repository screen, select your forked sample repository. GitHub might prompt you to enter your GitHub password again, or to install the Azure Pipelines GitHub app. Follow onscreen instructions to install the app. For more information, see GitHub app authentication.
On the Configure your pipeline page, select Python to Linux Web App on Azure.
On the next screen, select your Azure subscription and select Continue.
On the next screen, select your Azure web app and select Validate and configure.
Azure Pipelines creates an azure-pipelines.yml file and displays it in the YAML pipeline editor.
On the left navigation menu for your project, select Pipelines.
On the Pipelines page, select New pipeline, or Create pipeline if this pipeline is the first in the project.
On the Where is your code page, select GitHub Enterprise Server. You might be prompted to sign into GitHub.
On the Select a repository tab, select your forked sample repository. GitHub might prompt you to enter your GitHub password again or install the Azure Pipelines GitHub extension or app. Follow onscreen instructions to install the app. For more information, see Access to GitHub repositories.
On the Configure your pipeline page, select Starter pipeline.
On the Review your pipeline YAML page, replace the contents of the starter azure-pipelines.yml file with the following YAML pipeline file. In the YAML file:
Replace the
<your-service-connection-name>
and<your-web-app-name>
placeholders with your own values.Replace
<your-pool-name>
with the name of the agent pool you want to use, and replace<your-python-version>
with the version of Python running on your agent. This version should match theruntime_version
from the JSON output of theaz webapp up
command.
YAML pipeline file
On the Review your pipeline YAML page, look at the pipeline to see what it does. Make sure all the default inputs are appropriate for your code. To learn about the pipeline YAML file schema, see the YAML schema reference.
The following complete example YAML pipeline file defines your CI/CD pipeline as a series of stages, jobs, and steps, where each step contains the details for different tasks and scripts. The generated YAML code automatically populates the placeholders with values for your app and connection.
trigger:
- main
variables:
# Azure Resource Manager connection created during pipeline creation
azureServiceConnectionId: '<GUID>'
# Web app name
webAppName: '<your-webapp-name>'
# Agent VM image name
vmImageName: 'ubuntu-latest'
# Environment name
environmentName: '<your-webapp-name>'
# Project root folder. Point to the folder containing manage.py file.
projectRoot: $(System.DefaultWorkingDirectory)
pythonVersion: '3.11'
stages:
- stage: Build
displayName: Build stage
jobs:
- job: BuildJob
pool:
vmImage: $(vmImageName)
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
displayName: 'Use Python $(pythonVersion)'
- script: |
python -m venv antenv
source antenv/bin/activate
python -m pip install --upgrade pip
pip install setuptools
pip install -r requirements.txt
workingDirectory: $(projectRoot)
displayName: "Install requirements"
- task: ArchiveFiles@2
displayName: 'Archive files'
inputs:
rootFolderOrFile: '$(projectRoot)'
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
replaceExistingArchive: true
- upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
displayName: 'Upload package'
artifact: drop
- stage: Deploy
displayName: 'Deploy Web App'
dependsOn: Build
condition: succeeded()
jobs:
- deployment: DeploymentJob
pool:
vmImage: $(vmImageName)
environment: $(environmentName)
strategy:
runOnce:
deploy:
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
displayName: 'Use Python version'
- task: AzureWebApp@1
displayName: 'Deploy Azure Web App : $(webAppName)'
inputs:
azureSubscription: $(azureServiceConnectionId)
appName: $(webAppName)
package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
trigger:
- main
variables:
# Azure Resource Manager connection created during pipeline creation
azureServiceConnectionId: '<your-service-connection-name>'
# Web app name
webAppName: '<your-web-app-name>'
# Environment name
environmentName: '<your-web-app-name>'
# Project root folder.
projectRoot: $(System.DefaultWorkingDirectory)
# Python version:
pythonVersion: '<your-python-version>'
stages:
- stage: Build
displayName: Build stage
jobs:
- job: BuildJob
pool:
name: '<your-pool-name>'
demands: python
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
displayName: 'Use Python $(pythonVersion)'
- script: |
python -m venv antenv
source antenv/bin/activate
python -m pip install --upgrade pip
pip install -r requirements.txt
workingDirectory: $(projectRoot)
displayName: "Install requirements"
- task: ArchiveFiles@2
displayName: 'Archive files'
inputs:
rootFolderOrFile: '$(projectRoot)'
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
replaceExistingArchive: true
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'Container'
- stage: Deploy
displayName: 'Deploy Web App'
dependsOn: Build
condition: succeeded()
jobs:
- deployment: DeploymentJob
pool:
name: '<your-pool-name'
environment: $(environmentName)
strategy:
runOnce:
deploy:
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
displayName: 'Use Python version'
- task: AzureWebApp@1
displayName: 'Deploy Azure Web App : <your-web-app-name>'
inputs:
azureSubscription: $(azureServiceConnectionId)
appName: $(webAppName)
package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
startUpCommand: 'startup.txt'
Variables
The variables
section at the beginning of the YAML file defines the following variables:
Variable | Description |
---|---|
azureServiceConnectionId |
The ID of the Azure Resource Manager service connection. |
webAppName |
The name of the App Service web app. |
vmImageName |
The name of the operating system to use for the build agent. |
environmentName |
The name of the environment to deploy to, which is automatically created when the deployment job runs. |
projectRoot |
The root folder containing the app code. |
pythonVersion |
The version of Python to use on the build and deployment agents. |
Variable | Description |
---|---|
azureServiceConnectionId |
The ID of the Azure Resource Manager service connection. |
webAppName |
The name of the App Service web app. |
environmentName |
The name of the environment to deploy to, which is automatically created when the deployment job runs. |
projectRoot |
The folder containing the app code. The value is an automatic system variable. |
pythonVersion |
The version of Python to use on the build and deployment agents. |
Build and deployment stages
The pipeline consists of build and deployment stages.
Build stage
The build stage contains a single job that runs on the operating system defined in the vmImageName
variable, in this case ubuntu-latest
.
- job: BuildJob
pool:
vmImage: $(vmImageName)
The build stage contains a single job that runs on an agent in the pool
identified by the name
parameter.
You can specify the agent capabilities with the demands
keyword. For example, demands: python
specifies that the agent must have Python installed. To specify a self-hosted agent by name, you can use demands: Agent.Name -equals <agent-name>
.
- job: BuildJob
pool:
name: <your-pool-name>
demands: python
The job contains multiple steps:
First, the UsePythonVersion task selects the version of Python to use, as defined in the
pythonVersion
variable.- task: UsePythonVersion@0 inputs: versionSpec: '$(pythonVersion)' displayName: 'Use Python $(pythonVersion)'
The next step uses a script that creates a virtual Python environment and installs the app's dependencies from
requirements.txt
. TheworkingDirectory
parameter specifies the location of the app code.- script: | python -m venv antenv source antenv/bin/activate python -m pip install --upgrade pip pip install setuptools pip install -r ./requirements.txt workingDirectory: $(projectRoot) displayName: "Install requirements"
The ArchiveFiles task creates a ZIP archive that contains the built web app.
- task: ArchiveFiles@2 displayName: 'Archive files' inputs: rootFolderOrFile: '$(projectRoot)' includeRootFolder: false archiveType: zip archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip replaceExistingArchive: true
The parameters are set as follows:
Parameter Description rootFolderOrFile
The location of the app code. includeRootFolder
Whether to include the root folder in the .zip file. Set to false
. If set totrue
, the contents of the .zip file are put in a folder named s and the task can't find the app code.archiveType
The type of archive to create. Set to zip
.archiveFile
The location of the .zip file to create. replaceExistingArchive
Indicates whether to replace an existing archive if the file already exists. Set to true
.The
.zip
file then uploads to the pipeline as an artifact nameddrop
. The deployment stage uses the .zip file to deploy the app.- upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip displayName: 'Upload package' artifact: drop
- The
upload
parameter sets the location and name of the .zip file to upload. - The
artifact
parameter sets the name of the created artifact todrop
.
- The
Deployment stage
The deployment stage runs if the build stage completes successfully. The dependsOn
and condition
keywords define this behavior.
dependsOn: Build
condition: succeeded()
The deployment stage contains a single deployment job configured as follows.
- deployment: DeploymentJob
pool:
vmImage: $(vmImageName)
environment: $(environmentName)
The
deployment
keyword indicates that the job is a deployment job targeting an environment to deploy to. Theenvironment
is automatically created in your project when the job is run.The
pool
parameter specifies the deployment agent pool and uses the default agent pool if aname
isn't specified. The agent runs on the operating system defined in thevmImageName
variable, in this caseubuntu-latest
.
- deployment: DeploymentJob
pool:
name: <your-pool-name>
environment: $(environmentName)
The
deployment
keyword indicates that the job is a deployment job targeting an environment to deploy to. Theenvironment
is automatically created in your project when the job is run.The
pool
parameter specifies the deployment agent pool and must contain an agent with the capability to run the Python version specified in the pipeline.
The strategy
keyword defines the deployment strategy.
strategy:
runOnce:
deploy:
steps:
- The
runOnce
keyword specifies that the deployment job runs once. - The
deploy
keyword specifies thesteps
to run in the deployment job.
The steps
in this stage run the following tasks:
- UsePythonVersion@0 selects the version of Python to use, same as in the Build stage.
- AzureWebApp@1 deploys the web app and the
drop
ZIP artifact.
- task: AzureWebApp@1
displayName: 'Deploy Azure Web App : <your-web-app-name>'
inputs:
azureSubscription: $(azureServiceConnectionId)
appName: $(webAppName)
package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
- The
azureSubscription
parameter contains theazureServiceConnectionId
specified in the pipeline variables. - The
appName
contains the value of thewebAppName
variable. - The
package
specifies the name and location of the .zip file to deploy.
Also, because the python-vscode-flask-tutorial repository contains the app startup command in a file named startup.txt, you can specify the app startup command by adding the parameter: startUpCommand: 'startup.txt'
.
Run the pipeline
You're now ready to try out the pipeline.
In the pipeline editor, select Save and run.
On the Save and run screen, add a commit message if desired and then select Save and run.
You can watch the pipeline run by selecting the Stages or Jobs on the pipeline Summary page. Each job and stage displays a green check mark as it completes successfully. If errors occur, they appear in the summary or in the job steps.
You can quickly return to the YAML editor by selecting the vertical dots at the upper right on the Summary page and selecting Edit pipeline.
From the deployment job, select the Deploy Azure Web App task to display its output.
From the output, select the URL after App Service Application URL. The app should appear as follows:
Note
If an app deployment fails because of a missing dependency, the requirements.txt file wasn't processed during deployment. This issue can occur if you create the web app directly in the portal rather than using the az webapp up
command.
The az webapp up
command specifically sets the build action SCM_DO_BUILD_DURING_DEPLOYMENT
to true
. If you provision an app service through the portal, this action isn't automatically set.
To set this action:
- On the portal page for your web app, select Configuration from the left navigation menu.
- On the Application Settings tab, select New Application Setting.
- In the popup that appears, set Name to
SCM_DO_BUILD_DURING_DEPLOYMENT
, set Value totrue
, and select OK. - Select Save at the top of the Configuration page.
- Run the pipeline again. The dependencies should now install during deployment.
Trigger a pipeline run
This pipeline is set to run whenever a change checks in to the code repository. To trigger a pipeline run, commit a change to the repository. For example, you can add a new feature to the app, or update the app's dependencies.
- Go to the GitHub repository for your app.
- Make a change to the code, such as changing the title of the app.
- Commit the change.
- Go to your pipeline and verify that a new run is created and is running.
- When the run completes, verify that the change deployed to your web app.
- In the Azure portal, go to your web app and select Deployment Center from the left navigation menu.
- Select the Logs tab and verify that the new deployment is listed.
Deploy Django apps to App Service
You can use Azure Pipelines to deploy Django apps to App Service on Linux if you're using a separate database. You can't use a SQLite database, because App Service locks the db.sqlite3 file, preventing both reads and writes. This behavior doesn't affect external databases.
As explained in Container startup process, App Service automatically looks for a wsgi.py file in your app code, which typically contains the app object. If you want to customize the startup command, use the startUpCommand
parameter in the AzureWebApp@1
step of your YAML pipeline file.
When you use Django, you typically want to migrate the data models using manage.py migrate
after deploying the app code. You can add startUpCommand
with a post-deployment script for this purpose. For example, here's the startUpCommand
property in the AzureWebApp@1 task.
- task: AzureWebApp@1
displayName: 'Deploy Azure Web App : $(webAppName)'
inputs:
azureSubscription: $(azureServiceConnectionId)
appName: $(webAppName)
package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
startUpCommand: 'python manage.py migrate'
Run tests on the build agent
As part of your build process, you might want to run tests on your app code. Tests run on the build agent, so you need to install your dependencies into a virtual environment on the build agent. After the tests run, delete the test virtual environment before you create the .zip file for deployment.
The following script elements illustrate this process. Place them before the ArchiveFiles@2
task in the azure-pipelines.yml file. For more information, see Run cross-platform scripts.
# The | symbol is a continuation character, indicating a multi-line script.
# A single-line script can immediately follow "- script:".
- script: |
python -m venv .env
source .env/bin/activate
pip install setuptools
pip install -r requirements.txt
# The displayName shows in the pipeline UI when a build runs
displayName: 'Install dependencies on build agent'
- script: |
# Put commands to run tests here
displayName: 'Run tests'
- script: |
echo Deleting .env
deactivate
rm -rf .env
displayName: 'Remove .env before zip'
You can also use a task like PublishTestResults@2 to publish the test results to your pipeline. For more information, see Run tests.
Clean up resources
If you're finished with the Azure resources you created in this tutorial, delete them to avoid incurring further charges.
- Delete the Azure DevOps project you created. Deleting the project deletes the pipeline and service connection.
- Delete the Azure resource group that contains the App Service and the App Service Plan. In the Azure portal, go to the resource group, select Delete resource group, and follow the prompts.
- Delete the Azure storage account that maintains the Cloud Shell file system. Close Cloud Shell, then find the resource group that begins with cloud-shell-storage-. Select Delete resource group, and follow the prompts.