# Microsoft Excel

Omnata's Microsoft Excel plugin reads worksheets stored in SharePoint Online into Snowflake (inbound), and writes Snowflake table or view contents back into Excel worksheets in SharePoint (outbound). It uses the Microsoft Graph API with an Azure AD (Entra ID) App Registration.

### Supported Targets

With the Microsoft Excel plugin, you copy the contents of a Snowflake table or view into an Excel sheet hosted on a SharePoint Online site, or read worksheet data from SharePoint into Snowflake.

{% hint style="info" %}
Your Excel sheet **must** reside in a SharePoint (Office 365) site. The Microsoft Graph API has limitations when modifying a sheet directly in OneDrive.
{% endhint %}

### Authentication methods

#### Enterprise Application

This connection method uses your own Azure client credentials to authorize access. It is the most secure option since the OAuth credentials are owned and managed by you.

The following fields are required to create a connection in the Omnata UI:

<table><thead><tr><th>Field</th><th>Required</th><th>Description</th><th data-hidden>Default</th></tr></thead><tbody><tr><td>Client ID</td><td>Yes</td><td>Application (client) ID from your Azure AD App Registration.</td><td>—</td></tr><tr><td>Tenant ID</td><td>Yes</td><td>Directory (tenant) ID from Azure AD (Entra ID).</td><td>—</td></tr><tr><td>Client Secret</td><td>Yes</td><td>Client secret value from your App Registration. Stored as a Snowflake secret.</td><td>—</td></tr></tbody></table>

**Steps to create an application**

To create a new App Registration in Microsoft 365:

1. Go to the [Azure Portal](https://portal.azure.com/).
2. Search for **App registrations** in the top search bar (or find **Azure Active Directory** / **Microsoft Entra ID** in the left sidebar, then click **App registrations**).
3. Click **+ New registration**.
4. Fill in the registration form:
   * **Name** — give your app a descriptive name (e.g. `Omnata Excel Plugin`).
   * **Supported account types** — usually choose "Accounts in this organizational directory only" (single tenant).
   * **Redirect URI** (optional) — can be left blank, or generated from your Snowflake account by running:

     ```sql
     select 'https://'||get(parse_json(SYSTEM$allowlist()),0):"host"::varchar||'/oauth/complete-secret' as REDIRECT_URL;
     ```
5. Click **Register**.
6. From the app's overview page, copy the **Application (client) ID** and **Directory (tenant) ID**.
7. Go to **Certificates & secrets** → **+ New client secret**. Copy the **Value** immediately (it won't be shown again).

### Permission scopes

The plugin needs Microsoft Graph application permissions to read and (optionally) write SharePoint files. There are two ways to configure these — pick whichever matches your security posture.

#### Option 1 — Tenant-wide access (simplest)

Grants the app access to every SharePoint site in the tenant. Easiest to configure, but broader than many security teams want.

Add these Microsoft Graph **Application permissions** to the App Registration and grant admin consent:

| Sync direction                | Permissions                                  |
| ----------------------------- | -------------------------------------------- |
| Inbound only                  | `Sites.Read.All`, `Files.Read.All`           |
| Outbound (or both directions) | `Sites.ReadWrite.All`, `Files.ReadWrite.All` |

Steps:

1. In the Azure Portal, open your App Registration → **API permissions**.
2. Click **+ Add a permission** and choose **Microsoft Graph** (not "SharePoint" — these permissions are accessed via the Graph API).
3. Choose **Application permissions** (not Delegated).
4. Add the permissions from the table above for your sync direction.
5. Click **Add permissions**, then **Grant admin consent for \[your tenant]** — a Global Admin or Privileged Role Admin must do this. The Status column should show a green check.

{% hint style="info" %}
Microsoft Graph application permissions apply across your entire tenant by design. Without them, the connector cannot traverse the full SharePoint site and folder hierarchy required for syncs. Omnata does not access any data without explicit configuration in Snowflake.
{% endhint %}

#### Option 2 — Per-site least privilege (`Sites.Selected`)

Grants the app access only to specific SharePoint site collections that an admin explicitly authorises. The app has no access to any site until a grant is created. Enforcement is at the Microsoft 365 platform level. Recommended when your security team requires least-privilege scoping.

**1. Add the `Sites.Selected` permission**

In Azure Portal → App Registrations → your app → **API permissions**:

1. Click **+ Add a permission** → **Microsoft Graph** → **Application permissions**.
2. Add `Sites.Selected`.
3. Click **Grant admin consent**.

After admin consent, the app still has access to **no sites** — you must explicitly grant access to each site you want the plugin to reach.

**2. Grant the app access to each site**

For each SharePoint site the plugin needs to access, an admin issues a `POST` to the site's permissions endpoint via Microsoft Graph. Replace `{site-id}` with the SharePoint site ID and `{app-client-id}` with your App Registration's Client ID:

```http
POST https://graph.microsoft.com/v1.0/sites/{site-id}/permissions
Content-Type: application/json

{
  "roles": ["read"],
  "grantedToIdentities": [{
    "application": {
      "id": "{app-client-id}",
      "displayName": "Omnata Excel Plugin"
    }
  }]
}
```

Use `"roles": ["write"]` if the app also needs to write data (outbound syncs). For a connection that does both inbound and outbound, use `"roles": ["read", "write"]`.

{% hint style="info" %}
You can find a SharePoint site's `site-id` by calling `GET https://graph.microsoft.com/v1.0/sites/{tenant}.sharepoint.com:/sites/{site-name}` from Graph Explorer or PowerShell.
{% endhint %}

**Connection test behaviour under `Sites.Selected`**

The Omnata connection test for Microsoft Excel only validates that the Client ID, Tenant ID and Client Secret can exchange for an Azure AD access token — it does not enumerate sites. This is intentional: under `Sites.Selected`, the tenant-wide `GET /sites` health-check would 403 even on a correctly-configured connection.

In practice this means:

* A connection can be created and tested **before** any sites have been granted to the app.
* You will only see authorisation errors when you go to configure a sync that points at a specific site.

**Limitations of `Sites.Selected`**&#x20;

Search based plugin features rely on tenant-wide Microsoft Graph endpoints and are explicitly not covered by `Sites.Selected.`

If your only requirement is search, you'll need to grant `Sites.Read.All` (and `Files.Read.All` for outbound). If you can live without tenant-wide search, `Sites.Selected` covers the rest of the plugin's functionality.

**Troubleshooting `Sites.Selected`**

If a sync configuration tries to read or write a site that has not been granted to the app, Omnata will surface the underlying Graph API failure when fetching streams. A typical example:

```
Error fetching streams: INBOUND_LIST_STREAMS: Failed to resolve sharing link:
403 Client Error: Forbidden for url:
https://graph.microsoft.com/v1.0/shares/u!aHR0cHM6Ly9...
```

This means the SharePoint sharing link in your inbound configuration points to a site that is not in the `Sites.Selected` allow-list for this App Registration. Resolve it by running the `POST .../permissions` request above for that site, then click **Refresh** in the configuration UI.

If instead you see an error referencing the Graph search API, you've hit the search-endpoint restriction described above — provide a direct sharing URL rather than searching.

#### Reference

* [Microsoft Graph permissions reference](https://learn.microsoft.com/en-us/graph/permissions-reference)
* [Resolve Microsoft Graph authorization errors](https://learn.microsoft.com/en-us/graph/resolve-auth-errors)
* [Grant tenant-wide admin consent to an application](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/grant-admin-consent)
* [`Sites.Selected` overview](https://learn.microsoft.com/en-us/graph/permissions-overview#sites-permissions)

### Network addresses

The following hosts must be reachable from Snowflake for the plugin to function:

* `graph.microsoft.com`
* `login.microsoftonline.com`
* `*.sharepoint.com`

### Inbound Syncs

#### Sync modes

Inbound syncs support three modes, chosen via the **Sync Mode** radio button on the inbound sync configuration form:

<table><thead><tr><th width="131.578125">Mode</th><th>What you provide</th><th>What you get</th></tr></thead><tbody><tr><td><strong>Single file</strong></td><td>A SharePoint sharing link to a single Excel file (or to a folder, in which case you pick the file from a dropdown).</td><td>One sync targets one file. Each worksheet in the workbook becomes a stream. You can optionally enable auto-detection of new sheets so they are added as streams on subsequent runs.</td></tr><tr><td><strong>Multi-file</strong></td><td>A SharePoint sharing link to a parent folder, plus a regex pattern for filenames (e.g. <code>^daily_.*\.xlsx$</code>).</td><td>Each file matching the pattern becomes its own stream, on the assumption of one sheet per file. New files matching the pattern are picked up on subsequent runs. Best when each file represents a distinct dataset (e.g. one file per region or per customer).</td></tr><tr><td><strong>Multi-file consolidate</strong></td><td>A SharePoint sharing link to a parent folder, plus a regex pattern for filenames.</td><td>All files matching the pattern are appended into a <strong>single</strong> stream. Best when files share a common schema and you want them stitched together (e.g. one file per month, all conforming to the same columns).</td></tr></tbody></table>

The configuration form previews the matched files when you're in either multi-file mode, so you can sanity-check the regex before saving.

#### Data structure requirements

Worksheets must be structured like a database table:

* The top row contains column headers.
* Data rows follow immediately below the headers.
* No merged cells in the data range.
* No blank rows or columns within the data.

#### Supported Sync Strategies

* **Full Refresh** — re-reads the entire worksheet on every run.

### Handling Deletes

Excel doesn't expose change events for cell-level edits, row deletions, or sheet/file deletions. The plugin reads the **current** state of each worksheet on every run.

### Outbound Syncs

#### Supported Targets

You can copy the contents of a Snowflake table or view into a specific worksheet within an Excel workbook hosted in SharePoint. The first row is written as column headers, with data rows following.

#### Supported Sync Strategies

* **Replace** — clears the worksheet's existing contents and writes the Snowflake data in full.


---

# 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.omnata.com/omnata-product-documentation/omnata-sync-for-snowflake/apps/microsoft-excel.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.
