Azure Blob Storage
Datafly Signal writes batches of first-party events as blobs into an Azure Blob Storage container — ready for query with Synapse, Databricks, Fabric, or your preferred analytics engine.
Why route Azure Blob Storage through Datafly Signal
- The data lands in infrastructure you own. Signal writes to your Azure Blob Storage directly from your own deployment. Datafly operates no intermediary and never holds a copy of your event data.
- Client IP and user agent are recorded as event context. Both come from the original visitor request rather than from your server, so geography and device analysis done in the warehouse reflects the actual visitor.
- Presets for Retail and General. The mapping starts from a working configuration for your vertical instead of a blank field map.
- One consent decision governs every destination. Azure Blob Storage is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.
Prerequisites
Before configuring Azure Blob Storage in Signal, you need an Azure account with a Storage Account, a blob container, and a connection string.
Create an Azure Account
If you don’t already have one, sign up at azure.microsoft.com. Set up a subscription with billing configured.
Create a Storage Account
- In the Azure portal, search for Storage accounts and click Create.
- Select your Subscription and Resource group (or create a new one).
- Enter a Storage account name (e.g.
dataflysignalevents). Names must be globally unique, 3-24 characters, lowercase letters and numbers only. - Select a Region close to your Signal deployment.
- Choose Performance: Standard (recommended) or Premium.
- Choose Redundancy: LRS (cheapest), GRS, or ZRS based on your durability requirements.
- Click Review + Create > Create.
Create a Blob Container
- Open your new Storage Account in the Azure portal.
- In the left sidebar, click Containers (under Data storage).
- Click + Container.
- Enter a Name (e.g.
datafly-events). - Set Public access level to Private (recommended).
- Click Create.
Get the Connection String
- In your Storage Account, go to Security + networking > Access keys.
- Click Show next to one of the connection strings.
- Copy the full Connection string value.
Store the connection string securely. It provides full access to your storage account. For production, prefer a service principal or workload identity with the Storage Blob Data Contributor role, or a Shared Access Signature (SAS) with limited permissions. See Authentication.
Configuration
| Field | Type | Required | Description |
|---|---|---|---|
account_name | string | Yes* | The Azure Storage account name. Also accepts storage_account. *Not needed with a connection string. |
container | string | Yes | The blob container name. Also accepts container_name. |
auth_method | select | No | connection_string, account_key, sas, service_principal, managed_identity, workload_identity or default. See Authentication. |
connection_string | secret | Per method | Azure Storage connection string. |
account_key | secret | Per method | Storage account access key. |
sas_token | secret | Per method | SAS token granting Write + Create on the container. |
tenant_id | string | Per method | Microsoft Entra tenant ID (service principal; optional override for workload identity). |
client_id | string | Per method | Application (client) ID (service principal; user-assigned managed identity; optional override for workload identity). |
client_secret | secret | Per method | Service principal client secret. |
prefix | string | No | Optional prefix (virtual directory) prepended to all blob names. Include a trailing slash. |
file_format | select | Yes | json (newline-delimited, default), parquet or csv. |
parquet_compression | select | No | snappy (default), zstd, gzip or none. |
schema_mode | select | No | canonical (default, 16 fixed columns) or blueprint (typed columns from the blueprint). |
schema_columns | textarea | No | Declared columns for blueprint mode, one per line as name[:type[:source]]. |
batch_size | number | No | Events per file; above 1 consolidates events into one file per partition-hour. Typical: 1000. |
batch_max_wait_ms | number | No | Flush a batch after this long even if it is not full. Default 60000. |
Authentication
auth_method | Credentials | Notes |
|---|---|---|
connection_string | connection_string | Carries the account name and key. |
account_key | account_name, account_key | |
sas | account_name, sas_token | Scope the SAS to the container with Write and Create. |
service_principal | tenant_id, client_id, client_secret | A Microsoft Entra app registration. |
managed_identity | client_id optional | The identity of the VM or node running Signal. Set client_id to pick a user-assigned identity. |
workload_identity | none | AKS workload identity. See below. |
default | none | The Azure SDK default credential chain in the delivery worker environment. |
Service principal, managed identity and workload identity authenticate with Microsoft Entra ID, so the identity needs the Storage Blob Data Contributor role on the storage account or container. No storage key is stored in Signal for these methods.
Choosing an Authentication Method makes that method’s credentials required: the connection cannot be saved until they are filled in. If auth_method is not set, Signal uses the first credential filled in: connection string, then access key, then SAS token, then a complete service principal, then the default credential chain. Integrations created before auth_method existed keep working unchanged.
AKS workload identity
When Signal runs on AKS, the delivery workers can authenticate as a user-assigned managed identity without any secret. Enable the OIDC issuer and workload identity on the cluster, grant the identity Storage Blob Data Contributor, and add a federated credential for the delivery worker service account:
az identity federated-credential create -g <resource-group> --identity-name signal-delivery \
--name signal-delivery-worker \
--issuer "$(az aks show -g <resource-group> -n <cluster> --query oidcIssuerProfile.issuerUrl -o tsv)" \
--subject system:serviceaccount:<namespace>:<release>-delivery-worker \
--audiences api://AzureADTokenExchangeThen set the Helm values:
deliveryWorkers:
azureWorkloadIdentity:
enabled: true
clientId: "<identity client id>"
tenantId: "<tenant id>" # optionalThe chart creates the <release>-delivery-worker service account with the workload identity annotations and labels the delivery worker pods. Set auth_method to workload_identity on the integration. To use a different identity for one integration, set its client_id (and tenant_id); that identity needs its own federated credential for the same service account.
Token requests go to login.microsoftonline.com over HTTPS. If the storage account is reached through a private endpoint, add its address to deliveryWorkers.egressAllowPrivateCIDRs. managed_identity calls the instance metadata endpoint on port 80, so it also needs 80 in deliveryWorkers.egressPorts.
Before you connect
- The container must already exist. Signal writes files into it but does not create it.
- Storage firewall. If the account’s firewall is on, allow the delivery workers’ outbound IP addresses, or reach the account through a private endpoint for the
blobsub-resource. - Shared Key disabled. If the account disallows Shared Key authorization, access keys, key-based connection strings and account SAS tokens are refused: use a Microsoft Entra ID method or a user delegation SAS.
- Azure Government and Azure China work only with a connection string: the other methods write to
<account>.blob.core.windows.net.
Signal Setup
Quick Setup
- Navigate to Integrations in the sidebar.
- Open the Integration Library tab.
- Find Azure Blob Storage or filter by Cloud Storage.
- Click Install, select a variant if available, and fill in the required fields.
- Click Install Integration to create the integration with a ready-to-use default blueprint.
API Setup
curl -X POST http://localhost:8084/v1/admin/integration-catalog/azure_blob/install \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Azure Blob Storage",
"variant": "default",
"config": {
"account_name": "dataflysignalevents",
"container": "datafly-events",
"account_key": "YOUR_STORAGE_ACCOUNT_KEY",
"prefix": "events/"
},
"delivery_mode": "server_side"
}'Presets
The Azure Blob Storage blueprint ships the same eight presets as the other file-storage integrations. Each writes one named field per documented event-spec property (canonical names first, the legacy names as fallbacks), plus a global block under @root with the envelope fields, UTM parameters, the analytics and marketing consent flags, and email, phone and names as normalised SHA-256 digests (email_sha256, phone_sha256, first_name_sha256, last_name_sha256, name_sha256; phone shaped to E.164 first). A raw field is a deliberate edit to the installed blueprint.
| Preset | Events |
|---|---|
Retail (ecommerce) | Retail funnel per the ecommerce event spec: lists, search, product, cart, checkout, purchase, refund, wishlist, promotions, account and lead events, one named field per documented property. |
General (general) | Default. The key cross-vertical events (page, identify, search, product view, add to cart, checkout, purchase, refund, lead, sign-up, login, subscription start and cancel), one named field per documented event-spec property. |
Insurance (insurance) | Insurance funnel per the insurance event spec: quote (started, get_quote, requested, received, completed), lead (generate_lead, lead_submitted, contact_requested), policy view, basket, application and checkout, purchase and policy_purchased (policy number as transaction_id), renewals, cancellations, documents and claims. |
Travel (travel) | Travel funnel per the travel event spec: flight, hotel, car and destination search, listings, booking, payment, purchase, modifications and cancellations, check-in, loyalty and reviews. |
Media & Streaming (media) | Media and streaming per the media event spec: content engagement, video and audio playback, video content and ads, podcasts, subscriptions and purchases. |
Publishing (publishing) | Publishing per the publishing event spec: articles, content engagement, authors and topics, video and audio, newsletters, paywall and subscriptions. |
B2B / SaaS (saas) | B2B and SaaS per the SaaS event spec: marketing and leads, authentication, onboarding, trial and subscription lifecycle, product usage, support, referrals and seats. |
Automotive (automotive) | Automotive funnel per the automotive event spec: vehicle search, views, saves, comparisons, brochures, configuration, finance quotes, trade-in valuations, leads, dealer contact, test drives, reservations, purchase and service bookings. |
What Signal writes
By default each event is one blob (set batch_size to consolidate events into one file per partition-hour). Blob names use Hive-style partitions of the event time in UTC, which Synapse, Databricks and Fabric read directly:
<container>/<prefix>/yyyy=2026/mm=09/dd=23/hh=09/<pipeline_id>/<message_id>.ndjsonThe extension and content type follow file_format: .ndjson (the default), .csv or .parquet (Snappy-compressed by default). CSV and Parquet use either the canonical 16-column layout (message_id, event_type, event_name, anonymous_id, user_id, ip_address, user_agent, page_url, page_path, page_referrer, org_id, pipeline_id, event_timestamp, received_at, properties_json, context_json) or, with schema_mode set to blueprint, one typed column per field. See File formats and schema.
Only what the blueprint maps is written to properties (the row contract). Identify traits and client properties the blueprint does not map are never written, so the identified person’s name and email reach the file only as the digests above. An event with no section in the preset still arrives, with the @root block only: add a section for any other event whose properties you need.
A purchase written with the Retail preset (the properties object; digests are SHA-256 of the normalised value):
{
"@root": {
"anonymous_id": "df_anon_spec_0001",
"consent_analytics": true,
"consent_marketing": true,
"email_sha256": "86e0b9e56c17cc4d12387e1949b85053fbe73bc3ce5a1188713a9d300cc6133d",
"event_id": "evt-spec-0001",
"event_name": "purchase",
"event_timestamp": "2026-09-23T09:30:00.000Z",
"event_type": "track",
"first_name_sha256": "81f8f6dde88365f3928796ec7aa53f72820b06db8664f5fe76a7eb13e24546a2",
"ip_address": "203.0.113.7",
"last_name_sha256": "799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f",
"locale": "en-GB",
"message_id": "msg-spec-0001",
"name_sha256": "ed37d99b1445238af3386f81a77a2caf3fffc0cd610be39b4f3ba53943dc66bf",
"page_referrer": "https://www.google.com/",
"page_url": "https://shop.example.co.uk/checkout?gclid=g-123",
"phone_sha256": "a8acc3a90a7b4e4dc65e93db9240ed26523050ef754d63b75b5161de76781436",
"timezone": "Europe/London",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Version/18.0 Safari/605.1.15",
"user_id": "cust-1001"
},
"affiliation": "BMW Park Lane",
"coupon": "SAVE10",
"currency": "GBP",
"items": [
{
"currency": "GBP",
"item_brand": "BMW",
"item_category": "SUV",
"item_id": "STK-7421",
"item_name": "BMW X3 xDrive30e",
"item_variant": "M Sport",
"price": 49995,
"quantity": 1
}
],
"shipping": 10,
"tax": 43.33,
"transaction_id": "ORD-10042",
"value": 49995
}Stored event context
Alongside the fields your blueprint maps (where email, phone, names and address are stored as normalised SHA-256 by default), each file row carries the event’s collected context exactly as Signal received it. The blueprint does not filter or hash the context: this is deliberate (decision 2026-09-23), so the context is complete for attribution, fraud and debugging queries, and it is listed here so you can decide what to keep.
| Stored | What it contains |
|---|---|
| Visitor request | IP address, user agent, Accept-Language, locale and timezone |
| Page | URL including its query string, path, title and referrer |
| Campaign | UTM source, medium, campaign, term and content |
| Consent | The visitor’s consent state |
| Location | GeoIP country, region and city, and the network (ASN) |
| Device | Screen size; for app events the OS and version, manufacturer, model, advertising ID and IDFV when the app collected them |
| Vendor IDs | Cookie and click identifiers in vendor_ids (for example the _fbp browser ID, gclid, ttclid, the GA client ID) |
| Session and library | Session ID, number and engagement time; the collector’s name and version |
| Partner attributes | Profile attributes from partner enrichment (partner_attrs) |
In each NDJSON line these are the context object, with anonymous_id, user_id, device_id, message_id, event_id, click_ids and collected_ids beside the mapped properties. In CSV and Parquet with the canonical schema they are the ip_address, user_agent, page_url, page_path and page_referrer columns and, in full, the context_json column. In blueprint schema mode only the columns you declare are written.
The IP address, device and advertising IDs, and cookie and click identifiers are personal data under UK and EU GDPR. To keep any of them out of storage, strip, mask or hash the field for every integration with the organisation data layer, apply URL redaction to page URLs, or remove it in the installed blueprint; and set the storage’s retention to match your privacy notice. See Personal data by integration.
Consent
The integration’s consent category in Signal decides which events reach Azure Blob Storage; events that fail it are never written. Every row also carries the visitor’s consent state (context.consent / context_json, and consent_analytics / consent_marketing in @root) so downstream queries can apply their own rules.
Testing
- Enable the integration in Signal and trigger a test event on your website.
- In the Azure portal, open your Storage Account > Containers > your container.
- Browse the blob list and verify that event files are appearing under the configured prefix.
- Click on a blob to download and inspect the event data.
- In Signal, check the Live Events view to confirm delivery status shows as successful.
Troubleshooting
| Problem | Solution |
|---|---|
| Events not appearing in the container | Verify the storage account name, container name, and connection string are correct. |
AuthenticationFailed | The connection string is invalid or the account key has been rotated. Get a fresh connection string from Access Keys. |
ContainerNotFound | The container does not exist. Verify the container name in the Azure portal. |
AuthorizationPermissionMismatch | If using a SAS token, ensure it has Write and Create permissions on the container. With Entra ID authentication, assign Storage Blob Data Contributor to the identity (role assignments can take a few minutes to apply). |
workload_identity credential error at startup | The delivery worker pod has no workload identity environment. Check deliveryWorkers.azureWorkloadIdentity in the Helm values and that the pod carries the azure.workload.identity/use label. |
| Blobs appearing but empty | Check the batch settings. Events are buffered before flushing to blobs. |
| Connection timeout | Verify the storage account is accessible from Signal’s network. Check any firewall rules under Networking > Firewalls and virtual networks. |
Visit Azure Blob Storage documentation for full API reference, lifecycle management, and access tier configuration.