Skip to content

Review with Azure OpenAI (keyless OIDC)

Use this guide to run lgtmaybe against Azure OpenAI using GitHub's OIDC token federated to Entra (Azure AD) — no static Azure key stored in secrets. A key-based fallback is covered at the end.

How it works

GitHub Actions issues a short-lived OIDC token. Entra (Azure AD) exchanges it — via a federated credential on an app registration or managed identity — for an Azure AD access token scoped to your Azure OpenAI resource. The action does that exchange for you (pass azure_client_id / azure_tenant_id) and lgtmaybe picks up the ambient token through azure-identity's DefaultAzureCredential — no AZURE_API_KEY in your secrets.

Azure still needs two non-secret values: the deployment name (model) and the resource endpoint (api_base, https://<resource>.openai.azure.com).

One-time Azure setup

This is the human-only part — do it once in your Azure tenant:

  1. Register an Entra app (or use a user-assigned managed identity). Note its client ID (azure_client_id) and your tenant ID (azure_tenant_id).
  2. Add a federated credential to it for GitHub Actions:
  3. Issuer: https://token.actions.githubusercontent.com
  4. Subject: scope it to your repo, e.g. repo:<org>/<repo>:pull_request (or :ref:refs/heads/main, :environment:…)
  5. Audience: api://AzureADTokenExchange
  6. On the Azure OpenAI resource, grant the app the Cognitive Services OpenAI User role (least privilege — it can call models, not manage the resource).
  7. Deploy the model you want and note the deployment name — that is what you pass as model, not the upstream OpenAI model id.
  8. Note the resource endpoint (https://<resource>.openai.azure.com) — it becomes api_base. No static key is ever stored.

Workflow example

The action performs the OIDC exchange for you — no separate azure/login step needed. id-token: write is required so GitHub will mint the OIDC token.

name: lgtmaybe

on:
  pull_request_target:
  issue_comment:
    types: [created]

permissions:
  id-token: write          # required for the OIDC token exchange (keyless)
  pull-requests: write     # required to post review comments
  contents: read

jobs:
  review:
    if: ${{ github.event_name == 'pull_request_target' || github.event.issue.pull_request }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: lgtmaybe/lgtmaybe@v1
        with:
          provider: azure
          model: my-gpt-4o-deployment            # your deployment name
          api_base: ${{ secrets.AZURE_API_BASE }} # https://<resource>.openai.azure.com
          azure_client_id: ${{ secrets.AZURE_CLIENT_ID }}
          azure_tenant_id: ${{ secrets.AZURE_TENANT_ID }}

Choosing the model name

For Azure, model is the deployment name you chose in the portal, not the underlying OpenAI model id. A deployment of gpt-4o named my-gpt-4o-deployment is referenced as model: my-gpt-4o-deployment. litellm routes it as azure/<deployment-name>.

Running locally with ambient Azure credentials

Keyless works locally too. Install the azure extra, sign in (or use a managed identity), set the endpoint, and review your current branch's changes:

pip install 'lgtmaybe[azure]'
az login

export AZURE_API_BASE="https://<resource>.openai.azure.com"

lgtmaybe review \
  --provider azure \
  --model my-gpt-4o-deployment

DefaultAzureCredential finds your az login session (or a managed identity, or AZURE_* env vars) and lgtmaybe never stores a static key.

Key-based alternative

If you would rather use a resource key, set AZURE_API_KEY and AZURE_API_BASE (no id-token permission, no azure_client_id needed). The azure-identity extra is not required in this mode.

      - uses: lgtmaybe/lgtmaybe@v1
        with:
          provider: azure
          model: my-gpt-4o-deployment
          api_key: ${{ secrets.AZURE_API_KEY }}
          api_base: ${{ secrets.AZURE_API_BASE }}

Locally: export AZURE_API_KEY=… AZURE_API_BASE=… (or pass --api-key / --api-base).

Troubleshooting

azure requires credentials — neither a key nor an ambient AD credential was found. For keyless, check id-token: write is present and the federated credential's subject matches the triggering ref/event. For key mode, set AZURE_API_KEY.

azure requires the resource endpoint — set api_base (or AZURE_API_BASE) to https://<resource>.openai.azure.com.

keyless azure needs the azure-identity package — running keyless on the CLI without the extra; install lgtmaybe[azure]. (The Action image already bundles it.)

AADSTS70021 / no matching federated identity — the federated credential's issuer/subject/audience don't match this workflow. Audience must be api://AzureADTokenExchange and the subject must match the repo and event.

DeploymentNotFound / 404model must be the deployment name on the resource that api_base points at, not the upstream OpenAI model id.

Unsupported API version — pin one by setting AZURE_API_VERSION (e.g. 2024-08-01-preview) in the environment; litellm otherwise uses a default.