Azure Blob Storage
Lists a prefix of one container every poll interval, reads each blob it finds new or changed, and splits it into records. Only one worker of each group reads this source at a time.
Use it where the storage account cannot notify. Where it can, Azure Blob via Storage queue reads each blob as the storage account announces it, with no listing, and any number of workers share the work.
Example
Reads each blob under app/ one line per record, as the worker host's managed
identity, and moves it under done/ once Smeltr delivers it.
{
"id": "container-export",
"type": "azureBlobList",
"account": "acmelogs",
"container": "logs",
"prefix": "app/",
"framing": "lines",
"afterCommit": "move",
"movePrefix": "done/"
} 

Before you start
- Choose what it does with a blob once Smeltr delivers it,
afterCommit. That sets the permission it needs:keeponly lists and reads;deletealso deletes;movealso writes the blob undermovePrefix, then deletes it. - Choose how it signs in, and give that identity that permission on the container. A key,
signature or client secret goes in a credential.
- the worker host's managed identity, or a service principal, given Storage Blob Data Reader on the container for
keep, or Storage Blob Data Contributor fordeleteandmove(see assign a role for data access); - an account key; or
- a shared access signature for the blob service, allowed to list and read, and also to delete for
delete, or to create, write and delete formove.
- the worker host's managed identity, or a service principal, given Storage Blob Data Reader on the container for
Settings
It also takes the settings every source takes.
container string required
The container's name, alone.
logsinsights-logs-auditeventaccount string required
The storage account holding the container.
acmelogsacmearchive01prefix string default
Only blobs whose name starts with it. It does not start with /. Empty is the whole container.
app/resourceId=/SUBSCRIPTIONS/logs/2026/accessKey credential default none
The credential holding the account's access key. Set at most one of accessKey, sasToken, clientSecret and managedIdentityClientId; with none, the host's managed or workload identity is used.
AZURE_ACCESS_KEY{"secret": "azure-access-key","version": 1}sasToken credential default none
The credential holding a shared access signature that covers the blob service.
AZURE_SAS_TOKEN{"secret": "azure-sas-token","version": 1}tenantId string default none
A service principal's tenant. Set tenantId, clientId and clientSecret together, or none of them.
00000000-0000-0000-0000-000000000000clientId string default none
A service principal's client id.
11111111-1111-1111-1111-111111111111clientSecret credential default none
The credential holding a service principal's client secret.
AZURE_CLIENT_SECRET{"secret": "azure-client-secret","version": 1}managedIdentityClientId string default the host's managed or workload identity
The client id of the user-assigned managed identity to sign in as. Set it when the worker's host has more than one managed identity. Left out, it signs in as the host's managed identity, or an AKS pod's workload identity.
22222222-2222-2222-2222-222222222222format string default each payload is text, in _raw
How each payload becomes the record.
avro- Reads Avro files. Fields you don't declare are kept.
fields list of fields default none
The Avro fields to read, each with a name and a type such as i64 or str. Name a nested field with dots, such as user.id. Top-level fields you don't list are kept. Only with format avro.
[{"name": "user_id","type": "i64"},{"name": "user.name","type": "str"}]framing string default lines
How an object becomes records.
lines- Gives one record per line, or per piece
lineBreakercuts. object- Gives the whole object as one record, for a JSON document such as CloudTrail's that a pipeline unrolls.
escapedLines- Gives one record per line, unescaped as a
bytesobject destination wrote it.
lineBreaker string default a record per line
With lines framing, and only then: where a record ends. Its first capture group is the break and is dropped: the bytes before it end a record, line ends and all, and the bytes after it start the next. What follows the group is kept, so the pattern can say how a record starts: ([\r\n]+)\d{4}- keeps a stack trace with the line that logged it, and </event>(\s*) cuts XML events with no line ends between them. It needs a capture group and must not match the empty string.
([\r\n]+)\d{4}-</event>(\s*)identity identity default none
The connection identity it connects under, for a private CA or a client certificate.
azure-proxycorp-proxyafterCommit string default keep
What it does with a blob once every record is delivered.
keep- Leaves it and records it as read. Needs only read access, and costs a full listing per poll.
delete- Deletes it.
move- Copies it under
movePrefixin place ofprefix, then deletes it.
movePrefix string default none
Where move puts a blob, in place of prefix. Only with move, and outside prefix. It does not start with /, and ends in / when prefix does or is empty, and only then.
done/archive/2026/processed/app/Advanced
start string default everything
What it reads first.
everything- Reads everything its first listing finds.
new- Reads only what arrives after its first listing. Needs
afterCommitkeep.
pollIntervalSecs integer default 60
Seconds from the start of one listing to the start of the next. At least 1.
300303600endpoint string default the account's, in Azure's public cloud
The blob service's address, such as Azurite's or a sovereign cloud's.
http://localhost:10000/acmelogshttps://acmelogs.blob.core.usgovcloudapi.netauthEndpoint string default its cloud's own
Where a client secret is exchanged for a token. Only with clientSecret.
https://login.microsoftonline.ushttps://login.chinacloudapi.cnencoding string default auto
How a record's bytes become _raw's text: auto, none, or a WHATWG encoding label other than the replacement ones, such as utf-8, utf-16le or shift_jis.
auto- Detects each record's encoding.
utf-8- Decodes every record as UTF-8, as any other label decodes as its own encoding. Bytes that do not decode become U+FFFD.
none- Decodes nothing: the record has no
_raw, and its payload is__source.bytesalone. A pipeline reads it as text withdecode(__source.bytes, encoding).
Fields it gives
__source.bucket holds the container, and __source.key the blob's name.
| Field | Type | Holds |
|---|---|---|
__source.name | str | The name of the source that read the record. |
__source.type | str | The kind of source that read it, its type, such as kafka, s3List, httpApi or hecReceiver. |
__source.bucket | str | The bucket, or Azure container, the record's object is in. |
__source.key | str | The key, or Azure blob name, of the object the record was read from. |
__source.offset | i64 | The record's offset in its Kafka or Event Hubs partition, or its record number in its object, from 0. |
__source.bytes | bytes | The record's payload as the source read it, before any decoding. The source decodes _raw from it. A bytes destination writes it unless the destination names another field. Text functions refuse it; decode(__source.bytes, encoding) reads it as text. |
How it works
Every pollIntervalSecs it lists the blobs whose names start with prefix. The source reads a blob once two listings in a row find it unchanged, since
an append blob grows while something writes it. It reads the blob whole: it decompresses a gzip,
zstd, framed snappy or framed lz4 blob first and reads any other as it is. An empty blob holds
no record, and the source does not read it.
One worker of the group reads it at a time, the one the leader holds it for; the others read
nothing. The leader gives the hold to another worker after three heartbeat intervals without
hearing from the holder, the holder's interval or the leader's, whichever is longer. With keep, its progress is a ledger: the blobs it has read, each with the version it
read. The holder reports it on its heartbeat and the leader keeps it; the next holder fetches
the ledger and reads only what is not in it. The next holder delivers again what the previous
holder delivered after the last progress the leader kept. With delete or move the source keeps no progress: the next holder reads what the container still holds,
so it delivers again a blob delivered but not yet deleted or moved.
afterCommit says what it does with a blob once Smeltr delivers every record.
keepleaves the blob and records it in the ledger. When a blob changes after the source reads it, the source reads it again whole. A blob that is gone from the listing leaves the ledger. Every listing lists the whole prefix, so the cost of each poll grows with what the prefix holds.deletedeletes the blob, andmovecopies it undermovePrefixin place ofprefixand deletes it. The container is then its own record of what is left to read, and the source keeps no ledger. A blob that changed after the source read it stays in place, and the source reads it again. It deletes or moves an empty blob unread.
A blob handed out five times and never delivered is no longer read until it changes. With keep it is recorded in the ledger as read; with delete or move it stays in the container. The worker log says which blob. With delete or move only the holder remembers it, so a restart or a new holder hands it out again.
start says what the first listing reads. everything reads every blob
it finds. new records every blob it finds as read and reads only what arrives or
changes after it; it needs afterCommit keep, since only the ledger
remembers what was skipped.
The source's progress belongs to its name and to its storage account, container and prefix. Renamed, or pointed at another container or prefix, it starts afresh.
Once you remove the source and deploy, the leader forgets its progress, so a rollback that brings the source back starts it afresh.
Verify
Upload a blob with a line you can recognize under the prefix, then find that line at the
destination after two listings. On the Sources page, the source's worker column
names the worker that holds it, and with keep how many blobs its ledger holds. With delete or move, the blob leaves the prefix once it is delivered.
Troubleshooting
- Nothing arrives
- The source reads a blob only on the second listing that finds it unchanged, so wait two
pollIntervalSecs. Check that its name starts withprefix, which does not start with/, and the worker log for a listing that failed. - The worker column is empty
- No worker of the group runs the source, or none has reported it to the leader. Check that the group's workers are enrolled and running the deployment.
- A blob is read again
- It changed after the source read it, or the source moved to another worker before its last progress reached the leader. Both deliver its records again.
- Blobs stay in the container with delete or move
- The identity cannot delete or write there: check its role or signature, and the worker log for the reason.