Skip to main content
Version: 5.1

Track File Changes on an Azure Files Share

Time: About 20 minutes | What you'll build: A scheduled automation that reports every change on an Azure Files share, logging a "file created", "file modified", or "file deleted" event for each difference since its previous run.

Azure Files has no change feed and no file-level events, but many integrations genuinely need change events: syncing a share to another system, auditing edits, or triggering downstream work only when a file is new or different. In this guide you derive those events yourself, from information the share already keeps.

The key is the entity tag (eTag), a value Azure replaces on every write to a file. The tracker you build runs on a schedule: each run lists the share, compares every file's entity tag against a snapshot saved by the previous run, fires a hook for each difference, saves the new snapshot back to the share, and exits. A listing carries metadata only, so a run never downloads file content: checking a share costs a few list calls whether it holds megabytes or terabytes. And because the snapshot lives on the share itself, runs need no local state, and deletions that happen between runs are still reported.

How it works​

The WSO2 Integrator Scheduler invokes the automation periodically, and each run follows the same short flow:

  1. Load the snapshot the previous run saved on the share. On the very first run there is none, so the run starts from an empty baseline.
  2. List the share with extended info, collecting each file's path and entity tag. The listing reads metadata only; no file content is transferred.
  3. Compare the listing against the snapshot: an unknown path is a creation, a changed entity tag is a modification, and a snapshot entry missing from the listing is a deletion. Each difference fires the matching event hook, which is where your real integration logic goes.
  4. Save the new snapshot to the share and exit.

Before you begin​

Prerequisites
  • A working WSO2 Integrator environment. See Cloud setup.
  • An Azure storage account with a file share, and its account name and access key. The Azure Files connector setup guide walks through creating these.
  • The share must already exist. A tracker watches a share, it does not provision one.

Build the change tracker​

Build the automation in the Visual Designer; only the three event hooks are added in the code view. Switch to the Ballerina Code tab at any point to see the finished source.

Step 1: Create the automation​

An automation runs on a schedule with no inbound request, which is the right artifact for a tracker: each run checks the share once and exits, so nothing stays resident between checks.

  1. Create a new integration named ChangeTracker in a project named azure-files-change-tracker.
  2. Add an Automation artifact to the integration.

You land in the flow editor with a single Start node, the entry point the scheduler will call.

Add Artifact panel with the Automation artifact selected for the ChangeTracker integrationAdd Artifact panel with the Automation artifact selected for the ChangeTracker integration

Step 2: Connect to the share​

The tracker lists the share and stores its snapshot through one connection.

  1. Add a connection: select Add Connection, open Search Connectors, then search for azure.storage.files and select ballerinax/azure.storage.files.

  2. Configure the connection:

    FieldValue
    Share NameThe name of the share to watch
    AuthThe storage account name and access key
    Best practice

    Don't hardcode these values into the connection. Click each field and select Configurables in the Expression Panel's helper pane, then click New Configurable, so the values are supplied at runtime instead of stored in the flow. The tracker uses four: shareName, accountName, accountKey, and the defaulted snapshotPath, which only needs overriding to relocate the snapshot.

    Configure Files dialog with the share name and the credentials supplied through configurablesConfigure Files dialog with the share name and the credentials supplied through configurables
  3. Set Connection Name to shareClient, then select Save Connection.

Step 3: Add the event hooks​

The three hooks are where the tracker becomes your integration: the guide logs, but a real one might push a notification, enqueue downstream work, or sync the file elsewhere. Add them to functions.bal in the code view first, so the flow you build next can call them.

// Replace these three hooks with whatever the change should trigger.
isolated function onFileCreated(string path) {
log:printInfo("file created", path = path);
}

isolated function onFileModified(string path) {
log:printInfo("file modified", path = path);
}

isolated function onFileDeleted(string path) {
log:printInfo("file deleted", path = path);
}

Step 4: Build the diff flow​

The flow makes three moves: load the previous run's view of the share, list what is there now, and report every difference before saving the new view. The view is the Snapshot record in types.bal, saved on the share itself at the snapshotPath configurable and excluded from the diff so the tracker does not report its own bookkeeping. Build the moves on the canvas:

  1. Add a Declare Variable node for previous, a Snapshot, initialized to {}.
  2. Add the hasFile operation from the shareClient connection, pointed at snapshotPath, with its result in snapshotExists.
  3. Add an If node on snapshotExists. Inside it, the getFile operation reads the saved snapshot directly into previous: the operation binds the file's content by the target's record type, so no conversion step follows it.
  4. Add a Declare Variable node for current (another Snapshot, {}), then the list operation with recursive and includeExtendedInfo enabled, its result in entries. A Custom Expression node drains the listing into current.entries, keyed path to entity tag, skipping directories and snapshotPath itself.
  5. Add a Foreach node over current.entries.entries(). Inside it, declare known = previous.entries[path], and an If node calls onFileCreated when known is () and onFileModified when known != eTag.
  6. Close the flow with a Foreach over previous.entries that calls onFileDeleted for every path missing from current.entries, and the upload operation that saves current to snapshotPath: a record uploads as JSON resolved from the destination extension.

Two details are load-bearing. The listing's includeExtendedInfo is what puts each file's entity tag on its listing entry; without it the tags are absent and every comparison would be meaningless. And the snapshot is saved only after every difference has been reported, so a run that fails midway reports the same differences again on its next attempt instead of losing them.

Your flow should match the checkpoint below.

Flow editor showing the tracker automation: the snapshot check with hasFile and getFile, the listing drain into the current map, and the comparison foreachFlow editor showing the tracker automation: the snapshot check with hasFile and getFile, the listing drain into the current map, and the comparison foreach

Run and verify​

  1. Go to Configurations, supply the storage account name, the access key, and the share name, then select Run on the integration overview.

  2. The first run reports every file already on the share as created: there is no previous snapshot, so the first listing is the baseline. The run then saves its snapshot to the share (at /.change-tracker-snapshot.json unless snapshotPath says otherwise) and exits.

  3. Upload a file to the share, through the Azure portal, the Azure CLI, or an SMB mount, and run the automation again for a file created line. Overwrite the same file with different content and run again for file modified, and delete it and run once more for file deleted.

Automation flow with the terminal showing a file modified line and a file deleted line from a runAutomation flow with the terminal showing a file modified line and a file deleted line from a run

To operate the tracker continuously, schedule the run instead of starting it by hand: push the integration to the cloud and schedule periodic runs there, or use a cron entry, a Kubernetes CronJob, or a host scheduler. Pick the cadence by how fresh the events must be: a run costs a few list calls regardless of the share's size, so even frequent schedules stay cheap.

What's next​

Now that the tracker works, you can take it further:

  • Track changes within seconds. If a schedule's granularity is too coarse, run the same diff resident: move the body of main into a function and invoke it from a task:Listener service with trigger = {interval: ...}. The diff stays metadata-only, so even a few-second interval costs only listing calls; the trade is a process that never exits.
  • Make the hooks idempotent. The snapshot is saved only at the end of a run, so a run that fails after firing some hooks reports those same differences again on the next run. That at-least-once behavior is what keeps events from being lost, and hooks that tolerate a repeat make it harmless downstream.
  • Process the files themselves. To act on file content as files arrive, use the Azure Files trigger: it dispatches each file to a handler and fits consume-style processing, where each handled file is deleted or moved out of the watched path.