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

# Azure

> Connect an Azure subscription to NOFire AI

Connecting an Azure subscription to NOFire AI enables automatic discovery of your Azure infrastructure and lets NOFire AI query inventory, cost, and policy-compliance data during investigations. Discovery is subscription-wide: NOFire AI reads every supported resource type across all regions in the subscription, with no per-region configuration.

NOFire AI authenticates with an [Entra ID service principal](https://learn.microsoft.com/entra/identity-platform/app-objects-and-service-principals) and requests read-only Azure RBAC roles. NOFire AI never writes to or modifies Azure resources.

## Prerequisites

* An Azure subscription, and its **subscription ID**.
* Permission to register an application in your Entra ID tenant (**Application Developer** or higher).
* Permission to assign RBAC roles on the subscription (**Owner** or **User Access Administrator**).

## Step 1: Register an application in Entra ID

<Accordion title="Create the app registration in the Azure portal" defaultOpen>
  1. Open the [Entra ID app registrations blade](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) and click **New registration**.
  2. Enter a name (for example `nofireai-readonly`), leave **Supported account types** on **Accounts in this organizational directory only**, and leave the redirect URI empty. NOFire AI signs in with client credentials, not a browser redirect.
  3. Click **Register**.
  4. On the app's **Overview** page, copy the **Application (client) ID** and the **Directory (tenant) ID**. You need both in Step 3.
  5. Go to **Certificates & secrets → Client secrets → New client secret**. Add a description, choose an expiry, and click **Add**.
  6. Copy the secret **Value** immediately. Azure shows it only once, and this is the client secret you enter in Step 3.

  <Warning>
    Client secrets expire. Note the expiry date and update the secret on your NOFire AI connection before it lapses, or discovery and Azure queries will start failing with an authentication error.
  </Warning>
</Accordion>

<Accordion title="Alternative: create the service principal with the Azure CLI" defaultOpen="false">
  This single command registers the application, creates a secret, and assigns the **Reader** role at subscription scope:

  ```bash theme={null}
  SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000"

  az ad sp create-for-rbac \
    --name nofireai-readonly \
    --role Reader \
    --scopes "/subscriptions/${SUBSCRIPTION_ID}"
  ```

  The output carries three of the four values you need in Step 3:

  ```json theme={null}
  {
    "appId": "<client ID>",
    "password": "<client secret>",
    "tenant": "<tenant ID>"
  }
  ```

  Then add the cost role from Step 2:

  ```bash theme={null}
  az role assignment create \
    --assignee "<appId>" \
    --role "Cost Management Reader" \
    --scope "/subscriptions/${SUBSCRIPTION_ID}"
  ```
</Accordion>

<Accordion title="Alternative: create the service principal with Terraform" defaultOpen="false">
  If you manage Azure with Terraform, the configuration below creates the application, a client secret, and both role assignments. It requires the `azuread` and `azurerm` providers.

  ```hcl theme={null}
  data "azurerm_subscription" "current" {}

  resource "azuread_application" "nofireai" {
    display_name = "nofireai-readonly"
  }

  resource "azuread_service_principal" "nofireai" {
    client_id = azuread_application.nofireai.client_id
  }

  resource "azuread_application_password" "nofireai" {
    application_id = azuread_application.nofireai.id
    display_name   = "nofireai-discovery"
  }

  resource "azurerm_role_assignment" "reader" {
    scope                = data.azurerm_subscription.current.id
    role_definition_name = "Reader"
    principal_id         = azuread_service_principal.nofireai.object_id
  }

  resource "azurerm_role_assignment" "cost_management_reader" {
    scope                = data.azurerm_subscription.current.id
    role_definition_name = "Cost Management Reader"
    principal_id         = azuread_service_principal.nofireai.object_id
  }

  output "azure_subscription_id" {
    value = data.azurerm_subscription.current.subscription_id
  }

  output "azure_tenant_id" {
    value = data.azurerm_subscription.current.tenant_id
  }

  output "azure_client_id" {
    value = azuread_application.nofireai.client_id
  }

  output "azure_client_secret" {
    value     = azuread_application_password.nofireai.value
    sensitive = true
  }
  ```

  Apply, then read the values for Step 3:

  ```bash theme={null}
  terraform apply
  terraform output azure_client_id
  terraform output -raw azure_client_secret
  ```
</Accordion>

## Step 2: Assign read-only roles

Assign these built-in roles to the service principal at **subscription scope**. If you used the CLI or Terraform path above, they are already assigned.

| Role                       | Why NOFire AI needs it                                                    |
| -------------------------- | ------------------------------------------------------------------------- |
| **Reader**                 | Resource discovery, inventory queries, and policy-compliance state        |
| **Cost Management Reader** | Cost and forecast queries, which the **Reader** role alone does not grant |

<Accordion title="Assign the roles in the Azure portal" defaultOpen="false">
  1. Open **Subscriptions** and select the subscription you are connecting.
  2. Go to **Access control (IAM) → Add → Add role assignment**.
  3. Select the **Reader** role, click **Next**, choose **User, group, or service principal**, and select your `nofireai-readonly` application.
  4. Click **Review + assign**.
  5. Repeat for **Cost Management Reader**.
</Accordion>

<Note>
  Azure RBAC reader roles grant access by scope (subscription, resource group, or resource), not by data category, so a role granted at subscription scope can read every resource beneath it. To narrow what NOFire AI sees, assign the roles at resource-group scope instead and connect one subscription per environment.
</Note>

## Step 3: Add the Azure connection to NOFire AI

<Accordion title="Navigate to the Connections tab" defaultOpen>
  1. In the NOFire AI dashboard, go to **Connections**.
  2. Click **Connect** on the **Azure** card.
  3. Enter your connection details:
     * **Name**: a descriptive label (for example `production`)
     * **Subscription ID**: the subscription GUID to discover
     * **Tenant ID**: the directory (tenant) GUID, or a verified domain such as `contoso.onmicrosoft.com`
     * **Client ID**: the application (client) GUID from Step 1
     * **Client secret**: the secret value from Step 1
  4. Click **Save**.

  NOFire AI tests the credentials before saving the connection. The client secret is stored encrypted and is never displayed again.
</Accordion>

Once the connection is saved, NOFire AI validates the service principal, discovers the subscription's supported resources and the relationships between them, and sets the connection status to **READY**. You can watch the status on the Connections dashboard.

## What NOFire AI discovers

| Category         | Resource types                                                                                                                               |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **Organization** | Resource Groups                                                                                                                              |
| **Compute**      | Virtual Machines, VM Scale Sets, AKS clusters, Azure Functions, App Service and Web Apps, Container Instances                                |
| **Storage**      | Storage Accounts, Managed Disks                                                                                                              |
| **Databases**    | Azure SQL Database (including read replicas), Cosmos DB accounts, PostgreSQL Flexible Servers, MySQL Flexible Servers, Azure Cache for Redis |
| **Network**      | Virtual Networks, Subnets, Network Security Groups, Public IP Addresses, Load Balancers, Application Gateways, NAT Gateways, Route Tables    |

Discovered resources join the same context graph as your Kubernetes state from [NOFire Edge](/edge/introduction) and any other connected cloud or observability source, so an investigation can follow a failing workload through to the Azure resources behind it.

## What NOFire AI queries during investigations

| Capability             | What NOFire AI reads                                                 |
| ---------------------- | -------------------------------------------------------------------- |
| **Resource inventory** | Resource attributes, configuration, and tags across the subscription |
| **Cost analysis**      | Spend queries, forecasts, and cost-anomaly detection                 |
| **Compliance**         | Azure Policy compliance state and per-policy evaluation results      |

## Troubleshooting

<AccordionGroup>
  <Accordion title="Connection test fails with an authentication error">
    Confirm the tenant ID, client ID, and client secret all belong to the same app registration, and that the secret has not expired. Re-copy the secret **Value**, not the secret ID. The portal displays both, and only the value works.
  </Accordion>

  <Accordion title="Connection saves but discovers nothing">
    The service principal is authenticating but has no **Reader** assignment on that subscription. Check **Access control (IAM) → Role assignments** on the subscription and confirm the assignment is at subscription scope, not on a single resource group that holds no supported resources.
  </Accordion>

  <Accordion title="Cost queries fail while inventory works">
    **Cost Management Reader** is missing. Cost and forecast queries need it in addition to **Reader**.
  </Accordion>
</AccordionGroup>

## Multiple subscriptions

Add one connection per subscription. Subscriptions can share a single Entra ID app registration: assign the same service principal the **Reader** and **Cost Management Reader** roles on each subscription, then create a connection per subscription using the same tenant ID, client ID, and client secret with a different subscription ID.
