> For the complete documentation index, see [llms.txt](https://docs.p0.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.p0.dev/integrations/directory-integrations/microsoft-entra-id/viewing-security-perimeter-logs.md).

# Viewing Security perimeter logs

The P0 Security perimeter for Microsoft Entra ID runs in your own Azure subscription as a containerized Azure Function app (Node.js). It is the IAM-layer [P0 connector](/readme/connectors.md) for Entra ID: the only component authorized to write Entra role and group assignments, so its logs are the source of truth when you need to confirm that a grant or revoke happened, or to investigate why one was rejected.

This guide shows you how to read those logs. The perimeter is a standard Azure Function app, so most of this is general Azure Function log access. The [What P0 writes to the logs](#what-p0-writes-to-the-logs) section explains the P0-specific entries to look for.

## Prerequisites

* Access to the [Azure portal](https://portal.azure.com) for the subscription where the perimeter is installed.
* The name and resource group of the perimeter's function app. The function app is created during [Entra ID setup](/integrations/directory-integrations/microsoft-entra-id.md); if you are unsure of its name, ask the person who installed the integration.
* At least the **Reader** role on the function app to view the **Log stream**, and **Monitoring Reader** on the linked Application Insights instance to run log queries. Streaming logs with the Azure CLI also requires permission to read the app's publishing credentials.

{% hint style="info" %}
Application Insights is the recommended way to view perimeter logs. It retains history, works when the app scales to multiple instances, and supports queries. Use the **Log stream** when you want to watch activity live.
{% endhint %}

## Option 1: Stream logs live in the Azure portal

Use this option to watch the perimeter as a request comes in.

1. In the [Azure portal](https://portal.azure.com), open your function app.
2. In the left pane, under **Monitoring**, select **Log stream**.
3. Select **Application logs**. The window connects to the live log service and displays entries as the perimeter writes them.
4. Trigger an access request in P0 that targets this Entra tenant, then watch the stream for the corresponding entry.

{% hint style="info" %}
The built-in **Log stream** shows a single instance. If the perimeter has scaled out, use Live Metrics in Application Insights (Option 2) to see all instances.
{% endhint %}

## Option 2: View and query logs in Application Insights

Application Insights stores the perimeter's log history and lets you query it.

### Watch near-real-time activity

1. Open your function app and select **Application Insights** under **Settings**, then open the linked instance.
2. Select **Live Metrics** to view log entries and metrics in near real time across all instances. Sampled log entries appear under **Sample Telemetry**.

### Query historical logs

1. In the Application Insights instance, select **Logs** under **Monitoring**.
2. Run a query against the `traces` table, which holds the log messages the perimeter writes. For example, to see the most recent entries:

   ```kusto
   traces
   | where timestamp > ago(1h)
   | order by timestamp desc
   | project timestamp, message, severityLevel, operation_Name
   ```
3. To see only errors and warnings, filter on the log level:

   ```kusto
   traces
   | where timestamp > ago(1d)
   | where customDimensions.LogLevel in ("Error", "Warning")
   | project timestamp, message, customDimensions.LogLevel, operation_Name
   | order by timestamp desc
   ```
4. To trace a single invocation end to end, correlate by operation ID:

   ```kusto
   traces
   | where operation_Id == "<operation-id>"
   | project timestamp, message
   | order by timestamp asc
   ```

{% hint style="info" %}
Telemetry can take up to 5 minutes to appear in **Logs** because the client batches data before sending it. Live Metrics has no such delay.
{% endhint %}

## Option 3: Stream logs from the command line

If you prefer a terminal, stream logs with the [Azure Functions Core Tools](https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local) or the Azure CLI.

With Core Tools:

```bash
func azure functionapp logstream <function-app-name>
```

With the Azure CLI:

```bash
az webapp log tail --name <function-app-name> --resource-group <resource-group>
```

Replace `<function-app-name>` and `<resource-group>` with your perimeter's values. Both commands print log entries to your terminal until you stop them.

## What P0 writes to the logs

P0 sends each grant and revoke to the perimeter as an HTTP request to one of four endpoints. Looking for these paths is the quickest way to find the operation you care about:

| Endpoint                            | Operation                  |
| ----------------------------------- | -------------------------- |
| `POST /api/create-role-assignment`  | Grant a Directory role     |
| `POST /api/delete-role-assignment`  | Revoke a Directory role    |
| `POST /api/create-group-membership` | Add a user to a group      |
| `POST /api/delete-group-membership` | Remove a user from a group |

When you investigate a specific request, search the logs around the time of the request and match it to one of these endpoints. A successful call returns a `2xx` status; a rejected call returns `401` or `403`. See [Troubleshooting](#troubleshooting) for what those mean.

{% hint style="info" %}
Log wording can change between perimeter versions. Filter by the endpoint path and the request timestamp rather than by an exact message string.
{% endhint %}

## Troubleshooting

If a request fails, the status code in the perimeter logs points to the cause:

| Status             | Meaning                                                                       | What to do                                                                                                                                                                                         |
| ------------------ | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401 Unauthorized` | The perimeter rejected P0's federated token. This is an installation problem. | Confirm that P0's federated credentials are authorized in the function app. Reinstall the integration if the credentials are missing.                                                              |
| `403 Forbidden`    | The perimeter's security policy rejected the request.                         | Check that you've configured the correct policies and permissions on the Entra ID integration, and that the perimeter's managed identity has the Graph permissions needed to write the assignment. |
| `5x` / no response | The perimeter couldn't reach Microsoft Graph or hit an internal error.        | Review the surrounding `Error`-level entries in `traces` for the underlying Graph error.                                                                                                           |

If you can't resolve the issue from the logs, contact <support@p0.dev> with the relevant log entries and the approximate time of the request.

## Related

* [Microsoft Entra ID](/integrations/directory-integrations/microsoft-entra-id.md)
* [Requesting Microsoft Entra ID access](/integrations/directory-integrations/microsoft-entra-id/requesting-access.md)
