# Connect Azure

> Connect your Azure subscriptions with one command in Azure Cloud Shell, which creates a read-only app in your own Azure directory and hands its credential to LevelFour.

Source: https://docs.levelfour.ai/connectors/azure


LevelFour reads Azure as a service principal you own: an app registration named `LevelFour` in your own Azure directory (your Microsoft Entra tenant), holding read-only roles. One command creates it, grants the roles and sends its credential to LevelFour, so you copy nothing by hand. Its secret works only in your directory, and you can remove it at any time.

## Prerequisites

* LevelFour admin rights, to generate the command.
* An Azure identity that can register applications in Microsoft Entra ID and is an Owner of the subscriptions or management group you connect. Members can register applications unless your directory turned that off; if it did, the **Application Developer** role is enough.
* On a Microsoft Customer Agreement, run the command in the directory that holds the billing account. Azure gives billing roles only to identities from that directory.

Billing rights are optional here. They give LevelFour your negotiated prices, and a finance admin can add them later. See [Billing data](/connectors/azure/billing-data).

> **Note**
>
> Azure Government and other national clouds are not supported.

## Connect

### Open the connect dialog

Open **Connect Providers > Azure** in the dashboard. The first time, the connect dialog opens by itself. Otherwise click **Connect Azure**, or **Add an Azure account** when one is already connected.

The dialog shows a command that holds a one-time token. The token works for 60 minutes. After that, close the dialog and open it again for a fresh command.

### Run it in Azure Cloud Shell

It's one paste on your side:

1. In the dialog, click **Copy**.
2. Click **Open Azure Cloud Shell**. In Cloud Shell, pick **Bash**, and **No storage account required** if it asks.
3. Paste and press Enter.

Copy the command from the dashboard exactly as shown. It has this shape:

```bash
curl -fsSL -H "Authorization: Bearer ONE_TIME_TOKEN_FROM_DASHBOARD" \
  "https://api.levelfour.ai/api/v1/connectors/azure/setup/CONNECTION_ID/script" \
  -o levelfour_azure.sh && bash levelfour_azure.sh
```

Cloud Shell signs you in as yourself, so the command acts with your Azure permissions and no others.

### Choose what LevelFour reads

The script asks which subscriptions to connect:

```text
Connect Azure directory Contoso to LevelFour.
Which subscriptions should LevelFour read?
  1) This subscription only (00000000-1111-2222-3333-444444444444)
  2) All 4 subscriptions you can see in this directory
  3) A management group, which also covers subscriptions added to it later
Choose 1, 2 or 3 [2]:
```

| Answer | What LevelFour reads                                                                                                                                  |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `1`    | The subscription Cloud Shell is set to                                                                                                                |
| `2`    | Every enabled subscription you can see in this directory, as of now. Press Enter for this one                                                         |
| `3`    | Every subscription under a management group, including ones added later. The script asks for the group's ID and defaults to the root management group |

> **Tip**
>
> Pick a management group if your organization creates subscriptions often. LevelFour reads new ones without you running the command again. On a pay-as-you-go account, run the command again from the LevelFour page to give a new subscription its cost export, as [Billing data](/connectors/azure/billing-data#pay-as-you-go-the-daily-cost-export) explains.

To answer up front, set `LEVELFOUR_SCOPE` to `1`, `2` or `3` before you paste the command, and `LEVELFOUR_MANAGEMENT_GROUP` to the group's ID for `3`.

On a pay-as-you-go account the script also asks which Azure region should store your cost export, and suggests the one that holds most of your resource groups. Set `LEVELFOUR_EXPORT_REGION`, for example to `westeurope`, to answer that up front. [Billing data](/connectors/azure/billing-data#pay-as-you-go-the-daily-cost-export) lists what the export creates.

### Check what LevelFour can read

The script creates the service principal and grants its roles, then waits for Azure to apply them, which can take a few minutes. When LevelFour can read your subscriptions, it prints:

```text
Connected. Return to the LevelFour tab: your Azure costs start importing now.
```

Back on the LevelFour tab, the dialog moves to **Check access** by itself. It lists what LevelFour can read and, under **Not granted**, each role the command could not grant, with what that changes:

* **Reservations Reader** or **Savings plan Reader**: optional. Azure lets only a Global Administrator with elevated access grant them, and billing access covers your reservations and savings plans instead. See [Commitments](/connectors/azure/commitments).
* **Enrollment reader** or **Billing account reader**: optional. Costs import without them, at subscription scope. Click **Get the command for a billing admin** and have a billing account owner run it, as [Billing data](/connectors/azure/billing-data) explains.
* **Reader** or **Cost Management Reader**: LevelFour reads no cost on that scope. Run the command again from the LevelFour page as an Owner of that scope.
* **Cost export**, **Cost export storage** or **Storage Blob Data Reader**, on a pay-as-you-go account: no cost imports for that subscription. Run the command again from the LevelFour page, or set the export up by hand as [Billing data](/connectors/azure/billing-data#pay-as-you-go-the-daily-cost-export) describes.
* **Cost export** on a management group: Azure failed to list the group's subscriptions, or listed more than the command reads, so it created no export and no storage. Run the command again. If it fails the same way, choose `2` and connect the subscriptions instead of the group.

Cloud Shell prints the same roles on lines that start with `Not granted:`.

On a pay-as-you-go account, once LevelFour has accepted the connection, the command asks Azure for the cost of past months. Cloud Shell then lists each month Azure refused, and the current month still imports. To ask for those months again, run the command again from the LevelFour page, or export them by hand as [Billing data](/connectors/azure/billing-data#pay-as-you-go-the-daily-cost-export) describes.

## What the command does

1. Creates an app registration named `LevelFour`, for your directory only, and its service principal, or reuses the one an earlier run created.
2. Adds a client secret valid for two years. If your directory limits how long a secret may last, run the command again after `export LEVELFOUR_SECRET_YEARS=1`.
3. Grants these roles to the `LevelFour` service principal:

| Role                                        | Scope                                                         | What it allows                                 | Needed |
| ------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------- | ------ |
| Reader                                      | Each subscription or management group you chose               | List resources, their metrics and budgets      | Yes    |
| Cost Management Reader                      | The same scopes                                               | Read cost and usage                            | Yes    |
| Reservations Reader                         | Your whole directory (`/providers/Microsoft.Capacity`)        | List reservations and their utilization        | No     |
| Savings plan Reader                         | Your whole directory (`/providers/Microsoft.BillingBenefits`) | List savings plans                             | No     |
| Enrollment reader or Billing account reader | Your billing account                                          | Read negotiated prices, purchases and invoices | No     |

4. When you hold billing rights, grants the billing role. See [Billing data](/connectors/azure/billing-data).
5. On a pay-as-you-go account, creates a small storage account and a daily cost export in your subscription. See [Pay-as-you-go](/connectors/azure/billing-data#pay-as-you-go-the-daily-cost-export).
6. Checks that the new service principal can read your subscriptions, then sends its directory (tenant) ID, client ID and secret to LevelFour over HTTPS with the one-time token.
7. Once LevelFour holds the new secret, deletes the older LevelFour secrets on the app, so only the new one works. To keep them, for example when two LevelFour environments read the same directory, run `export LEVELFOUR_KEEP_OLD_SECRETS=1` before the command.

The secret never appears on screen. If Azure has not applied the roles within five minutes, or LevelFour refuses the report, the script deletes the secret it created and stops.

> **Note**
>
> Azure assigns the two directory-wide reader roles only to someone with elevated access, so most runs print `Not granted` for them. LevelFour then reads your commitments through the billing role. See [Commitments](/connectors/azure/commitments).

The command downloads the script to `levelfour_azure.sh`, then runs it. To read the script first, paste only the `curl` part, everything before `&& bash levelfour_azure.sh`. Then read the file and run it while the token still works:

```bash
less levelfour_azure.sh
bash levelfour_azure.sh
```

The file holds the one-time token, so delete it when you are done.

## Without the command

If your organization does not allow scripts in Cloud Shell, create the service principal in the Azure portal and paste its credential instead.

1. Sign in to the [Azure portal](https://portal.azure.com) as an Owner of the subscriptions you want to connect.
2. Open **Microsoft Entra ID**, then **App registrations**, and click **New registration**. Name it `LevelFour`, keep **Accounts in this organizational directory only**, and click **Register**. Copy the **Application (client) ID** and the **Directory (tenant) ID**.
3. Open **Certificates & secrets**, click **New client secret**, choose the longest expiry your policy allows, and copy the secret **Value**. Azure shows it only once.
4. On each subscription, or on a management group to cover every subscription under it, open **Access control (IAM)**, click **Add role assignment**, and assign **Reader** and **Cost Management Reader** to `LevelFour`.
5. On an Enterprise Agreement or a Microsoft Customer Agreement, ask a billing administrator to add `LevelFour` as **Enrollment reader** or **Billing account reader**, so LevelFour reads your negotiated prices and your reservations.
6. In LevelFour, open **Connect Providers > Azure**, click **Add service principal**, paste the three values, then click **Connect account**. Add the secret's expiry date too, and LevelFour reminds you before it lapses.

> **Warning**
>
> On a pay-as-you-go account, LevelFour reads cost from a daily export, which only the command sets up. Without it, a pay-as-you-go connection shows resources but no cost. Run the command once, or create the export by hand as [Billing data](/connectors/azure/billing-data#pay-as-you-go-the-daily-cost-export) describes.

## Confirming it worked

> **Success**
>
> Within a minute of `Connected.`, the Azure connection moves from **Validating** to **Importing**. The first costs appear within about 30 minutes, and the import then reaches back 13 months.

LevelFour lists every subscription the service principal can read and refreshes the list on each sync. Under a management group, new subscriptions appear on their own.

On a pay-as-you-go account, the current month arrives first. The command also asks Azure to export the 13 months before it, and each one appears as Azure writes it. See [Billing data](/connectors/azure/billing-data#pay-as-you-go-the-daily-cost-export).

## Rotating the secret

The secret expires after two years, or sooner if your directory sets a shorter limit. LevelFour reminds you before it lapses. To rotate it, open **Connect Providers > Azure**, click **Run the command again** on the Azure account and run the command in Cloud Shell. It reuses the `LevelFour` app, adds a fresh secret, sends that one and then deletes the older LevelFour secrets, so a secret that leaked stops working. If Azure refuses to delete one, the command says so: delete it under **App registrations > LevelFour > Certificates & secrets**.

## Removing access

Delete the app registration, which also deletes its service principal:

```bash
az ad app delete --id "$(az ad app list --display-name LevelFour --query '[0].appId' -o tsv)"
```

LevelFour stops reading at once. Azure keeps the role assignments, listed as **Identity not found**. Remove them under **Access control (IAM) > Role assignments** on each subscription or management group you connected.

Disconnecting Azure in LevelFour deletes only LevelFour's sealed copy of the secret: the app and its roles stay in your directory until you run this command. On a pay-as-you-go account, also remove the cost export: see [Removing the export](/connectors/azure/billing-data#removing-access). To have your Azure records removed from LevelFour, disconnect Azure in **Connect Providers > Azure** and choose to remove its data.

## Troubleshooting

**`The Azure CLI is required. Run this in Azure Cloud Shell`**
The script ran somewhere without the Azure CLI. Open [Azure Cloud Shell](https://shell.azure.com) and paste the command there.

**`Azure did not let your user create the LevelFour app`**
Your directory stops members registering applications. Run the command with the **Application Developer** or **Application Administrator** role, or have an admin register an app named `LevelFour` and add you as its owner, then run the command again.

**`Only an owner of the LevelFour app or an Application Administrator can`**
Someone else connected this Azure account first, so the app is theirs. Have them run the command, or add yourself as an owner under **App registrations > LevelFour > Owners**. To add billing access, you need neither: click **Get the command for a billing admin**.

**`Could not create a secret for LevelFour`**
Your directory limits secret lifetime or blocks secrets on app registrations. Run `export LEVELFOUR_SECRET_YEARS=1` and paste the command again. If your directory allows no client secrets at all, ask your Entra administrator for an exception for the `LevelFour` app.

**`Could not assign Reader on any of the chosen scopes`**
You are not an Owner of the subscriptions or management group you chose. Run the command as someone who is, or pick a scope you own.

**`Azure has not applied the roles yet`**
Azure took longer than five minutes to apply the new roles. Wait a few minutes, then run the command again from the LevelFour page. It reuses the same app.

**`LevelFour did not accept the connection`**
The one-time token expired or was already used. Close the dialog, open it again for a fresh command and run that.

**The connection shows an error that Azure refused the service principal.**
The secret was deleted, expired or mistyped, or the directory (tenant) ID does not match it. Click **Run the command again** on the Azure account and run it, which sends a fresh secret.

**The connection says the service principal cannot see any subscription.**
The roles are missing or have not applied yet. Check **Access control (IAM)** on a subscription for **Reader** and **Cost Management Reader** on `LevelFour`.

**Costs leave out reservations, savings plans or Marketplace charges.**
Azure reports purchases only at your billing account. Add the billing role as [Billing data](/connectors/azure/billing-data) describes.

## Next

* [Billing data](/connectors/azure/billing-data) gets your negotiated prices on an Enterprise Agreement or Microsoft Customer Agreement, and covers the pay-as-you-go export
* [Commitments](/connectors/azure/commitments) shows your reservations and savings plans, and how to buy or renew one
* [Automated Savings](/connectors/azure/automated-savings) lets LevelFour apply a saving you approved
* [Virtual tags](/virtual-tags) group your Azure cost by team, product, or any other dimension, alongside your other providers
