Azure SDK for Python: How to create a Python Azure Web App and Deploy Code from a GitHub Repository

Azure SDK for Python: How to create a Python Azure Web App and Deploy Code from a GitHub Repository

dev.to - Jan 25

Here is a quick read on how I created a Python 3.11 Web App and deployed code directly from a GitHub repository.

In order to be able to deploy an App Service Plan and the corresponding Web App, I installed and imported two libraries:

Installation through PIP

pip install azure-identity
pip install azure-mgmt-web
Enter fullscreen mode Exit fullscreen mode

Import Libraries

Since I was using the AzureCLI for authentication anyways, I was importing AzureCliCredential from the azure.identity library. This is just my personal preference for testing. Other classes from that library would work as well. From azure.mgmt.web, we'd need the WebSiteManagementClient class as well as multiple models for creating the App Service Plan and Web App configuration.

from azure.identity import AzureCliCredential
from azure.mgmt.web import WebSiteManagementClient
from azure.mgmt.web.models import AppServicePlan, SkuDescription, Site, SiteConfig, SiteSourceControl
Enter fullscreen mode Exit fullscreen mode

The following two libraries are not required for functionality but they help during testing. I used uuid to generate a unique (well, not 100%ish) name and json to prettify the output a little bit:

import uuid
import json
Enter fullscreen mode Exit fullscreen mode

Create the App Service Plan

First of all I created a random, unique number and provided appropriate subscription and resource group details.

# Create a random number
unique_deployment_number = str(uuid.uuid4().time_low)

# Provide Subscription / Resource Group details
my_subscription_id = '{subscription-id}'
my_resource_group_name = '{resource-group-name}'
Enter fullscreen mode Exit fullscreen mode

Secondly, I needed to authenticate to Azure and to create the appropriate client:

# Authenticate to Azure and create client
my_credential = AzureCliCredential()
webapp_client = WebSiteManagementClient(credential=my_credential, subscription_id=my_subscription_id)
Enter fullscreen mode Exit fullscreen mode

Then, I created a Linux App Service Plan. This required some specific settings, such as:

  • the App Service Plan name, for which I'm just using the term 'plan' and add aforementioned unique number to it.
  • the Azure location, where the Plan should be created (I'm using westeurope for testing, as this region usually has all the SKUs available)
  • the Azure Resource Group into which the Plan should be deployed
  • the Plan type, which must be linux since I wanted to create a Python Web App
  • the Plan must be reserved as this is a requirement for linux App Service Plans
  • the SKU details, which determine the costs of the App Service Plan (I was using the free tier in order to avoid high costs during testing)
# Create the App Service Plan
create_plan = webapp_client.app_service_plans.begin_create_or_update(
    name = 'plan-'+unique_deployment_number,
    resource_group_name = my_resource_group_name,
    app_service_plan = AppServicePlan(
        location = 'westeurope',
        kind = 'linux',
        reserved = True,
        sku = SkuDescription(
            name = 'F1',
            capacity = 1,
            tier = 'Free'
        )
    )
)
Enter fullscreen mode Exit fullscreen mode

Lastly, I used the json library to put the results out to the console.

# Output of the results as JSON
print(json.dumps(create_plan.result().as_dict(), sort_keys=True, indent=2))
Enter fullscreen mode Exit fullscreen mode

Result:

{
  "elastic_scale_enabled": false,
  "geo_region": "West Europe",
  "hyper_v": false,
  "id": "/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/Microsoft.Web/serverfarms/plan-4074503405",
  "is_spot": false,
  "is_xenon": false,
  "kind": "linux",
  "location": "westeurope",
  "maximum_elastic_worker_count": 1,
  "maximum_number_of_workers": 0,
  "name": "plan-4074503405",
  "number_of_sites": 0,
  "number_of_workers": 1,
  "per_site_scaling": false,
  "provisioning_state": "Succeeded",
  "reserved": true,
  "resource_group": "{resource-group-name}",
  "sku": {
    "capacity": 1,
    "family": "U",
    "name": "U13",
    "size": "U13",
    "tier": "LinuxFree"
  },
  "status": "Ready",
  "subscription": "{subscription-id}",
  "target_worker_count": 0,
  "target_worker_size_id": 0,
  "type": "Microsoft.Web/serverfarms",
  "zone_redundant": false
}
Enter fullscreen mode Exit fullscreen mode

Create the Web App

Next, I needed to create the web app on top of the newly created plan.

As first step, I was getting the App Service Plan Id.

try:
    app_service_plan = webapp_client.app_service_plans.get(
        name = 'plan-'+unique_deployment_number,
        resource_group_name = my_resource_group_name
    )
except Exception:
    print(f'Error: App Service Plan {my_app_service_plan_name} not found.')
Enter fullscreen mode Exit fullscreen mode

Secondly, I checked whether the name that I wanted to use for my web app was already in use or not (similar to the plan name I just built it from the same random number):

availability_check = webapp_client.check_name_availability(
    name = 'webapp-'+unique_deployment_number,
    type = 'Microsoft.Web/sites'
)
Enter fullscreen mode Exit fullscreen mode

And then finally, I was able to deploy the Web App. Note that both site configurations must be in place for the Web App to be properly created as Python App:

  • python_version
  • linux_fx_version
if availability_check.name_available == True:
    create_website = webapp_client.web_apps.begin_create_or_update(
        name = 'webapp-'+unique_deployment_number,
        resource_group_name = my_resource_group_name,
        site_envelope = Site(
            location = 'westeurope',
            server_farm_id = app_service_plan.id,
            site_config = SiteConfig(
                python_version = '3.11',
                linux_fx_version = 'PYTHON|3.11'
            )
        )
    )
    # Output of the results as JSON
    print(json.dumps(create_website.result().as_dict(), sort_keys=True, indent=2))
else:
    print(f'Error: Name {my_webapp_name} is already in use.')
Enter fullscreen mode Exit fullscreen mode

Since the name was not in use yet, the output confirmed the successful creation of the web app:

{
  "availability_state": "Normal",
  "client_affinity_enabled": true,
  "client_cert_enabled": false,
  "client_cert_mode": "Required",
  "container_size": 0,
  "custom_domain_verification_id": "{redacted}",
  "daily_memory_time_quota": 0,
  "default_host_name": "webapp-4074503405.azurewebsites.net",
  "enabled": true,
  "enabled_host_names": [
    "webapp-4074503405.azurewebsites.net",
    "webapp-4074503405.scm.azurewebsites.net"
  ],
  {...readacted...}
  "state": "Running",
  "storage_account_required": false,
  "type": "Microsoft.Web/sites",
  "usage_state": "Normal",
  "vnet_content_share_enabled": false,
  "vnet_image_pull_enabled": false,
  "vnet_route_all_enabled": false
}
Enter fullscreen mode Exit fullscreen mode

A check through the Web App in the Azure Portal confirmed the successful deployment.

Web_App

Deploy Code from GitHub

As last step, I wanted to deploy sample code from GitHub into the newly created Web App. Since this post is more about the process of creating the infrastructure and deploying the app and not about developing the app itself, I have simply forked this hello-world sample app [3] from the Azure Samples GitHub repository [4].

Since I did not want to use continuous integration but rather go for a one-off deployment, I used is_manual_integration = True for the source control settings.

source_control = webapp_client.web_apps.begin_create_or_update_source_control(
    resource_group_name = my_resource_group_name,
    name = 'webapp-'+unique_deployment_number,
    site_source_control = SiteSourceControl(
        is_manual_integration = True,
        repo_url = 'https://github.com/{repository-url}',
        branch = 'master'
    )
)
Enter fullscreen mode Exit fullscreen mode

In a proper script, I could now have checked the status through the source_control.status() variable to become 'Succeeded'. Once the deployment was done, the portal would reflect the External Repository Project:

Web App Repository

Finally, I could confirm the deployment by following the link to the Web App URL and see the corresponding Hello, World! message in the browser.

Hello World

Conclusions

I was able to use Python and the Azure SDK for Python for a step-by-step deployment of an App Service Plan, a Web App and a sample Python Flask application from a GitHub repository. I had some issues with creating the App Service Plan and the Web App with the correct settings for Python, which resulted in the following learnings:

  • When creating an App Service Plan of kind linux, the parameter reserved = True must be set, otherwise the App Service Plan will show as Windows in the Azure Portal.
  • When creating the Web App, both python_version and linux_fx_version must be set - otherwise the stack will not be configured for Python and App deployments would fail for me:

            site_config = SiteConfig(
                python_version = '3.11',
                linux_fx_version = 'PYTHON|3.11'
            )
    
  • The deployment from a GitHub repository was very straightforward.

References

# Title URL Accessed-On
1 azure-identity 1.12.0 https://pypi.org/project/azure-identity/ 2023-01-24
2 azure-mgmt-web 7.0.0 https://pypi.org/project/azure-mgmt-web/ 2023-01-24
3 Python Flask sample for Azure App Service (Linux) https://github.com/Azure-Samples/python-docs-hello-world 2023-01-24
4 Azure Samples https://github.com/Azure-Samples 2023-01-24
5 Azure Web Apps libraries for Python https://learn.microsoft.com/en-us/python/api/overview/azure/app-service?view=azure-python 2023-01-24

MORE ARTICLES