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

# OneDrive Connector

> Automatically forward files from a OneDrive drive to ROOTKey using a Terraform-deployed Azure Function App in your Azure subscription, with self-renewing webhooks, retries, dead-letter queue, and Key Vault-backed secrets.

## How It Works

The OneDrive connector is a self-contained integration that runs **entirely inside your Azure subscription**. ROOTKey publishes a [Terraform module](https://github.com/rootkey-ai/rootkey-connectors/tree/main/onedrive) that you deploy once. The module wires up an Azure Function App that subscribes to Microsoft Graph webhook notifications for one OneDrive drive. When the drive changes, the function runs a Graph delta query to identify the new/updated files and streams each file to the ROOTKey API authenticated with your **Connector API Key**.

ROOTKey stores the file and anchors it on-chain, enabling both integrity verification and **full file recovery** in the event of corruption, ransomware, or accidental deletion.

```
Your OneDrive Drive       Microsoft Graph         Azure Function App           ROOTKey API
───────────────────       ───────────────         ──────────────────           ───────────
  File created /     →    Webhook notification →  Validates clientState    →   POST /api-v1/connectors/files/
  updated                                          Acquires sync lease         (Connector API Key
                                                   Runs delta query             from Key Vault,
                                                   Streams each file            + idempotency
                                                                                headers per item)
```

<Note>
  ROOTKey's cyber resilience guarantee includes full recovery — not just detection. For that reason the connector uploads the **full file content**, not only a hash. Anchoring a hash alone cannot restore a corrupted, encrypted, or deleted file.
</Note>

<Note>
  The connector is bound to **one drive** per deployment — the Graph drive ID is part of the module's input. To monitor multiple drives, deploy the module once per drive; the resources are namespaced by a configurable `name_suffix` so they coexist in the same resource group.
</Note>

***

## What This Module Creates in Your Azure Subscription

Full transparency on what lands in your subscription when you `terraform apply`. Everything is namespaced by `name_suffix` + a deterministic hash of the drive ID, so multiple deployments don't collide.

| Resource                                                           | Purpose                                                                                                                                                 | Cost impact                                                                      |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `azurerm_linux_function_app`                                       | The connector itself (Node.js 22, Consumption plan, HTTPS-only, TLS 1.2 min, CORS closed).                                                              | Pay-per-execution; free tier covers 1M executions + 400 K GB-s/month, perpetual. |
| `azurerm_service_plan` (`Y1` Consumption)                          | Hosting plan for the Function App.                                                                                                                      | No fixed cost; you pay only for executions above the free tier.                  |
| `azurerm_storage_account`                                          | Function backing storage, delta-cursor blob, sync lock blob, and DLQ.                                                                                   | \~\$0.05–1/month for typical loads.                                              |
| `azurerm_storage_container` (`connector-state`)                    | Holds `delta-link.txt`, `subscription.json`, and `delta-sync.lock`.                                                                                     | Included in storage cost.                                                        |
| `azurerm_storage_queue` (`rootkey-dlq`)                            | Dead-letter queue for per-file failures. Auto-replayed by a queue-triggered function.                                                                   | Free in normal operation.                                                        |
| `azurerm_key_vault` (Standard, RBAC) + 3 secrets                   | Stores the Graph client secret, ROOTKey API key, and the random webhook clientState. **Secrets are never stored as plain Function App settings.**       | \~\$0.01/month. Purge protection is **enabled by default**.                      |
| `azurerm_user_assigned_identity`                                   | The Function App's identity. Granted least-privilege RBAC: `Key Vault Secrets User`, `Storage Blob Data Contributor`, `Storage Queue Data Contributor`. | None.                                                                            |
| `azurerm_log_analytics_workspace` + `azurerm_application_insights` | Telemetry and logs with configurable retention (default 30 days).                                                                                       | \$0 within the App Insights free tier (5 GB/month).                              |
| Role assignments                                                   | The RBAC entries linking the managed identity to the Key Vault, blob, and queue scopes.                                                                 | None.                                                                            |

The module **does not** create or modify the Microsoft Entra ID App Registration — you create that yourself and pass the Tenant ID, Client ID, and Client Secret in (see Prerequisites). It also does not create the Resource Group, which must pre-exist.

For a typical drive with **a few thousand uploads per month**, the total recurring cost added to your Azure bill is **well under \$5/month**, dominated by egress to the ROOTKey API.

***

## Infrastructure Impact Summary

<AccordionGroup>
  <Accordion title="Does this modify my OneDrive or my Microsoft 365 tenant?" icon="cloud">
    Apart from creating one Microsoft Graph webhook subscription on the configured drive, no. The connector reads files via the App Registration's `Files.Read.All` permission and never writes back to OneDrive. No mailbox, SharePoint site, or Teams resource is touched.
  </Accordion>

  <Accordion title="Does this modify my Resource Group or existing Azure resources?" icon="user-shield">
    The module creates new resources inside the Resource Group you specify and **does not modify any pre-existing resources** in it. Role assignments are scoped to the resources the module itself creates — it does not grant any permissions on resources outside its scope.
  </Accordion>

  <Accordion title="Does the file content leave my Azure subscription?" icon="cloud-arrow-up">
    Yes. The whole purpose of the connector is to forward file content to ROOTKey so it can be anchored and recovered. Transport is HTTPS-only (the module rejects non-`https://` API URLs at plan time). Files are streamed directly from Graph to the ROOTKey API; the Function App never writes them to local storage or to any other Azure service.
  </Accordion>

  <Accordion title="What happens to my secrets?" icon="key">
    The Graph client secret you paste into Terraform, the ROOTKey API Key, and a randomly generated webhook `clientState` are all stored in Azure Key Vault **in your own subscription**, encrypted at rest with the Microsoft-managed key for Key Vault. The Function App resolves them at boot using its managed identity and Key Vault references (`@Microsoft.KeyVault(SecretUri=…)`). They are **not** stored as plain Function App settings.
  </Accordion>

  <Accordion title="What happens if the ROOTKey API is unreachable?" icon="circle-exclamation">
    Each file gets up to **3 upload attempts** with exponential backoff (initial 1s, capped at 30s) before being sent to the SQS-equivalent Azure Storage DLQ. From the DLQ, a queue-triggered function automatically replays each message up to **5 more times** (queue retries with backoff). Only after all those retries fail does the message land in the `rootkey-dlq-poison` queue for human inspection. You can configure an Azure Monitor alarm on the DLQ or poison queue length to be notified.
  </Accordion>

  <Accordion title="What happens if a Microsoft Graph webhook is missed?" icon="bell">
    A timer trigger runs every 12h (and on every cold start, via `runOnStartup`) that does two things: renews/recreates the Graph subscription, and runs a safety-net delta sync. So even if a notification is dropped, the missed changes are picked up — at worst, within the next 12h, or immediately on the next deploy/restart.
  </Accordion>

  <Accordion title="What happens to other webhook callers?" icon="shield-halved">
    The webhook is protected by a 32-char random `clientState` value generated at apply time and stored in Key Vault. Any POST to the webhook URL without the matching `clientState` is rejected with `401`. This prevents random or malicious callers from triggering work.
  </Accordion>

  <Accordion title="Can I roll this back?" icon="rotate-left">
    Yes. Running `terraform destroy` removes every resource the module created (Function App, storage account, Key Vault, App Insights, identity, role assignments). The App Registration and the Resource Group are **not** deleted — they are your resources, not the module's. **Note:** if you enabled `purge_protection` on the Key Vault (the default), the Vault and its secrets will remain in soft-delete state for 7 days after destroy before they can be fully purged.
  </Accordion>
</AccordionGroup>

***

## Prerequisites

Before starting, ensure you have:

* An **Azure subscription** and a **pre-existing Resource Group** to host the connector.
* Permissions to register applications in **Microsoft Entra ID** (Application Administrator or Global Administrator) and to grant admin consent.
* Permissions to apply Terraform with `Contributor` and `User Access Administrator` (or equivalent) on the chosen Resource Group.
* **EventBridge-equivalent**: nothing extra on the OneDrive side — the connector self-registers the Graph subscription. You only need to confirm the drive ID.
* [Terraform](https://developer.hashicorp.com/terraform/install) v1.3 or later installed locally (or in a CI/CD pipeline that runs `terraform apply`).
* [Node.js](https://nodejs.org) v22 or later on the machine running Terraform — the Function App source is compiled at apply time.
* Azure CLI authenticated (`az login`) or service principal credentials in the environment.

***

## Required Microsoft Graph and Azure Permissions

### Microsoft Graph (Application permission)

The App Registration you create needs **one** Microsoft Graph application permission with admin consent:

| Permission       | Type        | Why                                                        |
| ---------------- | ----------- | ---------------------------------------------------------- |
| `Files.Read.All` | Application | Read drive items and file content for the monitored drive. |

### Azure RBAC (granted by the module to its own managed identity)

For full transparency — the module attaches these role assignments to a brand-new user-assigned managed identity it creates. None of these grant access to anything outside the resources the module itself provisions:

| Role                             | Scope                                      |
| -------------------------------- | ------------------------------------------ |
| `Key Vault Secrets User`         | The Key Vault created by the module.       |
| `Storage Blob Data Contributor`  | The Storage Account created by the module. |
| `Storage Queue Data Contributor` | The Storage Account created by the module. |

The Terraform principal applying the module needs `Contributor` (to create the resources) and `User Access Administrator` (to attach those role assignments) on the Resource Group.

***

## Configuration Fields

| Field                          | Required | Default               | Description                                                                                                                                                                                  |
| ------------------------------ | -------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Connector Name**             | Yes      | —                     | A human-readable name to identify this connector in the dashboard.                                                                                                                           |
| **Destination Vault**          | Yes      | —                     | The ROOTKey vault where anchored files will be stored.                                                                                                                                       |
| **Tenant ID**                  | Yes      | —                     | Microsoft Entra ID tenant ID (a UUID).                                                                                                                                                       |
| **Client ID**                  | Yes      | —                     | Application (client) ID of the App Registration.                                                                                                                                             |
| **Client Secret**              | Yes      | —                     | A client secret generated for the App Registration. Stored in Key Vault by the module.                                                                                                       |
| **Drive ID**                   | Yes      | —                     | Microsoft Graph ID of the OneDrive drive to monitor.                                                                                                                                         |
| **Azure Region**               | Yes      | —                     | Azure region where the connector resources will be deployed (e.g., `westeurope`).                                                                                                            |
| **Resource Group Name**        | Yes      | —                     | Pre-existing Azure Resource Group.                                                                                                                                                           |
| **Name Suffix**                | Yes      | —                     | 3–12 lowercase alphanumeric chars used to namespace the resources (e.g., `acme` or `prod`).                                                                                                  |
| **Max file size (bytes)**      | No       | `524288000` (500 MiB) | Files larger than this are skipped and sent to the DLQ.                                                                                                                                      |
| **Log retention (days)**       | No       | `30`                  | Application Insights / Log Analytics retention (30–730).                                                                                                                                     |
| **Key Vault purge protection** | No       | `true`                | Keep purge protection enabled for production. Set to `false` only during short pilots; once enabled it cannot be disabled and the Key Vault cannot be fully purged for 7 days after destroy. |
| **Tags**                       | No       | `{}`                  | Extra tags applied to every module-managed resource.                                                                                                                                         |

***

## Setup

The setup has a natural ordering: the dashboard requires the Tenant/Client/Drive IDs to create the connector, and the Function App requires the **Connector API Key** to call the ROOTKey API. The dashboard resolves this by generating a ready-to-run Terraform block with all values pre-filled.

<Steps>
  <Step title="Register an application in Microsoft Entra ID">
    Go to the [Azure Portal](https://portal.azure.com) → **Microsoft Entra ID** → **App registrations** → **New registration**.

    * **Name**: something descriptive, e.g., `ROOTKey OneDrive Connector`.
    * **Supported account types**: *Accounts in this organizational directory only*.
    * **Redirect URI**: leave blank.

    Click **Register**. Note the **Application (client) ID** and **Directory (tenant) ID** — you will need both.
  </Step>

  <Step title="Grant Microsoft Graph permission">
    In your new App Registration, go to **API permissions** → **Add a permission** → **Microsoft Graph** → **Application permissions**.

    Add `Files.Read.All`, then click **Grant admin consent for \[your tenant]** and confirm.
  </Step>

  <Step title="Create a client secret">
    Go to **Certificates & secrets** → **New client secret**.

    * Set an **expiry** appropriate for your rotation policy (e.g., 12 or 24 months).
    * Click **Add** and immediately copy the **Value** — it is shown only once.

    Store the secret securely until you paste it into the dashboard.

    <Warning>
      Azure App Registration secrets expire. Set a calendar reminder ahead of the expiry — when the secret expires, the connector starts failing with `401` from Graph. Rotation steps are in the [Troubleshooting](#troubleshooting) section.
    </Warning>
  </Step>

  <Step title="Find the Drive ID">
    Retrieve the drive ID via Microsoft Graph Explorer or directly with the Graph API. For a specific user:

    ```http theme={null}
    GET https://graph.microsoft.com/v1.0/users/{user-id-or-upn}/drives
    ```

    Copy the `id` of the drive you want to monitor.
  </Step>

  <Step title="Pre-create the Resource Group">
    In your subscription, create (or pick) a Resource Group to host the connector. The Terraform principal needs `Contributor` and `User Access Administrator` on that Resource Group.
  </Step>

  <Step title="Create the connector in the dashboard">
    Go to [app.rootkey.ai](https://app.rootkey.ai) → **Connectors** → **New Connector** → select **OneDrive**.

    Fill in all required fields (see [Configuration Fields](#configuration-fields) above). Save the connector.
  </Step>

  <Step title="Copy the Connector API Key and the Terraform block">
    At the end of the wizard, the dashboard displays:

    1. The **Connector API Key**.
    2. A **ready-to-run Terraform block**, pre-filled with your values.

    <Warning>
      The Connector API Key is shown **only once** and is already embedded in the Terraform block. Copy both now and store them securely before closing this screen. The key cannot be retrieved again.
    </Warning>

    The generated block looks like:

    ```hcl theme={null}
    module "rootkey_onedrive_connector" {
      source = "github.com/rootkey-ai/rootkey-connectors//onedrive"

      resource_group_name = "rootkey-connectors"
      azure_location      = "westeurope"
      name_suffix         = "acme"

      graph_tenant_id     = "11111111-1111-1111-1111-111111111111"
      graph_client_id     = "22222222-2222-2222-2222-222222222222"
      graph_client_secret = "Xyz~RandomSecretFromAppRegistration"
      drive_id            = "b!abcd...verylong-graph-drive-id"

      rootkey_api_key = "rk_conn_xxxxxxxxxxxxxxxxxxxx"

      # Optional
      max_file_size_bytes               = 524288000
      log_retention_days                = 30
      enable_key_vault_purge_protection = true
      tags = {
        "cost-center" = "security"
      }
    }
    ```
  </Step>

  <Step title="Deploy the Terraform module">
    Save the block into a `.tf` file in an empty directory, then run:

    ```bash theme={null}
    terraform init
    terraform apply
    ```

    The module bundles the Function App source, provisions every resource, and wires the managed identity, Key Vault, and storage roles.
  </Step>

  <Step title="Validate the connector">
    Because the timer is registered with `runOnStartup: true`, the Function App registers the Graph subscription within seconds of deploy. You can confirm by inspecting the `connector-state` blob container:

    ```bash theme={null}
    az storage blob list \
      --container-name connector-state \
      --account-name $(terraform output -raw storage_account_name) \
      --query "[].name"
    ```

    You should see `subscription.json` (and, after the first sync, `delta-link.txt`).

    Then upload a test file to the monitored drive. Within seconds it should appear in the destination vault and the connector status in the dashboard should be **ACTIVE**.
  </Step>
</Steps>

***

## Reliability and observability

The connector is built for at-least-once delivery to ROOTKey with explicit handling of every failure mode.

### Retry behaviour

| Layer                 | Retries                                                                                                                                    | When                                                                     |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| In-function retry     | 3 attempts per file (initial + 2 retries) with exponential backoff (1s → 2s, capped 30s) and jitter                                        | On `429`, `5xx`, network or timeout errors from the ROOTKey API.         |
| DLQ replay            | The queue-triggered `dlqReplay` function automatically reprocesses every DLQ message, with another 3-attempt in-function budget per replay | Until either success or the queue's `maxDequeueCount` (5) is exhausted.  |
| Poison queue          | If all DLQ replays fail, the message lands in `rootkey-dlq-poison` for human inspection                                                    | Persistent or systemic failure.                                          |
| Graph-side retry      | Microsoft Graph retries the webhook for up to \~4 hours with exponential backoff                                                           | The Function App returns `5xx` (only when the delta query itself fails). |
| Safety-net delta sync | The renewal timer also runs a delta query — catches up on any missed webhooks                                                              | Every 12h and on every cold start.                                       |

### Idempotency

Every upload to the ROOTKey API carries three headers extracted from the Graph drive item:

| Header                      | Source                                    |
| --------------------------- | ----------------------------------------- |
| `x-rootkey-source-drive-id` | The configured drive.                     |
| `x-rootkey-source-item-id`  | Graph `driveItem.id`.                     |
| `x-rootkey-source-etag`     | Graph `driveItem.eTag` (quotes stripped). |

The ROOTKey API uses these to deduplicate redelivered events.

### Concurrent invocations

Only one delta sync per drive runs at a time. The connector acquires a blob lease on `delta-sync.lock` in the `connector-state` container before running. Concurrent webhook invocations on the same drive return `202 Accepted` immediately and let the holder finish. The lease auto-expires after 60 s if the holder crashes, so the system self-recovers.

### What to monitor

| Signal                                                | What it means                                                                                | How to alert                                             |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| `rootkey-dlq` queue length > 0 for more than \~10 min | DLQ replay is failing repeatedly.                                                            | Azure Monitor metric alert on the queue length.          |
| `rootkey-dlq-poison` receives a message               | A file failed all replay attempts — human attention needed.                                  | Azure Monitor metric alert on `ApproximateMessageCount`. |
| Function App `Http5xx` > 0                            | The webhook is failing (Graph will retry, but if persistent the subscription may be paused). | Azure Monitor metric alert.                              |
| App Insights traces with `severityLevel >= 3`         | Recurring runtime errors.                                                                    | App Insights alert.                                      |
| `renewSubscription` hasn't run in > 13h               | Timer is unhealthy or the Function App is stopped.                                           | App Insights availability or platform health metric.     |
| ROOTKey dashboard connector status `ERROR`            | API rejected an upload (invalid key, vault deleted, quota).                                  | Email/Slack via your dashboard notification settings.    |

A starting Kusto query to see recent errors in App Insights:

```kusto theme={null}
traces
| where cloud_RoleName == "<function_app_name>"
| where severityLevel >= 3
| order by timestamp desc
| take 100
```

To peek at DLQ contents:

```bash theme={null}
az storage message peek \
  --queue-name rootkey-dlq \
  --account-name $(terraform output -raw storage_account_name) \
  --num-messages 10
```

***

## Security considerations

The module ships with a defensive default posture; a few choices have intentional trade-offs that are worth understanding upfront:

* **Secrets in Key Vault, not Function App settings.** Graph client secret, ROOTKey API key, and webhook `clientState` are all stored in Key Vault with the Function App's managed identity granted `Key Vault Secrets User` (read-only) RBAC.
* **Key Vault purge protection is enabled by default.** Set `enable_key_vault_purge_protection = false` only for short pilots — once enabled it CANNOT be disabled and the vault cannot be fully purged for 7 days after `terraform destroy`.
* **CORS is closed.** The webhook is server-to-server (Graph); browser access is explicitly disallowed.
* **HTTPS-only, TLS 1.2 minimum, FTPS disabled** on the Function App. HTTP/2 enabled.
* **`shared_access_key_enabled = true` on the Storage Account** is a known constraint of the Azure Functions Consumption plan: the runtime requires the legacy `AzureWebJobsStorage` connection string to bootstrap. **The connector's own state operations use RBAC via the managed identity, not the keys.** To remove the keys entirely, you need to move to a Premium / Flex Consumption / App Service plan that supports identity-based connections; that path is on the ROOTKey roadmap.

***

## Filtering Rules

To anchor only specific files (e.g., only PDFs, or exclude temporary files), configure [Filtering Rules](/pages/connectors/rules) on the connector after creation. Rules apply on the ROOTKey side — files filtered out are not stored in the vault.

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Files are uploaded to OneDrive but nothing reaches ROOTKey" icon="magnifying-glass">
    Check in this order:

    1. **The webhook subscription exists.** Inspect the `subscription.json` blob in the `connector-state` container. If absent, force the timer to run via the Azure Portal (Function App → `renewSubscription` → **Code + Test** → **Run**).
    2. **The DLQ.** Use `az storage message peek` on `rootkey-dlq`. If messages are present, look at `rootkey-dlq-poison` too — that's where messages land after all replays fail.
    3. **App Insights traces.** Run the Kusto query in [Reliability and observability](#reliability-and-observability) to find recent errors.
    4. **Graph permissions.** In Microsoft Entra ID → App registrations → API permissions, `Files.Read.All` must be granted with admin consent (green check next to it).
  </Accordion>

  <Accordion title="Connector status is ERROR in the dashboard" icon="triangle-exclamation">
    Common causes:

    * **Client Secret has expired.** Generate a new secret in the App Registration, update the `graph_client_secret` Terraform variable, and run `terraform apply`. The new secret is written to Key Vault and the Function App picks it up on the next cold start (or restart the Function App to force it).
    * **Admin consent was revoked for `Files.Read.All`.** Re-grant consent in the App Registration.
    * **The destination ROOTKey vault was deactivated or deleted.** Reactivate it or change the connector's vault binding.

    The dashboard error panel shows the underlying message from the ROOTKey API or the Graph service.
  </Accordion>

  <Accordion title="A large file fails to upload" icon="file-zipper">
    The Function App's memory and `max_file_size_bytes` together cap the maximum file size. The default is **500 MiB** on a Consumption plan instance.

    To support larger files: raise `max_file_size_bytes` in the Terraform module input and consider moving to a Premium or App Service plan with a higher memory ceiling. Open an issue on the [connector repository](https://github.com/rootkey-ai/rootkey-connectors) if you need help.
  </Accordion>

  <Accordion title="How do I rotate the Connector API Key?" icon="arrows-spin">
    1. In the dashboard, delete the connector and create a new one (the App Registration and Drive ID can be reused).
    2. Update the `rootkey_api_key` Terraform variable with the new key.
    3. Run `terraform apply` — the module writes the new key into Key Vault. The Function App picks it up on the next cold start.
  </Accordion>

  <Accordion title="How do I rotate the Graph client secret?" icon="key">
    1. In Microsoft Entra ID → App registrations → your app → **Certificates & secrets**, create a new client secret. Copy its value immediately.
    2. Update the `graph_client_secret` Terraform variable.
    3. Run `terraform apply` — the new value goes into Key Vault.
    4. Restart the Function App (e.g., `az functionapp restart`) to force it to pick up the new secret immediately, instead of waiting for the cached OAuth token to expire (up to 1 h).
    5. After confirming the connector is healthy, delete the old secret in the App Registration.
  </Accordion>

  <Accordion title="Can I monitor multiple drives?" icon="layer-group">
    Yes — deploy the module once per drive. Each instance is fully isolated: its own Function App, Key Vault, storage account, DLQ, and identity, namespaced by `name_suffix` and a hash of the drive ID. You can reuse the same App Registration and Resource Group across drives.
  </Accordion>

  <Accordion title="What is the rootkey-dlq-poison queue and what do I do if I see messages there?" icon="skull">
    The poison queue holds messages that the queue trigger could not process after all retries. Inspect each message — it includes the original DLQ payload (driveId, itemId, fileName, size, eTag, error). Common root causes:

    * The drive item was deleted or moved by the user before retries could complete (safe — the message can be discarded).
    * The ROOTKey vault is unreachable due to a misconfiguration or a key/vault rotation gone wrong.
    * A persistent Graph permission issue.

    Once the root cause is resolved you can replay a message by copying it back to `rootkey-dlq` (Azure Storage Explorer makes this easy).
  </Accordion>

  <Accordion title="Why does my Terraform plan want to recreate the Key Vault on destroy?" icon="trash">
    With `enable_key_vault_purge_protection = true` (the default), Azure prevents the Key Vault from being fully deleted until the soft-delete retention window (7 days) elapses. If you destroy and re-apply within that window, Terraform may attempt to recover the soft-deleted Key Vault automatically (the module's provider config enables `recover_soft_deleted_key_vaults`). For short-lived pilots that need to recycle freely, set `enable_key_vault_purge_protection = false`.
  </Accordion>
</AccordionGroup>

***

## Source code

The Terraform module and Function App source live in the public [rootkey-ai/rootkey-connectors](https://github.com/rootkey-ai/rootkey-connectors) repository under the [`onedrive/`](https://github.com/rootkey-ai/rootkey-connectors/tree/main/onedrive) directory. The code is licensed under the Apache License 2.0 — you are free to fork it, audit it, or pin to a specific commit if your change-management process requires it.

***

→ Back to [Connectors Overview](/pages/connectors/overview)
