Connecting Azure Fabric to Churney

By - Anders Ruge
-
2026

Here you can read our guide for connecting your Azure Fabric data warehouse with Churney.

Connecting Microsoft Fabric to Churney

Here you can read our guide for connecting your Microsoft Fabric Warehouse with Churney. In this guide, we go over the steps for giving Churney access to your data with hashed views and minimal permissions, using Entra ID identities only. No passwords, tokens or storage keys are exchanged at any point.

How the integration works

Churney does not read rows out of your warehouse directly. On a schedule agreed with you, Churney's sync:

  1. Starts a run of a small data pipeline that Churney maintains in a dedicated export workspace in your Fabric tenant. The pipeline runs a query against the hashed views and writes the result as Parquet files to a storage container you own.
  2. Copies those files from your container to Google Cloud Storage with Google's Storage Transfer Service.
  3. Loads them into BigQuery.

Everything Churney does in your tenant is done as one Entra ID application that you register. The pipeline itself runs as your export workspace's own identity. Files in your container expire automatically after 1 day.

The setup is two hand-overs:

  1. Churney sends you two Google Cloud identity ids. You need them in the "Register the Churney application" step; every other step can be done before they arrive.
  2. You send Churney the values listed under "What to share with Churney" at the end of this guide. Churney then runs the first sync, which also creates the export pipeline in your export workspace.

Prerequisites

Before you start, make sure that:

  • Your data is in a Fabric Warehouse. A Lakehouse alone is not enough, because the SQL analytics endpoint of a Lakehouse is read-only and cannot host the hashed views. If your data lives in a Lakehouse, create a Warehouse in the same workspace; it can query the Lakehouse tables directly.
  • The workspace is on a Fabric capacity (F SKU), not a trial or Power BI Premium capacity. Workspace identities need an F SKU to access storage.
  • The capacity is running when the sync runs. Churney never starts or stops your capacity. If you pause it on a schedule, tell us so we can align the sync window; a sync that hits a paused capacity fails and is retried at the next scheduled run.
  • Your Fabric tenant allows service principals to use Fabric APIs. A Fabric administrator enables this under Admin portal → Tenant settings → Developer settings → "Service principals can use Fabric APIs". Scope it to a security group; you add the Churney application to that group in the "Register the Churney application" step.
  • You can create an Entra ID app registration, a storage account and role assignments in your Azure subscription, or have someone who can.
  • You have the Azure CLI installed and are logged in (az login). The commands below use it; everything can also be done in the Azure portal and the Fabric portal.

Throughout the guide, replace the values in angle brackets. Fabric REST API calls use a token from your own login:

TOKEN=$(az account get-access-token --resource https://api.fabric.microsoft.com --query accessToken -o tsv)

What kind of data is required?

The short answer is as much as possible. The long answer is that Churney requires data about:

  • Payments
  • Trials (if applicable)
  • User demographic (if available)
  • User activity
  • Attribution Data: Source of truth for campaign performance (UTM/MMP/ad network)

Additionally, we need to know the Azure region of your Fabric capacity.

Generating hashed views

If you would like Churney's help with the hashed views, connect to your Warehouse and run the following query, where <your_schema_name> is the schema of the tables you wish to share with Churney:

SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = '<your_schema_name>';

Download the output as CSV and share it with Churney. We will come back to you with a script that creates the hashed views. Otherwise, continue with the example below.

Create hashed views

For context, Facebook has a guide on how to hash contact information for their conversion API: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters. Google has a similar guide for enhanced conversions: https://developers.google.com/google-ads/api/docs/conversions/enhance-conversions. We want to hash columns that would allow one to determine the identity of a user: first name, last name, birthday, street address, phone number, email address, IP address.

Create a schema churney_views in your Warehouse and, for each table you share, a view that hashes the personally identifiable columns with SHA-256. Fabric Warehouse uses T-SQL:

CREATE SCHEMA churney_views;
GO

CREATE VIEW churney_views.users AS
SELECT
    user_id,
    CONVERT(VARCHAR(64), HASHBYTES('SHA2_256', LOWER(TRIM(email))), 2)           AS email,
    CONVERT(VARCHAR(64), HASHBYTES('SHA2_256', LOWER(TRIM(full_name))), 2)       AS full_name,
    CONVERT(VARCHAR(64), HASHBYTES('SHA2_256', REPLACE(REPLACE(REPLACE(phone, ' ', ''), '-', ''), '+', '')), 2) AS phone,
    CONVERT(VARCHAR(64), HASHBYTES('SHA2_256', CONVERT(VARCHAR(8), birthday, 112)), 2) AS birthday,
    country_code,
    signup_date,
    created_at
FROM raw_data.users;

CONVERT(..., 2) renders the hash as lowercase hex without the 0x prefix, which matches the hashes Churney receives from other sources. Only the hashed views are shared with Churney; the raw tables stay private.

Two things to keep in mind for the views:

  • Give every table with events a timestamp column (DATETIME2) that Churney can use for incremental loads, for example event_time or created_at.
  • Fabric Warehouse created through the API uses a case-sensitive collation. Table and column names in the views must be referenced with the exact casing.

Create a storage account and container for Churney

Churney's pipeline writes Parquet files to an Azure Data Lake Storage Gen2 container. Create it in the same region as your Fabric capacity, with hierarchical namespace enabled, and let files expire after 1 day.

az group create --name churney-export --location <region>
az storage account create \
  --name <storageaccount> --resource-group churney-export --location <region> \
  --sku Standard_LRS --kind StorageV2 --enable-hierarchical-namespace true \
  --allow-shared-key-access false
az storage container create --account-name <storageaccount> --name churney-unload --auth-mode login
az storage account management-policy create --account-name <storageaccount> --resource-group churney-export --policy '{
  "rules": [{"enabled": true, "name": "expire-churney-unload", "type": "Lifecycle",
    "definition": {"actions": {"baseBlob": {"delete": {"daysAfterModificationGreaterThan": 1}}},
                   "filters": {"blobTypes": ["blockBlob"], "prefixMatch": ["churney-unload/"]}}}]}'

--allow-shared-key-access false disables storage account keys entirely; every access in this guide uses Entra ID.

Create the Churney export workspace

The pipeline Churney runs lives in its own workspace, so that Churney never needs any role in the workspace that holds your data. Create the workspace on your capacity and give it a workspace identity.

CAPACITY_ID=$(curl -s -H "Authorization: Bearer $TOKEN" https://api.fabric.microsoft.com/v1/capacities \
  | jq -r '.value[] | select(.displayName=="<your capacity name>") | .id')
EXPORT_WS=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://api.fabric.microsoft.com/v1/workspaces \
  -d "{\"displayName\":\"churney-export\",\"capacityId\":\"$CAPACITY_ID\"}" | jq -r .id)
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Length: 0" \
  "https://api.fabric.microsoft.com/v1/workspaces/$EXPORT_WS/provisionIdentity"

The last call is asynchronous. After about ten seconds, check the result in the Fabric portal under the export workspace → Workspace settings → Workspace identity. It shows the identity's service principal ID; keep it as <identity-sp-id>. If the state shows Failed the first time an identity is created in a tenant, delete it, wait five minutes and create it again.

Grant the workspace identity write access to the container:

az role assignment create --assignee-object-id <identity-sp-id> --assignee-principal-type ServicePrincipal \  --role "Storage Blob Data Contributor" \  --scope "/subscriptions/<subscription>/resourceGroups/churney-export/providers/Microsoft.Storage/storageAccounts/<storageaccount>/blobServices/default/containers/churney-unload"

Grant the export workspace read access to the hashed views

The export workspace's identity needs to read churney_views, and nothing else. Access is opt-in: share the Warehouse with the identity without data access, then grant the one schema in SQL.

  1. In the Fabric portal, open the workspace that holds your Warehouse, select the Warehouse → Share. Add churney-export (the workspace identity has the same name as the export workspace). Leave every checkbox unticked, in particular "Read all data using SQL", and select Grant. This lets the identity connect to the Warehouse but see no tables.
  2. Connect to the Warehouse and grant the hashed-view schema:
GRANT SELECT ON SCHEMA::churney_views TO [churney-export];

Do not give the identity a workspace role; Viewer would grant read access to every table in the workspace.

Create the connections for the Churney pipeline

The pipeline Churney runs uses two connections, both authenticated with the workspace identity: one to your Warehouse's SQL endpoint, one to the container. Find the SQL endpoint under your Warehouse → Settings → SQL endpoint, it looks like <guid>.datawarehouse.fabric.microsoft.com.

SQL_CONN=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://api.fabric.microsoft.com/v1/connections -d '{
  "connectivityType":"ShareableCloud","displayName":"churney-warehouse",
  "connectionDetails":{"type":"SQL","creationMethod":"SQL","parameters":[
    {"dataType":"Text","name":"server","value":"<sql-endpoint>"},
    {"dataType":"Text","name":"database","value":"<warehouse-name>"}]},
  "privacyLevel":"Organizational",
  "credentialDetails":{"singleSignOnType":"None","connectionEncryption":"Encrypted","skipTestConnection":false,
    "credentials":{"credentialType":"WorkspaceIdentity"}}}' | jq -r .id)
ADLS_CONN=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://api.fabric.microsoft.com/v1/connections -d '{
  "connectivityType":"ShareableCloud","displayName":"churney-unload",
  "connectionDetails":{"type":"AzureDataLakeStorage","creationMethod":"AzureDataLakeStorage","parameters":[
    {"dataType":"Text","name":"server","value":"https://<storageaccount>.dfs.core.windows.net"},
    {"dataType":"Text","name":"path","value":"churney-unload"}]},
  "privacyLevel":"Organizational",
  "credentialDetails":{"singleSignOnType":"None","connectionEncryption":"Encrypted","skipTestConnection":false,
    "credentials":{"credentialType":"WorkspaceIdentity"}}}' | jq -r .id)

Print the two connection ids; you share them with Churney:

echo "$SQL_CONN $ADLS_CONN"

Churney creates the pipeline itself, named churney_unload, in the export workspace using these two connections, and keeps its definition up to date on every sync. It is a single Copy activity with two parameters, query and sink_path; it has no schedule and no query of its own. You can open it in the Fabric portal to inspect it. Manual edits to it are overwritten by the next sync.

Register the Churney application

Churney authenticates as an Entra ID application in your tenant. Instead of a client secret, the application trusts two Google Cloud identities through federated credentials. Use the two ids Churney sent you: <churney-sync-id> for the sync itself and <churney-transfer-id> for the file transfer.

APP_ID=$(az ad app create --display-name churney-sync --sign-in-audience AzureADMyOrg --query appId -o tsv)
SP_ID=$(az ad sp create --id $APP_ID --query id -o tsv)

az ad app federated-credential create --id $APP_ID --parameters '{
  "name": "churney-sync",
  "issuer": "https://accounts.google.com",
  "subject": "<churney-sync-id>",
  "audiences": ["api://AzureADTokenExchange"],
  "description": "Churney sync: runs the export pipeline and reads table metadata"}'

az ad app federated-credential create --id $APP_ID --parameters '{
  "name": "churney-transfer",
  "issuer": "https://accounts.google.com",
  "subject": "<churney-transfer-id>",
  "audiences": ["api://AzureADTokenExchange"],
  "description": "Google Storage Transfer Service: reads the churney-unload container"}'

Grant the application the permissions it needs:

# Use the two connections in the pipeline definition
for CONN in $SQL_CONN $ADLS_CONN; do
  curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
    "https://api.fabric.microsoft.com/v1/connections/$CONN/roleAssignments" \
    -d "{\"principal\":{\"id\":\"$SP_ID\",\"type\":\"ServicePrincipal\"},\"role\":\"User\"}"
done

# Create, update and run the pipeline in the export workspace
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  "https://api.fabric.microsoft.com/v1/workspaces/$EXPORT_WS/roleAssignments" \
  -d "{\"principal\":{\"id\":\"$SP_ID\",\"type\":\"ServicePrincipal\"},\"role\":\"Contributor\"}"

# Read the exported files
az role assignment create --assignee-object-id $SP_ID --assignee-principal-type ServicePrincipal \
  --role "Storage Blob Data Reader" \
  --scope "/subscriptions/<subscription>/resourceGroups/churney-export/providers/Microsoft.Storage/storageAccounts/<storageaccount>/blobServices/default/containers/churney-unload"

# Use the two connections in the pipeline definition
for CONN in $SQL_CONN $ADLS_CONN; do  curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \    "https://api.fabric.microsoft.com/v1/connections/$CONN/roleAssignments" \    -d "{\"principal\":{\"id\":\"$SP_ID\",\"type\":\"ServicePrincipal\"},\"role\":\"User\"}"done

The application also needs to read column metadata from the hashed views. Give it the same opt-in access as the workspace identity: share the Warehouse with churney-sync without data access (Warehouse → Share, no checkboxes ticked), then run:

GRANT SELECT ON SCHEMA::churney_views TO [churney-sync];

Finally, add the application to the security group that the tenant setting "Service principals can use Fabric APIs" is scoped to.

What to share with Churney

When the steps above are done, send Churney:

  1. Azure region of your Fabric capacity
  2. Tenant ID
  3. Application (client) ID of churney-sync
  4. Export workspace ID
  5. Warehouse connection ID and ADLS connection ID
  6. Warehouse SQL endpoint host and Warehouse name
  7. Storage account name and container name

Churney fills these into your sync configuration and runs the first sync. That run creates the churney_unload pipeline in your export workspace; nothing else needs to happen on your side.

Setup & onboarding overview

Once you have shared the details above, Churney:

  1. Creates a Google Cloud project for your data and, in it, the service account and the Storage Transfer Service identity whose ids you add as federated credentials.
  2. Runs a first sync of every view in churney_views and checks the result with you.
  3. Schedules the recurring sync at the cadence agreed with you.

Churney holds no credentials for your tenant at any point: no client secret, no storage key, no SAS token. Access can be revoked by deleting the churney-sync app registration or the export workspace.

Authentication

Every connection uses Entra ID and short-lived tokens:

  • Churney → Fabric. Churney's sync runs as a Google Cloud service account. For each run, Google issues it a signed identity token, which Entra ID exchanges for an access token for the churney-sync application through the federated credential you created. This is Entra ID's workload identity federation; the token is valid for about an hour and is never stored. The same token is used to start the pipeline and to read column metadata from the hashed views.
  • Pipeline → Warehouse and container. The pipeline's two connections authenticate as the export workspace's identity, which Fabric manages. Churney never sees these credentials.
  • Churney → container. Google's Storage Transfer Service authenticates as the churney-sync application through the second federated credential, with read-only access to the container.

Data sync

The sync is incremental: rather than re-copying entire tables each time, we only pull rows that are new or have changed since the last successful sync, using the timestamp column you designate for each view. We also apply a configurable safety-margin window so that late-arriving or recently-updated records are not missed. Each table is exported by its own pipeline run and processed independently, so if one table fails, the others still complete and the specific failures are reported clearly.

Each run writes to a fresh folder in the container, named after the run, and the lifecycle rule deletes it after 1 day. Pipeline runs consume capacity units on the capacity the export workspace is assigned to.

Optimize your customer acquisition for maximum Lifetime Value

Your data warehouse has incredible value. Our causal AI helps unlock it.