> ## Documentation Index
> Fetch the complete documentation index at: https://docs.energy.nlead.ch/llms.txt
> Use this file to discover all available pages before exploring further.

# Azure Deployment

> One Bicep file: app, storage, registry, logs and the orchestrator timer

The service runs as a single **Azure Container App** plus a scheduled job.
The entire footprint is one Bicep file —
[`infra/main.bicep`](https://github.com/n-lead-GmbH/sup_brp_bridge/blob/main/infra/main.bicep):

| Resource                     | Purpose                                                                                              |
| ---------------------------- | ---------------------------------------------------------------------------------------------------- |
| Container App (0→1 replicas) | Runs the FastAPI service, external HTTPS ingress, `/health` probes                                   |
| Container Apps **cron job**  | Calls `POST /v1/process/orchestrate` with the process key every 15 min (`orchestrateCron` parameter) |
| Azure Files share            | Mounted at `/app/data/audit`: audit trail + process state survive restarts and scale-to-zero         |
| Container Registry (Basic)   | Holds the service image                                                                              |
| Log Analytics workspace      | Container and job logs                                                                               |

<Note>
  **Why scale-to-zero?** Traffic is a handful of calls per day plus a
  cheap orchestrator ping; the orchestrator only acts when input changed,
  so idle runs are no-ops. Max one replica keeps the file-based state
  single-writer.
</Note>

## Bootstrap

The ACR doesn't exist until the template runs, so the first deployment uses a
placeholder image and then swaps in the real one:

<Steps>
  <Step title="Deploy the infrastructure">
    ```bash theme={null}
    RG=nlead-bridge-rg
    PREFIX=nleadbridge
    az group create --name "$RG" --location germanywestcentral
    az deployment group create --resource-group "$RG" \
      --template-file infra/main.bicep \
      --parameters namePrefix="$PREFIX" \
                   externalApiKey="$(openssl rand -hex 32)" \
                   processApiKey="$(openssl rand -hex 32)" \
                   engrateApiKey='<engrate console key>' \
                   sftpPassword='<sftp password>' \
                   sftpHost='<trader sftp host>' \
                   sftpUsername='<sftp user>' \
                   homeControlArea='TenneT'
    ```
  </Step>

  <Step title="Build the image in ACR (no local Docker needed)">
    ```bash theme={null}
    az acr build --registry "${PREFIX}acr" --image scheduling-bridge:latest .
    ```
  </Step>

  <Step title="Point the app at the real image">
    ```bash theme={null}
    az containerapp update --name "${PREFIX}-app" --resource-group "$RG" \
      --image "${PREFIX}acr.azurecr.io/scheduling-bridge:latest"
    ```

    The public URL is in the deployment output `appFqdn`.
  </Step>
</Steps>

Re-running step 1 is idempotent; code redeploys only need steps 2–3.

## Continuous deployment

The manual GitHub Actions workflow
[`deploy.yml`](https://github.com/n-lead-GmbH/sup_brp_bridge/blob/main/.github/workflows/deploy.yml)
runs the same ACR build + app update. It needs:

* repository secret `AZURE_CREDENTIALS` (service principal with contributor
  on the resource group),
* repository variables `AZURE_RESOURCE_GROUP` and `AZURE_NAME_PREFIX`.

## Security posture

* Two scoped API keys: `EXTERNAL_API_KEY` (hand to the customer; forecast
  ingestion only) and `PROCESS_API_KEY` (internal pipeline + orchestrator
  timer). `/health` stays open for probes.
* Secrets live as Container Apps secrets — move to Key Vault references when
  the team standardizes on it.
* Before live Engrate submissions, set the real balancing-group EICs
  (`AREA_MARKET_IDS`) and the ECC counterparty EIC; use
  `ENGRATE_TEST_MODE=true` to exercise the integration without gate-closure
  validation.
* If the customer calls from fixed egress IPs, add ingress IP restrictions
  on the Container App.
