IntegrationsObject StorageAzure Blob Storage

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

  1. In the Azure portal, search for Storage accounts and click Create.
  2. Select your Subscription and Resource group (or create a new one).
  3. Enter a Storage account name (e.g. dataflysignalevents). Names must be globally unique, 3-24 characters, lowercase letters and numbers only.
  4. Select a Region close to your Signal deployment.
  5. Choose Performance: Standard (recommended) or Premium.
  6. Choose Redundancy: LRS (cheapest), GRS, or ZRS based on your durability requirements.
  7. Click Review + Create > Create.

Create a Blob Container

  1. Open your new Storage Account in the Azure portal.
  2. In the left sidebar, click Containers (under Data storage).
  3. Click + Container.
  4. Enter a Name (e.g. datafly-events).
  5. Set Public access level to Private (recommended).
  6. Click Create.

Get the Connection String

  1. In your Storage Account, go to Security + networking > Access keys.
  2. Click Show next to one of the connection strings.
  3. 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

FieldTypeRequiredDescription
account_namestringYes*The Azure Storage account name. Also accepts storage_account. *Not needed with a connection string.
containerstringYesThe blob container name. Also accepts container_name.
auth_methodselectNoconnection_string, account_key, sas, service_principal, managed_identity, workload_identity or default. See Authentication.
connection_stringsecretPer methodAzure Storage connection string.
account_keysecretPer methodStorage account access key.
sas_tokensecretPer methodSAS token granting Write + Create on the container.
tenant_idstringPer methodMicrosoft Entra tenant ID (service principal; optional override for workload identity).
client_idstringPer methodApplication (client) ID (service principal; user-assigned managed identity; optional override for workload identity).
client_secretsecretPer methodService principal client secret.
prefixstringNoOptional prefix (virtual directory) prepended to all blob names. Include a trailing slash.
file_formatselectYesjson (newline-delimited, default), parquet or csv.
parquet_compressionselectNosnappy (default), zstd, gzip or none.
schema_modeselectNocanonical (default, 16 fixed columns) or blueprint (typed columns from the blueprint).
schema_columnstextareaNoDeclared columns for blueprint mode, one per line as name[:type[:source]].
batch_sizenumberNoEvents per file; above 1 consolidates events into one file per partition-hour. Typical: 1000.
batch_max_wait_msnumberNoFlush a batch after this long even if it is not full. Default 60000.

Authentication

auth_methodCredentialsNotes
connection_stringconnection_stringCarries the account name and key.
account_keyaccount_name, account_key
sasaccount_name, sas_tokenScope the SAS to the container with Write and Create.
service_principaltenant_id, client_id, client_secretA Microsoft Entra app registration.
managed_identityclient_id optionalThe identity of the VM or node running Signal. Set client_id to pick a user-assigned identity.
workload_identitynoneAKS workload identity. See below.
defaultnoneThe 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://AzureADTokenExchange

Then set the Helm values:

deliveryWorkers:
  azureWorkloadIdentity:
    enabled: true
    clientId: "<identity client id>"
    tenantId: "<tenant id>"   # optional

The 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 blob sub-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

  1. Navigate to Integrations in the sidebar.
  2. Open the Integration Library tab.
  3. Find Azure Blob Storage or filter by Cloud Storage.
  4. Click Install, select a variant if available, and fill in the required fields.
  5. 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.

PresetEvents
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>.ndjson

The 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.

StoredWhat it contains
Visitor requestIP address, user agent, Accept-Language, locale and timezone
PageURL including its query string, path, title and referrer
CampaignUTM source, medium, campaign, term and content
ConsentThe visitor’s consent state
LocationGeoIP country, region and city, and the network (ASN)
DeviceScreen size; for app events the OS and version, manufacturer, model, advertising ID and IDFV when the app collected them
Vendor IDsCookie and click identifiers in vendor_ids (for example the _fbp browser ID, gclid, ttclid, the GA client ID)
Session and librarySession ID, number and engagement time; the collector’s name and version
Partner attributesProfile 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.

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

  1. Enable the integration in Signal and trigger a test event on your website.
  2. In the Azure portal, open your Storage Account > Containers > your container.
  3. Browse the blob list and verify that event files are appearing under the configured prefix.
  4. Click on a blob to download and inspect the event data.
  5. In Signal, check the Live Events view to confirm delivery status shows as successful.

Troubleshooting

ProblemSolution
Events not appearing in the containerVerify the storage account name, container name, and connection string are correct.
AuthenticationFailedThe connection string is invalid or the account key has been rotated. Get a fresh connection string from Access Keys.
ContainerNotFoundThe container does not exist. Verify the container name in the Azure portal.
AuthorizationPermissionMismatchIf 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 startupThe 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 emptyCheck the batch settings. Events are buffered before flushing to blobs.
Connection timeoutVerify 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.

See also