Skip to content

Azure resources and setup

The pipeline targets an existing Python 3.11 Linux Consumption Function App and an existing Azure Static Web App. It deploys application code; it does not provision the resource group, storage, or email service.

Resource checklist

ResourceUsed forSetup notes
Resource groupOrganizes the existing app resourcesMatches AZURE_RESOURCE_GROUP
Function App, runtime v4 / Python 3.11Hourly processingMatch the CI Python minor version and runtime locks
Function host storageRuntime and timer coordinationConfigured through AzureWebJobsStorage
Results storage accountPublic JSON and private season stateConfigured through RESULTS_STORAGE_CONNECTION_STRING
Azure Communication Services EmailWinner and recap deliveryConfigure a verified sender and connect it to ACS
Azure Static Web AppBrowser UI in public/No linked API or frontend build is required
Microsoft Entra applicationGitLab deployment identityUses workload identity federation for Function deployment

These are the repository’s deployment assumptions, not an inventory retrieved from a live Azure subscription. Verify the existing resource names and hosting plan in Azure before using the settings below.

1. Prepare storage and email

  1. Configure the Function’s host storage through its Azure runtime setup.
  2. Enable static website hosting on the results account to create $web, or ensure the intended $web container exists. The app expects it to exist.
  3. Allow anonymous blob reads for the published results at the Blob endpoint if using the current direct browser URL. Public access to the Storage website endpoint alone does not establish public Blob endpoint access.
  4. Keep pool-state private. The Function creates it if absent; its credentials need container creation and blob read/write permissions.
  5. Configure Blob service CORS for each browser origin that reads the feed.
  6. Set up the verified ACS email sender and populate the application settings in the configuration reference.

Storage’s *.web.core.windows.net website endpoint and *.blob.core.windows.net/$web/... Blob endpoint behave differently. The website endpoint does not support Storage CORS; the application uses the Blob endpoint for cross-origin JSON. See Azure Storage website behavior and the public website guide.

2. Configure GitLab identity for the Function

sequenceDiagram
    participant Job as GitLab deployment job
    participant Entra as Microsoft Entra ID
    participant Azure as Azure Resource Manager
    Job->>Job: Request job OIDC token
    Job->>Entra: Exchange token using client and tenant IDs
    Entra->>Entra: Validate issuer, subject, and audience
    Entra-->>Job: Short-lived Azure credentials
    Job->>Azure: Configure remote build and deploy ZIP
  1. Create an Entra app registration and its service principal.

  2. Add a federated credential with the following exact values for your project:

    FieldValue
    Issuerhttps://gitlab.com for this repository
    Subjectproject_path:bclingan-development/cloud/azure-ravens-pool:ref_type:branch:main
    Audienceapi://AzureADTokenExchange

    Update the subject if the project path or default branch changes. A self-managed GitLab issuer must expose discovery and signing keys to Azure.

  3. Grant the service principal Website Contributor scoped to the existing Function App, as used by this deployment design. Check authorization failures against the actual scope; the job does not need to create a resource group.

  4. Protect the default branch and configure the five AZURE_* identity/resource variables listed in configuration.

GitLab generates AZURE_OIDC_TOKEN for the job. Do not store that token or create a client secret to replace it. GitLab’s Azure OIDC guide describes the federation flow.

3. Deploy the Function

The deploy:function job:

  1. Packages function_app.py, host.json, requirements.txt, and .py, .html, .txt, and .css files beneath app/ into /tmp/function.zip.
  2. Logs in with the federated identity and selects the subscription.
  3. Removes WEBSITE_RUN_FROM_PACKAGE and enables SCM_DO_BUILD_DURING_DEPLOYMENT=true and ENABLE_ORYX_BUILD=true.
  4. Submits the ZIP with --build-remote true and a 600-second command timeout.

Azure installs the fully pinned requirements.txt using the Function’s configured Python version. Changes to app settings can restart the Function. The pipeline leaves storage credentials and email settings managed in Azure.

See Python build options for the remote-build model. This ZIP and these settings are specific to the current hosting approach; do not carry them unchanged to another hosting plan.

For a manual code deployment from the repository root, after configuring and verifying the destination:

Terminal window
func azure functionapp publish <function-app-name>

The application exposes only the hourly timer. Use Function invocation logs and published data to verify it; do not assume /api/ping exists.

4. Deploy the public website

The deploy:production job installs the locked Static Web Apps CLI and deploys public/ to the production environment with SWA_DEPLOYMENT_TOKEN.

SWA build settingValue
App location/public
API locationEmpty
Output locationEmpty
Frontend build commandNone

The CLI receives Node runtime metadata even though this is a static-only site. No Function source is linked to this deployment. The feed URL and CORS settings are covered in public website.

Custom domains are configured on the Static Web App. Use the DNS record and validation steps shown by Azure, then add the custom domain’s origin to Blob CORS as well. See Static Web Apps custom domains.

Hosting lifecycle

Microsoft lists Linux Consumption retirement as September 30, 2028. Plan a move to Flex Consumption before retirement and revisit packaging, identity, build settings, and operational checks as part of that work. See Azure Functions hosting guidance.