# Azure (https://docs.capy.ai/cloud-credentials/azure)

> Let cloud machines sign in as a Microsoft Entra app registration or managed identity through a federated identity credential, with no client secret.

Machines sign in as a Microsoft Entra app registration or user-assigned managed identity through a federated identity credential. Entra matches a credential's subject exactly, so Azure tokens carry the project-level subject `org/<org id>/project/<project id>` instead of one subject per thread. The thread code is still in the `thread` claim. Read [OIDC federation](/cloud-credentials) first for how the token, the issuer, and refresh work.

## Set up Azure [#set-up-azure]

Open **Settings → Cloud credentials → Azure** and follow these steps. The setup page fills in your issuer URL, organization ID, and project ID, so copy the commands from there. The commands below use placeholders.

<Steps>
  <Step title="Add the project in Capy">
    On **Azure**, choose **Add project**. Pick the Capy project, then enter the **Tenant ID** (the directory ID from Microsoft Entra) and the **Client ID** (the app registration's application ID).

    <Screenshot name="cloud-access-azure-setup" alt="Azure setup with the project, tenant ID, client ID, and the project's subject" caption="The subject is the value the federated credential must match." />
  </Step>
  <Step title="Trust Capy">
    Run this once in Cloud Shell to add a federated credential for the project:

    ```bash
    az ad app federated-credential create --id '<client id>' \
      --parameters '{"name":"capy-<project id>","issuer":"<issuer URL>","subject":"org/<org id>/project/<project id>","audiences":["api://AzureADTokenExchange"]}'
    ```

    If the credential already exists, run `az ad app federated-credential update` with `--federated-credential-id 'capy-<project id>'` and the same parameters. For a managed identity, add a federated credential with the issuer URL, this subject, and the audience `api://AzureADTokenExchange`. Entra allows at most 20 federated credentials on one app or managed identity, so one identity serves at most 20 Capy projects.
  </Step>
  <Step title="Grant access">
    Assign the app the roles the agent needs. For example, to give it **Reader** on the current subscription:

    ```bash
    az ad sp show --id '<client id>' >/dev/null 2>&1 \
      || az ad sp create --id '<client id>'
    SUBSCRIPTION_ID=$(az account show --query id -o tsv)
    az role assignment create --assignee '<client id>' \
      --role Reader --scope "/subscriptions/$SUBSCRIPTION_ID"
    ```
  </Step>
  <Step title="Verify">
    Choose **Verify**. Capy requests a token for Azure Resource Manager with a test token as the client assertion, then shows the client ID it signed in as.

    <Screenshot name="cloud-access-azure-check" alt="Azure status card with a passed test sign-in and the variables machines export" caption="Azure SDKs read these variables through DefaultAzureCredential." />
  </Step>
</Steps>

Entra's sign-in log attributes each exchange to the project, because the subject has no thread.

## What machines get [#what-machines-get]

| Files in `/etc/capy/cloud-access/` | Exports |
| --- | --- |
| `azure.jwt` | `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, and `AZURE_FEDERATED_TOKEN_FILE` (`/etc/capy/cloud-access/azure.jwt`) |

Azure SDKs read these variables through `DefaultAzureCredential` and `WorkloadIdentityCredential` with no login step. The `az` CLI isn't preinstalled. When the agent installs it, the login is `az login --service-principal -u "$AZURE_CLIENT_ID" -t "$AZURE_TENANT_ID" --federated-token "$(cat "$AZURE_FEDERATED_TOKEN_FILE")"`.

## Revoke access [#revoke-access]

To stop every exchange at once, delete the `capy-<project id>` federated credential. To stop Capy from sending tokens, remove the project from **Azure** in Capy. Access tokens that machines already hold last until they expire.

## Troubleshooting [#troubleshooting]

A failed check shows the error from Entra and the likely fix. For a machine with no token files, see [OIDC federation troubleshooting](/cloud-credentials#troubleshooting).

<AccordionGroup>
  <Accordion title="AADSTS70021">No federated credential matches this project's subject. Run **Trust Capy**, and check that the subject has no `/thread/` part.</Accordion>
  <Accordion title="AADSTS700016">The app isn't in this tenant. Check the tenant ID and client ID.</Accordion>
</AccordionGroup>
