Edit

Share via


Part 6: Main app startup code

Previous part: Dependencies and environment variables

Immediately following the import statements, the app's startup code initializes key variables used throughout the request-handling functions.

First, the application creates the Flask app object, which serves as the foundation for defining routes and handling incoming HTTP requests. Next, it retrieves the third-party API endpoint URL from an environment variable. This allows the endpoint to be easily configured without modifying the codebase:

app = Flask(__name__)
app.config["DEBUG"] = True

number_url = os.environ["THIRD_PARTY_API_ENDPOINT"]

Next, it obtains the DefaultAzureCredential object, which is the recommended credential to use when authenticating with Azure services. See Authenticate Azure hosted applications with DefaultAzureCredential.

credential = DefaultAzureCredential()

When run locally, DefaultAzureCredential looks for the AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET environment variables that contain information for the service principal that you're using for local development. When run in Azure, DefaultAzureCredential defaults to using the system-assigned managed identity enabled on the app. It's possible to override the default behavior with application settings, but in this example scenario, we use the default behavior.

The code next retrieves the third-party API's access key from Azure Key Vault. In the provisioning script, the Key Vault is created using az keyvault create, and the secret is stored with az keyvault secret set.

The Key Vault resource itself is accessed through a URL, which is loaded from the KEY_VAULT_URL environment variable.

key_vault_url = os.environ["KEY_VAULT_URL"]

To retrieve a secret from Azure Key Vault, the application must create a client object that communicates with the Key Vault service. Since the goal is to read a secret, the app uses the SecretClient class from the azure.keyvault.secrets library. This client requires two inputs:

  • The Key Vault URL – typically retrieved from an environment variable
  • A credential object – such as the DefaultAzureCredential instance created earlier, which represents the identity under which the app is running.
keyvault_client = SecretClient(vault_url=key_vault_url, credential=credential)

Creating a SecretClient object does not immediately authenticate the application. The client is simply a local construct that stores the Key Vault URL and the credential object. Authentication and authorization happen only when you invoke an operation through the client, such as get_secret, which generates a REST API call to the Azure resource.

api_secret_name = os.environ["THIRD_PARTY_API_SECRET_NAME"]
vault_secret = keyvault_client.get_secret(api_secret_name)

# The "secret" from Key Vault is an object with multiple properties. The key we
# want for the third-party API is in the value property. 
access_key = vault_secret.value

Even if an application's identity is authorized to access Azure Key Vault, it must also be explicitly authorized to perform specific operations—such as reading secrets. Without this permission, a call to get_secret() fails, even if the identity is otherwise valid. To address this, the provisioning script sets a "get secrets" access policy for the app using the Azure CLI command, az keyvault set-policy. For more information, see Key Vault Authentication and Grant your app access to Key Vault. The latter article shows how to set an access policy using the Azure portal. (The article is also written for managed identity, but applies equally to a service principle used in local development.)

Finally, the app code sets up the client object through which it can write messages to an Azure Storage Queue. The Queue's URL is in the environment variable STORAGE_QUEUE_URL.

queue_url = os.environ["STORAGE_QUEUE_URL"]
queue_client = QueueClient.from_queue_url(queue_url=queue_url, credential=credential)

As with Azure Key Vault, the application uses a specific client object from the Azure SDK to interact with Azure Queue Storage. In this case, it uses the QueueClient class from the azure-storage-queue library.

To initialize the client, the app uses the from_queue_url method, providing the queue’s fully qualified URL and a credential object. This credential object is again the DefaultAzureCredential instance created earlier, which represents the identity under which the app is running.

As noted earlier in this guide, that authorization is granted by assigning the “Storage Queue Data Contributor” role to the application's identity - either a managed identity in Azure or a service principal during local development. This role assignment is done in the provisioning script using the Azure CLI command az role assignment create.

Assuming all this startup code succeeds, the app has all its internal variables in place to support its /api/v1/getcode API endpoint.