# Power BI workspace access bootstrap

This guide walks through adding the **main Euno Power BI application** (the service principal you configure in Euno as **Client ID** / **Client secret**) as a **workspace Admin** across **many** workspaces, without using the Power BI UI for each workspace.

It complements [Step 4 in the main Power BI setup](/sources/business-intelligence/powerbi-integration.md#step-4-add-the-euno-powerbi-application-to-your-workspaces).

## Prerequisites (tenant)

Complete the main Power BI guide through **Step 3** (tenant settings, security groups, admin API toggles). In particular, for the bootstrap flow you need a **Fabric / Power BI admin** who can complete device-code sign-in.

## Step A — Create the bootstrap app registration

1. In [**Microsoft Entra admin center**](https://entra.microsoft.com/) → **App registrations** → **New registration**.
2. **Name:** e.g. `Euno Power BI workspace bootstrap`.
3. **Supported account types:** *Accounts in this organizational directory only*.
4. **Redirect URI:** leave empty (device code flow).
5. **Register** and note the **Application (client) ID** and **Directory (tenant) ID**.

### API permissions (bootstrap app)

1. **API permissions** → **Add a permission** → **Power BI Service** → **Delegated permissions**.
2. Add **`Tenant.ReadWrite.All`**.
3. **Grant admin consent** for the tenant (an administrator must consent for all users).

Do **not** add Power BI **Application** `Tenant.ReadWrite.All` on this app if you want to avoid the same service-principal admin API conflicts you can hit on the main app—**delegated** is enough for this script.

### Public client (device code)

1. **Authentication** → **Advanced settings** → **Allow public client flows** = **Yes**.
2. **Save**.

## Step B — Find the main Euno app’s **service principal object ID**

1. **Entra** → **Enterprise applications** → search for your main Euno app by name or **Application ID**.
2. Open it and copy **Object ID** (a UUID).

You will pass this to the script as `--principal-object-id`.

## Step C — Run the script

Download the script here.

{% file src="/files/x6pVSgXoPzj3jfhaGq85" %}

{% file src="/files/BWllDh5EiMf66XdVwZGj" %}

```bash
python3 scripts/powerbi/grant_euno_workspace_access.py --help
# or: chmod +x scripts/powerbi/grant_euno_workspace_access.py && ./scripts/powerbi/grant_euno_workspace_access.py --help
```

The `grant-euno-workspace-access.sh` wrapper runs Python with **`-u`** (unbuffered I/O) so the device user code appears immediately when stdout is not a terminal (CI, log capture, etc.).

Pass **exactly one** workspace selection flag (see table below), plus optional `--skip`, `--limit`, `--dry-run`, `--role`, `--poll-interval`, or `--access-token-file`.

Use **exactly one** workspace selection mode:

| Mode                       | Flags                               |
| -------------------------- | ----------------------------------- |
| Explicit UUIDs             | `--workspace-ids 'uuid1,uuid2,...'` |
| Name substring             | `--name-contains 'Finance'`         |
| Name regex (Python)        | `--name-regex '^prod-.+'`           |
| All workspaces (inventory) | `--all`                             |

**`--skip N`:** after filtering, skip the first **N** workspaces, then apply **`--limit`** (stable order follows the Admin API listing).

**`--limit N`:** after filtering and **`--skip`**, process at most **N** workspaces.

**`--dry-run`:** print targets only; **no** `POST`. If you use only `--workspace-ids`, no sign-in is required. For `--name-*` / `--all`, dry-run needs a token: pass **`--access-token-file`** whose first line is a delegated Power BI access token (advanced).

**`--role`:** defaults to `Admin` (matches the main guide’s Step 4). Other values follow [GroupUser](https://learn.microsoft.com/en-us/rest/api/power-bi/admin/groups-add-user-as-admin) documentation.

### What the script does

1. Starts [**OAuth 2.0 device code**](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-device-code) against your tenant for the bootstrap app, with scope `Tenant.ReadWrite.All` on Power BI (plus `offline_access` for refresh token issuance if Microsoft returns one).
2. Prints a **user code** and opens a browser to **`https://login.microsoft.com/device`** (override with env `DEVICE_VERIFY_URL` if needed). Complete sign-in as a **Fabric admin**.
3. Polls the token endpoint every **5 seconds** (override with `--poll-interval` or env `POLL_INTERVAL`) until tokens are issued.
4. Unless you only passed `--workspace-ids`, calls **`GET /admin/groups`** (paginated) and filters to `type == "Workspace"`.
5. For each target workspace, calls **`POST /admin/groups/{workspaceId}/users`** with `principalType: App` and `identifier` = your main app’s **service principal object ID**.

### Example: two workspaces by ID (dry run, no sign-in)

```bash
python3 scripts/powerbi/grant_euno_workspace_access.py \
  --tenant "<TENANT_ID>" \
  --bootstrap-client-id "<BOOTSTRAP_CLIENT_ID>" \
  --principal-object-id "<MAIN_SP_OBJECT_ID>" \
  --workspace-ids "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee,11111111-2222-3333-4444-555555555555" \
  --dry-run
```

### Example: first 10 workspaces whose names contain “test”

```bash
python3 scripts/powerbi/grant_euno_workspace_access.py \
  --tenant "<TENANT_ID>" \
  --bootstrap-client-id "<BOOTSTRAP_CLIENT_ID>" \
  --principal-object-id "<MAIN_SP_OBJECT_ID>" \
  --name-contains "test" \
  --limit 10
```

### Example: all workspaces (use with care)

```bash
python3 scripts/powerbi/grant_euno_workspace_access.py \
  --tenant "<TENANT_ID>" \
  --bootstrap-client-id "<BOOTSTRAP_CLIENT_ID>" \
  --principal-object-id "<MAIN_SP_OBJECT_ID>" \
  --all
```

## Security and operations

* The **bootstrap app** is powerful in combination with a **Fabric admin** user. Restrict who can run the script; rotate bootstrap app settings if credentials leak. Consider removing the app after the bootstrap is successful.
* **Refresh tokens** returned by the device-code flow are sensitive; this script does **not** print them. Run from a secure workstation.
* **Re-running** the script for a workspace that already has the app may return a non-2xx response from Microsoft; treat as idempotent “already added” and verify in the Power BI UI if unsure.

## Related links

* [Power BI integration (main setup)](/sources/business-intelligence/powerbi-integration.md)
* [Add User As Admin (Microsoft Learn)](https://learn.microsoft.com/en-us/rest/api/power-bi/admin/groups-add-user-as-admin)
* [Device code flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-device-code)
* [Enable service principal authentication for admin APIs](https://learn.microsoft.com/en-us/fabric/admin/enable-service-principal-admin-apis)

## Q\&A

### Why is automation awkward? (Two different Microsoft models)

**What you want:** Call the Power BI Admin API [**Groups - Add User As Admin**](https://learn.microsoft.com/en-us/rest/api/power-bi/admin/groups-add-user-as-admin) so a **Fabric / Power BI administrator** can add your Euno app to each workspace in one pass.

**What Microsoft requires:** The same API documentation states:

* The caller must be a [**Fabric administrator**](https://learn.microsoft.com/en-us/rest/api/power-bi/admin/groups-add-user-as-admin#permissions) (directory role on the identity making the call).
* The token must include scope **`Tenant.ReadWrite.All`** for the Power BI resource.

At the same time, Microsoft’s guidance for **service principals** using **read-only** Power BI admin APIs says the app **must not** have certain **Power BI&#x20;*****Application*** permissions configured, or service-principal admin calls can fail in practice. See [Enable service principal authentication for admin APIs](https://learn.microsoft.com/en-us/fabric/admin/enable-service-principal-admin-apis).

**Net effect for many customers:**

* The **Euno integration app** (client credentials, no interactive user) should stay **clean**: no Power BI **Application** permission like `Tenant.ReadWrite.All`, so it can keep using **read-only admin** APIs and tenant settings as documented in the main guide.
* **`AddUserAsAdmin`** is reliably exercised with a **delegated (user) token** from a **Fabric admin** account, obtained through a **small separate “bootstrap” app** that is **not** the integration.

That is why this page describes a **second Entra application** used only for this bootstrap script.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.euno.ai/sources/business-intelligence/powerbi-integration/powerbi-workspace-access-bootstrap.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
