> ## Documentation Index
> Fetch the complete documentation index at: https://conductorone-hunner-patch-1.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up a Microsoft Azure connector

> C1 provides identity governance and just-in-time provisioning for Azure. Integrate your Azure instance with C1 to run user access reviews (UARs), enable just-in-time access requests, and automatically provision and deprovision access.

The Microsoft Azure connector uses the [Cloud Infrastructure Access](/product/admin/cloud-infrastructure-access) data model, which stores access as role-scope bindings and computes effective access on demand. This enables access requests with resource hierarchy navigation, access reviews scoped by inheritance, entitlement configuration rules based on role and scope, and lifecycle automation across the Azure resource tree.

## Capabilities

| Resource | Sync | Provision |
| :- | :- | :- |
| Accounts | <Icon icon="circle-info" /> | |
| Groups | <Icon icon="circle-info" /> | |
| Managed identities | <Icon icon="circle-info" /> | |
| Enterprise applications | <Icon icon="circle-info" /> | |
| Azure roles | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="square-check" iconType="solid" color="#c937ae" /> |
| Tenant root | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | |
| Management groups | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | |
| Resource groups | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | |
| Subscriptions | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | |
| Azure resources (opt-in) | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | |

<Icon icon="circle-info" /> This connector pulls account, group, managed identity, and enterprise application information from the Entra ID connector. You'll configure this relationship when setting up the connector.

## Gather Azure credentials

Configuring the connector requires you to pass in credentials generated in Azure. Gather these credentials before you move on.

<Warning>
  A user with the **Global Administrator** permission in Azure must perform this task.
</Warning>

### Create a new Entra application

<Steps>
  <Step>
    In the Entra admin center, navigate to **App registrations**.
  </Step>

  <Step>
    Click **+ New registration**.
  </Step>

  <Step>
    Give the application a name, such as "C1", and select the supported account type relevant to your Entra installation. You do not need to set a redirect URL.
  </Step>

  <Step>
    Click **Register**.
  </Step>

  <Step>
    The new app is created. Carefully copy and save the **Application (client) ID** and the **Directory (tenant) ID** shown on the application summary page.
  </Step>

  <Step>
    Next, we'll generate a client secret for this app. Click **Certificates & secrets**.
  </Step>

  <Step>
    Click **+ New client secret**.
  </Step>

  <Step>
    Give the client secret a description and set its expiration.
  </Step>

  <Step>
    Click **Add**.
  </Step>

  <Step>
    The client secret is generated. Carefully copy and save the **Secret Value**.
  </Step>
</Steps>

### Assign Azure RBAC permissions to the application

Repeat this process for each subscription you want to sync to C1. Alternately, you can grant a Management Group scope encompassing all the desired subscriptions.

<Note>
  Management groups are listed on every sync run, regardless of whether any role assignment references one. If the connector's credentials only have **Reader** at the subscription level, that listing is denied and the whole sync fails -- it does not degrade gracefully. Grant **Reader** at the Management Group scope too (instead of, or in addition to, at each subscription below it).
</Note>

<Steps>
  <Step>
    In the Azure portal's search bar, type "Subscriptions" and select the relevant Azure subscription.
  </Step>

  <Step>
    In the left-hand menu of your subscription, select **Access control (IAM)**.
  </Step>

  <Step>
    Click **+ Add** > **Add role assignment**.
  </Step>

  <Step>
    On the **Role** tab, search for and select the **Reader** role.
    If you want to use the C1 connector to provision Azure roles, also grant the **User Access Administrator** role. Then, on the **Conditions** tab, select **Allow user to assign all roles**.
  </Step>

  <Step>
    Click **Next**.
  </Step>

  <Step>
    On the **Members** tab, ensure **User, group, or service principal** is selected for **Assign access to**.
  </Step>

  <Step>
    Click **+ Select members**.
  </Step>

  <Step>
    In the **Select members** pane, search for and select the name of your App Registration.
  </Step>

  <Step>
    Click **Select** at the bottom of the pane.
  </Step>

  <Step>
    Click **Review + assign** at the bottom.
    Allow time for the new role to propagate. Azure role assignments can take several minutes (typically five to 15, sometimes up to 30) to fully propagate.
  </Step>
</Steps>

### Grant access to provision tenant root scoped role assignments (optional)

If you need C1 to provision role assignments at the Azure **tenant root scope** (`/`) — that is, role assignments that appear as **"Root (inherited)"** throughout your Azure hierarchy — the app requires an additional **User Access Administrator** assignment at that scope.

<Note>
  The **Tenant Root Group** visible in the Azure Portal is a management group, not the actual tenant root scope. Role assignments made there will not grant the necessary permission. This setup must be done using the Azure CLI.
</Note>

<Warning>
  A user with the **Global Administrator** permission in Azure must perform this task.
</Warning>

<Steps>
  <Step>
    Elevate the Global Administrator's own access to the tenant root scope. Run the following command using the Azure CLI:

    ```sh theme={null}
    az rest --method POST \
      --url "https://management.azure.com/providers/Microsoft.Authorization/elevateAccess?api-version=2016-07-01"
    ```
  </Step>

  <Step>
    Assign the **User Access Administrator** role to your app at the tenant root scope. Replace `<app-object-id>` with the **Object ID** of your Entra app registration (found in Entra ID → App registrations → your app → Overview):

    ```sh theme={null}
    az role assignment create \
      --role "User Access Administrator" \
      --assignee-object-id "<app-object-id>" \
      --assignee-principal-type ServicePrincipal \
      --scope "/"
    ```
  </Step>

  <Step>
    Revoke the Global Administrator's temporary elevated access using one of the following methods:

    **Option A — Azure Portal:** Navigate to **Microsoft Entra ID** → **Properties**, set **Access management for Azure resources** back to **No**, and click **Save**.

    **Option B — Azure CLI:**

    ```sh theme={null}
    az rest --method DELETE \
      --url "https://management.azure.com/providers/Microsoft.Authorization/elevateAccess?api-version=2016-07-01"
    ```
  </Step>
</Steps>

To remove this permission from the app in the future:

```sh theme={null}
az role assignment delete \
  --role "User Access Administrator" \
  --assignee "<app-object-id>" \
  --scope "/"
```

**Done.** Next, move on to the connector configuration instructions.

### Optional configuration

Three additional settings are available in the connector's configuration form:

* **Azure cloud** — the Azure cloud environment to connect to: `public` (default), `usgovernment`, or `china`. This is the only way to point the connector at the US Government or China clouds instead of the public Azure cloud.
* **Skip roles with no assignments** — when enabled, only Azure roles with at least one active assignment are synced, instead of every role definition in scope.
* **Sync sub-resources** — which kinds of nested sub-resource to sync. Leave empty to sync none. Sub-resources are not a separate resource type: each one is synced as an **Azure resource**, nested under the Azure resource that owns it.

<Note>
  **Sync sub-resources only has an effect if the Azure resources resource type is enabled.**

  Sub-resources are synced as children of the Azure resources that own them — a blob container is
  synced under its storage account. If Azure resources are not being synced, there are no parents
  to sync them under, and enabling this setting does nothing.
</Note>

#### Supported sub-resource types

Each value below is a scope Azure documents as directly role-assignable. Everything listed here
syncs as an Azure resource — the values select which kinds are included, not which resource types
exist. Select only the ones you need: **each selected value costs one extra API call per parent
resource, on every sync.**

| Value | Syncs, as an Azure resource | Nested under |
| :- | :- | :- |
| `blob_containers` | Blob containers | Storage accounts |
| `storage_queues` | Storage queues | Storage accounts |
| `storage_tables` | Storage tables | Storage accounts |
| `service_bus_queues` | Service Bus queues | Service Bus namespaces |
| `service_bus_topics` | Service Bus topics | Service Bus namespaces |
| `event_hubs` | Event hubs | Event Hubs namespaces |
| `subnets` | Subnets | Virtual networks |

Not currently supported: Key Vault secrets, keys and certificates (listing them requires a Key
Vault data-plane connection, which is separate from the Azure Resource Manager access this
connector uses), Azure Files shares, Service Bus topic subscriptions, and Event Hubs consumer
groups.

#### Why sync sub-resources

Azure allows a role to be assigned directly at a sub-resource scope. A common example is granting
`Storage Blob Data Contributor` on a single blob container rather than on the whole storage
account:

```
/subscriptions/{id}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{account}/blobServices/default/containers/{container}
```

Assignments made at those scopes are only visible in C1 if the sub-resource itself is synced.
With this setting disabled, access granted on an individual container does not appear anywhere,
even though the storage account above it syncs normally.

The trade-off is API calls: each selected type costs one additional request per parent resource
per sync — selecting all three storage types means three extra calls for every storage account in
the tenant. That is why nothing is selected by default, and why the types are chosen individually
rather than with a single on/off switch.

<Note>
  If a resource cannot be listed because it does not offer that sub-resource at all — a disabled
  storage account, or a Premium account that has no queue or table service — the connector skips it
  and continues syncing everything else. A permissions error is different: if the connector is not
  allowed to list a resource's children, the sync fails, because silently syncing less access than
  exists would be misleading.
</Note>

## Configure the Azure connector

<Warning>
  To complete this task, you'll need:

  * The **Connector Administrator** or **Super Administrator** role in C1
  * Access to the set of Azure credentials generated by following the instructions above
</Warning>

<Tabs>
  <Tab title="Cloud-hosted">
    **Follow these instructions to use a built-in, no-code connector hosted by C1.**

    <Steps>
      <Step>
        In C1, navigate to **Apps** > **Connectors** and click **Add connector**.
      </Step>

      <Step>
        Search for **Azure** and click **Add**.
      </Step>

      <Step>
        Choose where to add the connector: **Create a new app**, or **Add to an existing app** (then select the app).

        If you're creating a new app, choose whether to link it to an application discovered from your identity provider: select **Yes** and pick the IdP application, or **No** to continue with just the connector.
      </Step>

      <Step>
        Set the connector's **Name** and, optionally, a **Description**.
      </Step>

      <Step>
        Click the pencil icon next to **Owners** to choose who can configure and manage this connector.
      </Step>

      <Step>
        Click **Add**. The connector is created and its configuration page opens.
      </Step>

      <Step>
        Find the **Settings** area of the page and click **Edit**.
      </Step>

      <Step>
        Paste the application (client) ID into the **Azure client ID** field.
      </Step>

      <Step>
        Paste the client secret into the **Azure client secret** field.
      </Step>

      <Step>
        Paste the directory (tenant) ID into the **Azure tenant ID** field.
      </Step>

      <Step>
        Click **Save**.
      </Step>

      <Step>
        Finally, tell the connector where to find the identities that will be used for this app in C1.

        1. In the **Shared identity source** area of the page, click **Edit**.
        2. Select your Entra connector.
        3. Under **Only import identities of type**, select **Users**, **Groups**, and **Apps**. Azure reports both managed identities and enterprise applications as service principals, so role assignments held by an enterprise application only resolve when **Apps** is selected.
        4. **Optional.** Limit the identities pulled from the connector you selected to only those with a certain entitlement by setting the entitlement.
        5. Click **Save**.
      </Step>

      <Step>
        The connector's label changes to **Syncing**, followed by **Connected**. You can view the logs to ensure that information is syncing.
      </Step>
    </Steps>

    **Done.** Your Azure connector is now pulling access data into C1.
  </Tab>

  <Tab title="Self-hosted">
    **Follow these instructions to use the Azure connector, hosted and run in your own environment.**

    When running in service mode on Kubernetes, a self-hosted connector maintains an ongoing connection with C1, automatically syncing and uploading data at regular intervals. This data is immediately available in the C1 UI for access reviews and access requests.

    ### Resources

    * [Official download center](https://dist.conductorone.com/ConductorOne/baton-azure): For stable binaries (Windows/Linux/macOS) and container images.

    ### Step 1: Set up a new Azure connector

    <Steps>
      <Step>
        In C1, navigate to **Apps** > **Connectors** and click **Add connector**.
      </Step>

      <Step>
        Search for **Baton** and click **Add**.
      </Step>

      <Step>
        Choose where to add the connector: **Create a new app**, or **Add to an existing app** (then select the app).

        If you're creating a new app, choose whether to link it to an application discovered from your identity provider: select **Yes** and pick the IdP application, or **No** to continue with just the connector.
      </Step>

      <Step>
        Set the connector's **Name** and, optionally, a **Description**.
      </Step>

      <Step>
        Click the pencil icon next to **Owners** to choose who can configure and manage this connector.
      </Step>

      <Step>
        Click **Add**. The connector is created and its configuration page opens.
      </Step>

      <Step>
        In the **Settings** area of the page, click **Edit**.
      </Step>

      <Step>
        Click **Rotate** to generate a new Client ID and Secret.

        Carefully copy and save these credentials. We'll use them in Step 2.
      </Step>
    </Steps>

    ### Step 2: Create Kubernetes configuration files

    Create two Kubernetes manifest files for your Azure connector deployment:

    #### Secrets configuration

    ```yaml expandable theme={null}
    # baton-microsoft-azure-secrets.yaml
    apiVersion: v1
    kind: Secret
    metadata:
      name: baton-microsoft-azure-secrets
    type: Opaque
    stringData:
      # C1 credentials
      BATON_CLIENT_ID: <C1 client ID>
      BATON_CLIENT_SECRET: <C1 client secret>
      
      # Azure credentials
      BATON_ENTRA_CLIENT_ID: <Azure application (client) ID>
      BATON_ENTRA_CLIENT_SECRET: <Azure client secret>
      BATON_ENTRA_TENANT_ID: <Azure directory (tenant) ID>
      BATON_EXTERNAL_SYNC_MODE: true
      BATON_EXTERNAL_RESOURCE_C1Z: <The path to the c1z file to sync external Baton resources with>
      BATON_EXTERNAL_RESOURCE_ENTITLEMENT_ID_FILTER: <Optional. The entitlement that external users, groups must have access to sync external Baton resources>

      # Optional: include if you want C1 to provision access using this connector
      BATON_PROVISIONING: true
    ```

    See the connector's README or run `--help` to see all available configuration flags and environment variables.

    #### Deployment configuration

    ```yaml expandable theme={null}
    # baton-microsoft-azure.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: baton-microsoft-azure
      labels:
        app: baton-microsoft-azure
    spec:
      selector:
        matchLabels:
          app: baton-microsoft-azure
      template:
        metadata:
          labels:
            app: baton-microsoft-azure
            baton: true
            baton-app: microsoft-azure
        spec:
          containers:
          - name: baton-microsoft-azure
            image: public.ecr.aws/conductorone/baton-azure:latest
            imagePullPolicy: IfNotPresent
            env:
            - name: BATON_HOST_ID
              value: baton-microsoft-azure
            envFrom:
            - secretRef:
                name: baton-microsoft-azure-secrets
    ```

    ### Step 3: Deploy the connector

    <Steps>
      <Step>
        Create a namespace in which to run C1 connectors (if desired), then apply the secret config and deployment config files.
      </Step>

      <Step>
        Check that the connector data uploaded correctly. In C1, click **Apps**. On the **Managed apps** tab, locate and click the name of the application you added the Azure connector to. Azure data should be found on the **Entitlements** and **Accounts** tabs.
      </Step>
    </Steps>

    **Done.** Your Azure connector is now pulling access data into C1.
  </Tab>
</Tabs>
