Blog Post

Apps on Azure Blog
5 MIN READ

Using Azure Python SDK to manage Azure Container Apps - Part 2

ractando's avatar
ractando
Icon for Microsoft rankMicrosoft
Feb 22, 2024

 

In the Part 1 of this article, we discussed the implementation of Azure Python SDK for managing Azure Container Apps while using the DefaultAzureCredentials with Azure CLI (Developer Authentication). Here, we will be creating/updating a Container App Job using Azure Python SDK, while using Service Principal for authentication. Also, we will be using an App Service to deploy our project. We will be referring to the job creation sample which is one of the many templates provided in the Azure Python SDK template list.

 

Getting Started

 

Prerequisites

 

It is assumed here that you already have an existing Azure Subscription, Resource Group and a Container App Environment available. Also, we will be using a Linux App Service to run our Python project. Hence please make sure that you have a Python app service on your subscription as well.

 

Configuring Microsoft Entra ID

 

The first thing to do is to create a Service Principal by registering an application on the Entra ID. When you register a new application in Microsoft Entra ID, a service principal is automatically created for the app registration. The service principal is the app's identity in the Microsoft Entra tenant. Access to resources is restricted by the roles assigned to the service principal, giving you control over which resources can be accessed and at which level.

 

You can register your application and get the details for the Service Principal by following the steps provided here. Then, from the App Registration, create a new Client Secret as described here. Make sure that you keep all the 3 details (AZURE_CLIENT_ID, 

AZURE_TENANT_ID & AZURE_CLIENT_SECRET) handy for further usage since we will be using them while configuring our App Service.

 

 

Now, the next step is to provide this Service Principal Contributor access on the Subscription. For this, on the portal

  1. Go to Subscriptions > Access Control (IAM) > Add - Role Assignments
  2. In Privileged Administrator Role, select Contributor, Next.
  3. Select User, group, or service principal and add the App Registration as a member.
  4. Review + Assign.

 

Note: If you do not provide access to the Service Principal, you will receive an AuthorizationFailed error. Message: The client 'xx' with object id 'xx' does not have authorization to perform action 'Microsoft.App/containerApps/write' over scope '/subscriptions/xx/resourceGroups/xx/providers/Microsoft.App/containerApps/xx' or the scope is invalid. If access was recently granted, please refresh your credentials.

 

Configuring Linux App Service

 

In this example, our App Service will host the python program which when triggered will create the Container App Job with the desired configuration. For that we will need certain configuration on the app service.

 

We have already created a Python based App Service, enabled Application logs and added SCM_DO_BUILD_DURING_DEPLOYMENT = true as an Environment variable, since we will be doing a ZIP Deployment.

 

As a best practice, we are saving the ACR Password in a Key Vault (KV) and to make the app service use it, we will pass the KV Secret reference in the app's Environment Variable section. For the app to read the secret from the KV, we will have to provide it the permission to perform that action. For the same we will be granting our app access to the key vault by creating a System assigned Managed Identity and assigning it the role of a Key Vault Secret User. To do this, you can follow the steps as listed here.

 

In the Environment Variables section, we will add certain parameters which our program will use.

 

  • AZURE_CLIENT_ID : <As saved in the earlier step>
  • AZURE_TENANT_ID : <As saved in the earlier step>
  • AZURE_CLIENT_SECRET : <As saved in the earlier step>
  • CA_ENV_NAME : The Container App Environment name where the job will be deployed.
  • CA_JOB_NAME : The name of the Container App Job (A value must consist of lower-case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character)
  • REGISTRY_PSSWD : Deleted.KeyVault(SecretUri=https://<KV_NAME>.vault.azure.net//secrets/<SECRET_NAME>/)
  • SCM_DO_BUILD_DURING_DEPLOYMENT : true

 

Configuring Python Code

 

app.py

 

 

from azure.mgmt.appcontainers import ContainerAppsAPIClient
from azure.identity import EnvironmentCredential
import os
from flask import Flask
app = Flask(__name__)

@app.route('/cajob', methods=['POST','GET'])
def cajob():
    #Initiating Connection
    client = ContainerAppsAPIClient(credential=EnvironmentCredential(),subscription_id="<SUBSCRIPTION_ID>")

    #Refrencing Environment Variables
    CA_JOB_NAME = os.environ["CA_JOB_NAME"]
    CA_ENV_NAME = os.environ["CA_ENV_NAME"]
    REGISTRY_PASSWORD_REF = os.environ["REGISTRY_PSSWD"]

    response = client.jobs.begin_create_or_update(
        resource_group_name="default-rg",
        job_name= CA_JOB_NAME,
        job_envelope={
            "location": "East US 2", #Make sure this is same as the Managed Environment location
            "properties": {
                "configuration": {
                    "manualTriggerConfig": {"parallelism": 4, "replicaCompletionCount": 1},
                    "replicaRetryLimit": 10,
                    "replicaTimeout": 10,
                    "triggerType": "Manual"
                },
                "environmentId": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/default-rg/providers/Microsoft.App/managedEnvironments/"+CA_ENV_NAME,
                "template": {
                    "containers": [
                        {
                            "image": "<ACR_NAME>.azurecr.io/nginx-acr:latest",
                            "name": CA_JOB_NAME + "-cnt",
                            "probes": [
                                {
                                    "httpGet": {
                                        "httpHeaders": [{"name": "Custom-Header", "value": "Awesome"}],
                                        "path": "/health",
                                        "port": 8080,
                                    },
                                    "initialDelaySeconds": 5,
                                    "periodSeconds": 3,
                                    "type": "Liveness",
                                }
                            ],
                            "registries": [
                                {
                                    "server": "https://<ACR_NAME>.azurecr.io",
                                    "username": "myimgregistry",
                                    "passwordSecretRef": "acr-password"
                                }
                            ],
                        }
                    ],
                    "secrets": [    
                        {
                            "name": "acr-password",
                            "value": REGISTRY_PASSWORD_REF
                        },
                    ],
                },
            },
        },
    ).result()
    print(response)
    return "--CA Job OK--"

 

 

 

requirements.txt

 

 

flask
azure-mgmt-appcontainers
azure-identity

 

 

 

Note: Before running this template, please make sure that the region of the Container App Environment is same as the one mentioned in the template. If not, this may result in a 'ManagedEnvironmentNotFound' or an 'InvalidResource' error.

 

Deploying the Python Code on App Service

 

In this case, I have placed both the above-mentioned files directly under the project root 'pysdk'. The folder structure is as follows:

 

 

pysdk
  - app.py
  - requirements.txt

 

 

 

We will be using the az webapp up command to deploy the python project to the app service.

 

 

az webapp up -n <APP_SERVICE_NAME>

 

 

 

 

This will directly deploy the python project on the App Service. There is no need to mention the full app URL (<APP_SERVICE_NAME>.azurewebsites.net). Only mentioning the App Service name will work.

 

After successful deployment of the project, we will access the default webpage of the app service after appending /cajob in front of it.

 

The above response marks a successful run. However, if you receive a 500 Internal Server Error, please go to the Application Logs to check for errors and debug further.

 

After this, if we go to our Managed Environment where we have created the Container App Job, we will be able to see it in the list.

 

Updated Feb 22, 2024
Version 3.0