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

# Microsoft Azure (NHI)

> Give a read-only Entra app the Reader role so Lumos can inventory Azure managed identities, service principals and agent identities, the workloads they run on, and the RBAC roles they hold.

## What you're setting up

You're registering a read-only application in your Microsoft Entra tenant and giving it the built-in **Reader** role in Azure. Lumos signs in as that application with the OAuth 2.0 client credentials grant and reads Azure Resource Manager (ARM), mostly through Azure Resource Graph. Lumos uses it to inventory the non-human identities that hold Azure RBAC roles: managed identities, agent identities and other service principals, the workloads they run on, and the roles, actions and data actions they hold at each scope.

There is no custom policy to write. Reader is the only role the connection needs, and you decide what Lumos scans by where you assign it.

**Recommended setup.** Reader assigned to the app on the **Tenant Root Group**. That covers every subscription in the tenant, including ones created later, and it's the setup the steps below walk through. To give Lumos less, see [Reducing access](#reducing-access) for what each reduction costs.

**What Lumos does NOT do:**

* **No writes.** Reader grants `*/read` on the control plane and nothing else. Lumos cannot create, change or delete resources, role assignments or policies.
* **No content reads.** Lumos does not query blob contents, Key Vault secrets, keys or certificates, database rows, queue messages or logs. Reader carries no data actions, though it allows more control-plane reads than Lumos makes. [What the Reader role is used for](#what-the-reader-role-is-used-for) lists every read Lumos makes.
* **No key listing.** Calls such as a storage account's `listKeys` are actions outside `*/read`, so Reader cannot retrieve storage keys, connection strings or SAS rules.
* **No Microsoft Graph access.** The app requests only the `https://management.azure.com/.default` scope. It needs no Graph permissions and no admin consent. The default **User.Read** permission on a new registration isn't used and can be removed.

***

## Prerequisites

Before you start, make sure you have:

* **Application Developer** (or higher) in Microsoft Entra, to register the app.
* **Owner**, **User Access Administrator** or **Role Based Access Control Administrator** on the Tenant Root Group, to assign the Reader role. A Global Administrator without one of these can grant it to themselves with **Access management for Azure resources** (see Step 3).
* The Lumos integration page open. You'll paste the **Tenant ID**, **Client ID** and **Client Secret** into it at the end.
* An Azure tenant in the global (commercial) Azure cloud. Azure Government and Azure China are not supported.

This integration reads Azure RBAC: which identities hold which roles, where. It does not read the credentials those identities authenticate with. Client secrets and certificates are stored in Microsoft Entra ID, not in Azure Resource Manager, and the [Entra ID (NHI)](https://docs.lumos.com/integrations/library/non-human/entra-id-nhi.md) integration reads them, along with the federated credentials on your service principals and managed identities. Connect both to see each identity's Azure access alongside its credentials.

***

## Step 1: Register the application

1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com/).
2. Go to **Entra ID → App registrations → New registration**.
3. Set a **Name** (e.g. `Lumos Azure NHI Read-Only`).
4. Under **Supported account types**, select **Single tenant only**.
5. Leave **Redirect URI** empty.
6. Click **Register**.
7. On the app's **Overview** page, copy the **Directory (tenant) ID** and the **Application (client) ID**. You'll paste both into Lumos in Step 4.

***

## Step 2: Create the client secret

1. In the app, go to **Certificates & secrets → Client secrets → New client secret**.
2. Set a **Description** (e.g. `Lumos Azure NHI`) and an **Expires** date. Microsoft caps this at 24 months.
3. Click **Add**, then copy the secret's **Value** (not the Secret ID). It is shown only once.

When the secret expires Lumos can no longer connect. Create a new secret before the expiry date and paste it into the integration.

***

## Step 3: Assign the Reader role on the Tenant Root Group

Lumos scans whatever the Reader assignment reaches. The Tenant Root Group is the management group above every other management group and subscription in the tenant, so Reader there covers every subscription, including ones created later.

1. In the [Azure portal](https://portal.azure.com/), go to **Management groups** and open the **Tenant Root Group**.
2. Go to **Access control (IAM) → Add → Add role assignment**.
3. On the **Role** tab, choose **Reader** and click **Next**.
4. On the **Members** tab, choose **User, group, or service principal**, click **Select members**, and search for the app by name.
5. Click **Review + assign**.

Lumos checks that the app can read at least one whole subscription: every resource type, plus the subscription's role assignments and role definitions. Reader on the subscription or on a management group above it passes, whether it's assigned to the app directly or to a group the app belongs to. A broader role such as Contributor or Owner, or a custom role that reads every resource type, also passes. Reader assigned only on resource groups or individual resources does not.

**If you are a Global Administrator without an Azure role.** In the Azure portal, go to **Microsoft Entra ID → Properties**, set **Access management for Azure resources** to **Yes**, and save. Sign out and back in to pick up the change. This gives you User Access Administrator at the root scope `/`, which lets you assign Reader on the Tenant Root Group or anywhere below it. Set it back to **No** once the assignment is made.

**Optional: Reader at the root scope `/`.** The Tenant Root Group is a management group, not the root scope `/` above it. Role assignments made on tenant-level services, such as `/providers/Microsoft.Capacity`, are visible only to an app that holds Reader at `/`. Without it, everything else is unaffected; those assignments just don't appear in Lumos. Role assignments made at `/` itself and on management groups don't need this: Lumos sees them with Reader on any subscription below them. The portal can't assign roles at `/`, so use the Azure CLI:

1. Turn on **Access management for Azure resources** as described above, then sign out and back in.

2. Run the following, with the Application (client) ID from Step 1:

   ```bash theme={null}
   az role assignment create --assignee <client-id> --role Reader --scope "/"
   ```

3. Set **Access management for Azure resources** back to **No**.

Azure Resource Graph takes a short time to reflect a new role assignment (under a minute in our testing). If validation in Step 4 says the app can't see any Azure subscriptions, wait a few minutes and try again.

***

## Step 4: Connect in Lumos

1. In the Lumos app, go to **Integrations**, open the **Discover** tab, and search for **"Microsoft Azure (NHI)"**.
2. Paste the values you collected:
   * **Tenant ID:** Directory (tenant) ID from Step 1
   * **Client ID:** Application (client) ID from Step 1
   * **Client Secret:** secret Value from Step 2
3. Select **Connect**. When **Connected** appears, select **Complete**.

When Azure is reachable, Lumos checks the credentials and the app's access before saving. If Azure is throttling requests or unavailable at that moment, Lumos saves the connection without finishing the check; if the first sync then fails or comes back empty, recheck Step 3. When the check fails, the message names what to fix:

| Message | Fix |
| :- | :- |
| Azure rejected this app's sign-in | Check the Tenant ID, the client ID and secret, and that the secret hasn't expired |
| This app can't see any Azure subscriptions | The app holds no role on any subscription. Assign Reader on a subscription or management group (Step 3), then wait a few minutes |
| This app can't read a whole Azure subscription | The app's roles cover only some resource types, resource groups or resources. Assign Reader on a whole subscription or a management group above it |
| Azure denied this app access | Assign Reader to the app on each subscription or management group Lumos should scan (Step 3) |

After you connect, the connector's **"Integrations"** tab shows its sync status. **Synced** means the first run completed. See [NHI Connector Sync Status](https://docs.lumos.com/nhi-sync-status.md) for what each status means.

***

## What Lumos inventories

**Non-human accounts**

| Account type | What it is |
| :- | :- |
| System-assigned managed identity | An identity that belongs to one Azure resource. Lumos records the resource it runs on |
| User-assigned managed identity | A standalone identity resource. Lumos creates one account for each workload it is attached to, so an identity attached to three workloads appears three times. An identity attached to nothing appears once, as its own workload |
| Agent service principal | A service principal that Azure labels as an agent identity in its role assignments |
| Unclassified service principal | Any other service principal that holds an Azure role. Azure doesn't say what kind of service principal it is, so Lumos can't classify it further |

Service principals with no Azure role assignment are not listed. The [Entra ID (NHI)](https://docs.lumos.com/integrations/library/non-human/entra-id-nhi.md) integration lists service principals from the directory, whether or not they hold an Azure role.

**Workloads.** Lumos links a managed identity to the resource it runs on for these services: virtual machines and scale sets, Azure Arc servers, AKS clusters, App Service apps and slots, Container Apps and Container Apps jobs, Container Instances, Logic Apps, Data Factory, Automation accounts, API Management, AI Search, Batch pools, and Azure Machine Learning computes, online endpoints and batch endpoints. An identity on any other service is treated as its own workload.

**Roles and scopes.** Lumos reads built-in and custom role definitions, the actions and data actions each role grants (wildcards kept as written), and the role assignments that place them on a scope. Scopes are modelled as a tree: the tenant root, management groups, subscriptions, resource groups, and individual resources, plus tenant-level services. A role assigned on a scope is shown as reaching every scope beneath it. Individual resources appear only when they matter for access: resources with a role assigned directly on them, resources a custom role is limited to, and workloads.

***

## Sync behavior

* **Cadence:** Lumos syncs this integration every 4 hours by default. To change the schedule, contact your Lumos account team. You can also start a sync at any time with **"Sync"** on the connector's **"Integrations"** tab.
* **Authentication:** Lumos uses the client secret to request short-lived ARM access tokens with the OAuth 2.0 client credentials grant.
* **How Lumos reads:** almost every read is a tenant-wide Azure Resource Graph query, so Lumos makes one paged query per data set instead of one call per subscription. Azure Machine Learning computes are the exception: Resource Graph doesn't index them, so Lumos reads them per ML workspace through ARM.
* **Rate limits:** Resource Graph allows 15 queries per 5 seconds per caller. Lumos stays under that limit and retries when Azure throttles it.
* **Tenant size:** if the app can read more than 10,000 subscriptions, the sync fails instead of returning partial data. Resource Graph only answers from the first 10,000 subscriptions, so a partial inventory would look complete when it isn't.

***

## What the Reader role is used for

Lumos uses a small part of what Reader allows. Resource Graph returns only what the app can already read, so the scope of your Reader assignment is the scope of the scan.

| Read | Source | Used for |
| :- | :- | :- |
| Subscriptions | ARM Subscriptions - List (`Microsoft.Resources/subscriptions/read`) | Validating the connection |
| The app's own permissions on each subscription | ARM Permissions - List (no role needed; any caller can read its own) | Validating the connection |
| Management groups, subscriptions and resource groups | Resource Graph `resourcecontainers` | The scope tree |
| Resources with a managed identity, and user-assigned identities | Resource Graph `resources` | Managed identity accounts and their workloads |
| Batch pools | Resource Graph `batchresources` | Managed identities on Batch pools |
| Role assignments and role definitions | Resource Graph `authorizationresources` | Service principal accounts, roles, actions, data actions and assignments |
| Azure Machine Learning computes | ARM ML Compute - List (`Microsoft.MachineLearningServices/workspaces/computes/read`) | Managed identities on ML computes |

***

## Reducing access

The recommended setup gives Lumos a complete inventory of the tenant. You can narrow it by where you assign Reader, at the cost listed below. A narrower role than Reader won't help much: the connection check needs read access to every resource type on at least one whole subscription.

| Change | How | What you give up |
| :- | :- | :- |
| Scan specific management groups | In Step 3, assign Reader on each management group to scan instead of the Tenant Root Group | Subscriptions outside those management groups aren't scanned, and neither are the identities and role assignments in them |
| Scan specific subscriptions | In Step 3, assign Reader on each subscription to scan. Repeat for every subscription you add later | Same as above, and new subscriptions aren't covered until you assign Reader on them |

With either change, Lumos still reads role assignments made at `/` and on the management groups above each scanned subscription.

***

## Known limitations

* **No Entra credentials.** Client secrets and certificates are stored in Microsoft Entra ID. They, and federated credentials, come from the [Entra ID (NHI)](https://docs.lumos.com/integrations/library/non-human/entra-id-nhi.md) integration, not this one.
* **No last activity.** Every account shows as active. Azure exposes no status or last-used time for managed identities or service principals through ARM.
* **Roles held through an Entra group are not shown** on the group's member service principals. Resolving group membership needs Microsoft Graph, which this integration doesn't call.
* **Access granted outside Azure RBAC is not read:** Key Vault access policies, database Entra admins, and local keys or logins such as storage account keys, SAS rules, the Container Registry admin user and SQL logins. Identities that can retrieve a key still show through roles that include its `listKeys` action.
* **Role conditions (ABAC) are ignored.** A role assignment with a condition is shown as granting the role in full, which can overstate access.
* **PIM-eligible role assignments are not shown.** Only active assignments are read.
* **Deny assignments are not read.**
* **New Foundry agents** show their identity and roles but no workload, as they exist only in Foundry's own API.
* **Child resources** such as Automation runbooks are placed under their resource group, not under their parent resource.
* **Excluded actions (`notActions`) are listed, not subtracted.** A role such as Contributor shows `*` as an implied action. The actions it excludes, such as the authorization writes, are listed in the role's description in Lumos, not removed from what it implies.

***

## Security FAQ

**Does Lumos access data inside my Azure subscriptions (blobs, secrets, database rows)?**

No. Lumos reads only what [What the Reader role is used for](#what-the-reader-role-is-used-for) lists: subscriptions, the scope tree, resources with a managed identity, and role assignments and definitions. It does not query blob contents, Key Vault secrets, keys or certificates, database rows or logs.

The built-in Reader role grants the `*/read` control-plane actions and no data actions. Retrieving storage keys or connection strings needs actions such as `listKeys`, which are outside `*/read`. Reader does allow more reads than Lumos uses, so the limit there is what Lumos calls, not what the role allows.

**Can I use a custom role instead of Reader?**

Yes, if it reads every resource type plus role assignments and role definitions on at least one whole subscription; the connection check fails otherwise. A custom role limited to some resource types doesn't pass.

**Can I see what Lumos read?**

Partly. Azure's Activity Log records changes and actions, not reads, so Lumos's reads don't appear there. Each time Lumos signs in as the app to get a token, Microsoft Entra records it under **Service principal sign-ins** in the sign-in logs. Entra keeps sign-in logs for 7 days on Entra ID Free and 30 days on P1 or P2. To keep them longer, route them to a storage account with Azure Monitor.

**How are credentials handled?**

Lumos stores the client secret encrypted at rest in its secrets manager, separately for each connection, and uses it to request short-lived ARM access tokens with the OAuth 2.0 client credentials grant. The secret is the only long-lived credential involved. Rotate it in Entra and update it in Lumos as part of your normal rotation schedule, or sooner if you suspect compromise.

**Can I limit what Lumos scans?**

Yes, by where you assign Reader. [Reducing access](#reducing-access) lists the options and what each costs.

**Can I remove the integration later?**

Yes. Remove the app's Reader assignment, delete the client secret, or delete the app registration. Once the change takes effect, Lumos's reads fail. A removed role assignment can take several minutes to apply. Deleting the secret or the app doesn't revoke an access token that was already issued; it stays valid until it expires, usually within 60 to 90 minutes.
