This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can trysigning in orchanging directories.
Access to this page requires authorization. You can trychanging directories.
In this tutorial, you'll deploy a data-driven Python web app (Django) toAzure App Service with theAzure Database for PostgreSQL relational database service. Azure App Service supportsPython in a Linux server environment. If you want, see theFlask tutorial or theFastAPI tutorial instead.
In this tutorial, you learn how to:
If you just want to see the sample app in this tutorial running in Azure, just run the following commands in theAzure Cloud Shell, and follow the prompt:
mkdir msdocs-django-postgresql-sample-appcd msdocs-django-postgresql-sample-appazd init --template msdocs-django-postgresql-sample-appazd up
First, you set up a sample data-driven app as a starting point. For your convenience, thesample repository, includes adev container configuration. The dev container has everything you need to develop an application, including the database, cache, and all environment variables needed by the sample application. The dev container can run in aGitHub codespace, which means you can run the sample on any computer with a web browser.
Note
If you are following along with this tutorial with your own app, look at therequirements.txt file description inREADME.md to see what packages you'll need.
Step 1: In a new browser window:
Step 2: In the GitHub fork:
pip install -r requirements.txt
for your repository at the end. Also, the provided.env file already contains a dummySECRET_KEY
variable that Django needs to run locally.Step 3: In the codespace terminal:
python manage.py migrate
.python manage.py runserver
.Your application running on port 8000 is available.
, selectOpen in Browser.You should see the sample application in a new browser tab.To stop the application, typeCtrl
+C
.Tip
You can askGitHub Copilot about this repository. For example:
Having issues? Check theTroubleshooting section.
In this step, you create the Azure resources. The steps used in this tutorial create a set of secure-by-default resources that include App Service, Azure Database for PostgreSQL, and Azure Cache. For the creation process, you specify:
Sign in to theAzure portal and follow these steps to create your Azure App Service resources.
Step 1: In the Azure portal:
Step 2: In theCreate Web App + Database page, fill out the form as follows.
Step 3: The deployment takes a few minutes to complete. Once deployment completes, select theGo to resource button. You're taken directly to the App Service app, but the following resources are created:
The creation wizard generated the connectivity variables for you already asapp settings. However, the security best practice is to keep secrets out of App Service completely. You'll move your secrets to a key vault and change your app setting toKey Vault references with the help of Service Connectors.
Step 1: Retrieve the existing connection string
SECRET_KEY
setting, which is required by your Django app.Step 2: Create a key vault for secure management of secrets
Step 3: Secure the key vault with a Private Endpoint
Step 4: Configure the PostgreSQL connector
Step 5: Establish the Key Vault connection
Step 6: Finalize the PostgreSQL connector settings
Step 7: Configure the Redis connector to use Key Vault secrets
Step 8: Verify the Key Vault integration
@Microsoft.KeyVault(...)
, which means that it's akey vault reference because the secret is now managed in the key vault.Step 9: The sample application reads the SECRET_KEY environment variable to set therequired SECRET_KEY setting. You create it as an app setting in this step.
To summarize, the process for securing your connection secrets involved:
Note
Ideally, theSECRET_KEY
app setting should be configured as a key vault reference too, which is a multi-step process. For more information, seeHow do I change the SECRET_KEY app setting to a Key Vault reference?
Having issues? Check theTroubleshooting section.
In this step, you configure GitHub deployment using GitHub Actions. It's just one of many ways to deploy to App Service, but also a great way to have continuous integration in your deployment process. By default, everygit push
to your GitHub repository kicks off the build and deploy action.
Step 1: In the left menu, selectDeployment >Deployment Center.
Step 2: In the Deployment Center page:
.github/workflows
directory.By default, the deployment centercreates a user-assigned identity for the workflow to authenticate using Microsoft Entra (OIDC authentication). For alternative authentication options, seeDeploy to App Service using GitHub Actions.Step 3: Back in the GitHub codespace of your sample fork, rungit pull origin starter-no-infra
.This pulls the newly committed workflow file into your codespace.
Step 4 (Option 1: with GitHub Copilot):
Step 4 (Option 2: without GitHub Copilot):
AZURE_POSTGRESQL_USER
,AZURE_POSTGRESQL_PASSWORD
,AZURE_POSTGRESQL_HOST
,AZURE_POSTGRESQL_NAME
, andAZURE_REDIS_CONNECTIONSTRING
.Step 5:
Configure Azure database and cache connections
. Or, selectStep 6:Back in the Deployment Center page in the Azure portal:
Step 7: You're taken to your GitHub repository and see that the GitHub action is running. The workflow file defines two separate stages, build and deploy. Wait for the GitHub run to show a status ofSuccess. It takes about 5 minutes.
Having issues? Check theTroubleshooting guide.
With the PostgreSQL database protected by the virtual network, the easiest way to runDjango database migrations is in an SSH session with the Linux container in App Service.
Step 1: Back in the App Service page, in the left menu,
Step 2: In the SSH session, runpython manage.py migrate
. If it succeeds, App Service isconnecting successfully to the database.
Tip
In the SSH session, only changes to files in/home
can persist beyond app restarts. Changes outside of/home
aren't persisted. The SSH session is useful for running commonpython manage.py
commands, such as user creation with thepython manage.py createsuperuser
. For more information, see the documentation fordjango django-admin and manage.py. Use the superuser account to access the/admin
portion of the web site.
Having issues? Check theTroubleshooting section.
Step 1: In the App Service page:
Step 2: Add a few restaurants to the list.Congratulations, you're running a web app in Azure App Service, with secure connectivity to Azure Database for PostgreSQL.
Azure App Service captures all console logs to help you diagnose issues with your application. The sample app includesprint()
statements to demonstrate this capability as shown below.
def index(request): print('Request for index page received') restaurants = Restaurant.objects.annotate(avg_rating=Avg('review__rating')).annotate(review_count=Count('review')) lastViewedRestaurant = request.session.get("lastViewedRestaurant", False)
Step 1: In the App Service page:
Step 2: From the left menu, selectLog stream. You see the logs for your app, including platform logs and logs from inside the container.
Learn more about logging in Python apps in the series onsetting up Azure Monitor for your Python application.
When you're finished, you can delete all of the resources from your Azure subscription by deleting the resource group.
Step 1: In the search bar at the top of the Azure portal:
Step 2: In the resource group page, selectDelete resource group.
Step 3:
In this step, you create the Azure resources and deploy a sample app to App Service on Linux. The steps used in this tutorial create a set of secure-by-default resources that include App Service, Azure Database for PostgreSQL, and Azure Cache for Redis.
The dev container already has theAzure Developer CLI (AZD).
From the repository root, runazd init
.
azd init --template python-app-service-postgresql-infra
When prompted, give the following answers:
Question | Answer |
---|---|
The current directory is not empty. Would you like to initialize a project here in '<your-directory>'? | Y |
What would you like to do with these files? | Keep my existing files unchanged |
Enter a new environment name | Type a unique name. The AZD template uses this name as part of the DNS name of your web app in Azure (<app-name>-<hash>.azurewebsites.net ). Alphanumeric characters and hyphens are allowed. |
Sign into Azure by running theazd auth login
command and following the prompt:
azd auth login
Create the necessary Azure resources with theazd provision
command. Follow the prompt to select the desired subscription and location for the Azure resources.
azd provision
Theazd provision
command takes about 15 minutes to complete (the Redis cache takes the most time). Later, you'll modify your code to work with App Service and deploy the changes withazd deploy
. While it's running, the command provides messages about the provisioning and deployment process, including a link to the deployment in Azure.
This AZD template contains files (azure.yaml and theinfra directory) that generate a secure-by-default architecture with the following Azure resources:
Once the command finishes creating resources and deploying the application code the first time, the deployed sample app doesn't work yet because you must make small changes to make it connect to the database in Azure.
Having issues? Check theTroubleshooting section.
The AZD template you use generated the connectivity variables for you already asapp settings and outputs the them to the terminal for your convenience. App settings are one way to keep connection secrets out of your code repository.
In the AZD output, find the settingsAZURE_POSTGRESQL_USER
,AZURE_POSTGRESQL_PASSWORD
,AZURE_POSTGRESQL_HOST
,AZURE_POSTGRESQL_NAME
, andAZURE_REDIS_CONNECTIONSTRING
. To keep secrets safe, only the setting names are displayed. They look like this in the AZD output:
App Service app has the following connection settings: - AZURE_POSTGRESQL_NAME - AZURE_POSTGRESQL_HOST - AZURE_POSTGRESQL_USER - AZURE_POSTGRESQL_PASSWORD - AZURE_REDIS_CONNECTIONSTRING - AZURE_KEYVAULT_RESOURCEENDPOINT - AZURE_KEYVAULT_SCOPE
For your convenience, the AZD template shows you the direct link to the app's app settings page. Find the link and open it in a new browser tab.
Having issues? Check theTroubleshooting section.
In the GitHub codespace, start a new chat session by selecting theChat view, then selecting+.
Ask, "@workspace How does the app connect to the database?" Copilot might give you some explanation about how the connection settings are configured inazureproject/development.py andazureproject/production.py.
Ask, "@workspace In production mode, my app is running in an App Service web app, which uses Azure Service Connector to connect to a PostgreSQL flexible server using the Django client type. What are the environment variable names I need to use?" Copilot might give you a code suggestion similar to the one in theOption 2: without GitHub Copilot steps below and even tell you to make the change in theazureproject/production.py file.
Openazureproject/production.py in the explorer and add the code suggestion.
GitHub Copilot doesn't give you the same response every time, and it's not always correct. You might need to ask more questions to fine-tune its response. For tips, seeWhat can I do with GitHub Copilot in my codespace?.
In the terminal, runazd deploy
.
azd deploy
Having issues? Check theTroubleshooting section.
With the PostgreSQL database protected by the virtual network, the easiest way to run Django database migrations is in an SSH session with the Linux container in App Service.
In the AZD output, find the URL for the SSH session and navigate to it in the browser. It looks like this in the output:
Open SSH session to App Service container at: <URL>
In the SSH session, runpython manage.py migrate
. If it succeeds, App Service isconnecting successfully to the database.
Note
Only changes to files in/home
can persist beyond app restarts. Changes outside of/home
aren't persisted.
Having issues? Check theTroubleshooting section.
In the AZD output, find the URL of your app and navigate to it in the browser. The URL looks like this in the AZD output:
Deploying services (azd deploy) (✓) Done: Deploying service web - Endpoint: <URL>
Add a few restaurants to the list.
Congratulations, you're running a web app in Azure App Service, with secure connectivity to Azure Database for PostgreSQL.
Having issues? Check theTroubleshooting section.
Azure App Service can capture console logs to help you diagnose issues with your application. For convenience, the AZD template alreadyenables logging to the local file system and isshipping the logs to a Log Analytics workspace.
The sample application includesprint()
statements to demonstrate this capability, as shown in the following snippet.
def index(request): print('Request for index page received') restaurants = Restaurant.objects.annotate(avg_rating=Avg('review__rating')).annotate(review_count=Count('review')) lastViewedRestaurant = request.session.get("lastViewedRestaurant", False)
In the AZD output, find the link to stream App Service logs and navigate to it in the browser.
Stream App Service logs at: <URL>
Learn more about logging in Python apps in the series onsetting up Azure Monitor for your Python application.
Having issues? Check theTroubleshooting section.
To delete all Azure resources in the current deployment environment, runazd down
and follow the prompts.
azd down
Listed below are issues you might encounter while trying to work through this tutorial and steps to resolve them.
If you can't connect to the SSH session, then the app itself has failed to start. Check thediagnostic logs for details. For example, if you see an error likeKeyError: 'AZURE_POSTGRESQL_HOST'
, it might mean that the environment variable is missing (you might have removed the app setting).
If you encounter any errors related to connecting to the database, check if the app settings (AZURE_POSTGRESQL_USER
,AZURE_POSTGRESQL_PASSWORD
,AZURE_POSTGRESQL_HOST
, andAZURE_POSTGRESQL_NAME
) were changed or deleted. Without that connection string, the migrate command can't communicate with the database.
Pricing for the created resources is as follows:
psql
from the app's SSH session.Using the autogenerated workflow file from App Service as an example, eachgit push
kicks off a new build and deployment run. From a local clone of the GitHub repository, you make the desired updates and push to GitHub. For example:
git add .git commit -m "<some-message>"git push origin main
TheDjango sample application configures settings in theazureproject/production.py file so that it can run in Azure App Service. These changes are common to deploying Django to production, and not specific to App Service.
Django validates the HTTP_HOST header in incoming requests. The sample code uses theWEBSITE_HOSTNAME
environment variable in App Service to add the app's domain name to Django'sALLOWED_HOSTS setting.
# Configure the domain name using the environment variable# that Azure automatically creates for us.ALLOWED_HOSTS = [os.environ['WEBSITE_HOSTNAME']] if 'WEBSITE_HOSTNAME' in os.environ else []
Django doesn't supportserving static files in production. For this tutorial, you useWhiteNoise to enable serving the files. The WhiteNoise package was already installed with requirements.txt, and its middleware is added to the list.
# WhiteNoise configurationMIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', # Add whitenoise middleware after the security middleware 'whitenoise.middleware.WhiteNoiseMiddleware',
Then the static file settings are configured according to the Django documentation.
SESSION_ENGINE = "django.contrib.sessions.backends.cache"STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
For more information, seeProduction settings for Django apps.
From the portal steps above, you can changeSECRET_KEY
to a Key Vault reference by running the following Azure CLI commands in thecloud shell:
# Change the following variables to match your environmentSUBSCRIPTION_ID=<subscription-id>RESOURCE_GROUP=<resource-group-name>KEY_VAULT_NAME=<key-vault-name>APP_SERVICE_NAME=<app-name>SECRET_NAME=djangoSecretKey# Set the subscription IDaz account set --subscription $SUBSCRIPTION_ID# Assign 'Key Vault Secrets Officer' role to your user at the scope of the key vaultaz role assignment create \ --assignee $(az ad signed-in-user show --query id -o tsv) \ --role $(az role definition list --name "Key Vault Secrets Officer" --query "[].id" -o tsv) \ --scope $(az keyvault show --name $KEY_VAULT_NAME --resource-group $RESOURCE_GROUP --query id --output tsv)# Add the secret to the key vaultaz keyvault secret set \ --vault-name $KEY_VAULT_NAME \ --name $SECRET_NAME \ --value $(python -c 'import secrets; print(secrets.token_hex())')# Add Key Vault reference to the App Service configurationaz webapp config appsettings set \ --resource-group $RESOURCE_GROUP \ --name $APP_SERVICE_NAME \ --settings "SECRET_KEY=@Microsoft.KeyVault(SecretUri=https://$KEY_VAULT_NAME.vault.azure.net/secrets/$SECRET_NAME)"
You can also do the same thing in the portal. For more information, see:
If a step fails in the autogenerated GitHub workflow file, try modifying the failed command to generate more verbose output. For example, you can get more output from thepython
command by adding the-d
option. Commit and push your changes to trigger another deployment to App Service.
SeeSet up GitHub Actions deployment from the Deployment Center.
You might have noticed that the GitHub Copilot chat view was already there for you when you created the codespace. For your convenience, we include the GitHub Copilot chat extension in the container definition (see.devcontainer/devcontainer.json). However, you need aGitHub Copilot account (30-day free trial available).
A few tips for you when you talk to GitHub Copilot:
@workspace
. For more information, seeUse the@workspace agent.@workspace
) even where to make the changes, but it's not allowed to make the changes for you. It's up to you to add the suggested changes and test it.Advance to the next tutorial to learn how to secure your app with a custom domain and certificate.
Learn how App Service runs a Python app:
Was this page helpful?
Was this page helpful?