Skip to content

Airlock

In a Trusted Research Environment (TRE) the workspaces represent a security boundary that enables researchers to access data, execute analysis, apply algorithms and collect reports. The airlock capability is the only mechanism that allows users to import or export data, tools or other file based artefacts in a secure fashion with a human approval. This constitutes the mechanism focused on preventing data exfiltration and securing TRE and its workspaces from inappropriate data, while allowing researchers to work on their projects and execute their tasks. The airlock feature brings several actions: ingress/egress mechanism, data movement, security gates, approval mechanism and notifications. As part of TRE's Safe Settings all activity must be tracked for auditing purposes.

The Airlock feature aims to address these goals:

  • Prevent unauthorised data import or export.
  • Provide a process to allow approved data to be imported through the security boundary of a TRE Workspace.
  • Track requests and decisions, supporting cycles of revision, approval or rejection.
  • Automatically scan data being imported for security issues.
  • Require manual review by the Airlock Manager for data being exported or imported.
  • Notify the requesting researcher of progress and required actions.
  • Audit all steps within the airlock process.

Typically in a TRE, the Airlock feature would be used to allow a researcher to export the outputs of a research project such as summary results. With the airlock, data to be exported must go through a human review, typically undertaken by a data governance team.

The Airlock feature creates events on every meaningful step of the process, enabling organisations to extend the notification mechanism.

Storage Architecture

The airlock uses a consolidated storage architecture with 2 storage accounts and metadata-based stage management. Each airlock request gets a dedicated container (named with the request ID), and the request's stage is tracked via container metadata rather than by copying data between storage accounts.

graph TB
    subgraph External["External"]
        researcher["fa:fa-user Researcher"]
        reviewer["fa:fa-user-shield Airlock Manager"]
    end

    appgw["fa:fa-shield-alt App Gateway"]

    subgraph Core["TRE Core"]
        direction TB
        subgraph CoreStorage["Core: stalairlock"]
            ie{{"stage: import-external"}}
            eapp{{"stage: export-approved"}}
            iip{{"stage: import-in-progress"}}
            irej{{"stage: import-rejected"}}
            iblk{{"stage: import-blocked"}}
        end
        processor["fa:fa-cog Airlock Processor"]
    end

    subgraph WSStorage["Workspace: stalairlockg"]
        iappr{{"stage: import-approved"}}
        eint{{"stage: export-internal"}}
        eip{{"stage: export-in-progress"}}
        erej{{"stage: export-rejected"}}
        eblk{{"stage: export-blocked"}}
    end

    subgraph Workspace["TRE Workspace"]
        vm["fa:fa-desktop Researcher VM"]
    end

    researcher -- "SAS token" --> appgw
    reviewer -- "SAS token" --> appgw
    appgw -- "Public stages only" --> CoreStorage
    processor -. "All stages" .-> CoreStorage
    processor -. "All stages" .-> WSStorage
    vm -- "Private Endpoint" --> WSStorage

    style Core fill:#1a3d6d,stroke:#0d2240,color:#fff
    style CoreStorage fill:#2c5f9e,stroke:#1a3d6d,color:#fff
    style WSStorage fill:#8b5c00,stroke:#5c3d00,color:#fff
    style External fill:#444,stroke:#333,color:#fff
    style Workspace fill:#1a5c1a,stroke:#0d330d,color:#fff
    style appgw fill:#0078d4,stroke:#005a9e,color:#fff
    style processor fill:#cc7000,stroke:#995300,color:#fff
    style vm fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style ie fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style eapp fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style iip fill:#4a6fa5,stroke:#2c5f9e,color:#fff
    style irej fill:#b85450,stroke:#8b3e3b,color:#fff
    style iblk fill:#b85450,stroke:#8b3e3b,color:#fff
    style iappr fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style eint fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style eip fill:#8b7800,stroke:#5c5000,color:#fff
    style erej fill:#b85450,stroke:#8b3e3b,color:#fff
    style eblk fill:#b85450,stroke:#8b3e3b,color:#fff
    style researcher fill:#0078d4,stroke:#005a9e,color:#fff
    style reviewer fill:#0078d4,stroke:#005a9e,color:#fff

Airlock architecture overview. Hexagon shapes represent container metadata stages. Green = user-accessible, yellow = processing, red = terminal.

Storage Accounts:

Storage Account Name Pattern Purpose
Core Storage stalairlock{tre_id} All core-managed stages: import external, in-progress, rejected, blocked; export approved
Global Workspace Storage stalairlockg{tre_id} All workspace-managed stages: import approved; export internal, in-progress, rejected, blocked

Key design principles:

  • Metadata over movement — In-account stage transitions (submitted→in-review, in-review→rejected/blocked) simply update container metadata, providing near-instant transitions. Data is physically copied in only two places: when a request is submitted the writable draft container is sealed (its file is copied into an immutable request-id container and the draft is deleted, structurally revoking the researcher's SAS), and again on approval when data crosses the core/workspace boundary.
  • ABAC security — Azure Attribute-Based Access Control conditions restrict which stages each identity can access on the storage account, enforced at the Azure RBAC layer.
  • Shared infrastructure — All workspaces share the same workspace storage account, with network isolation via per-workspace private endpoints and ABAC conditions filtering by workspace_id.

Ingress/Egress Mechanism

The Airlock allows a TRE user to start the import or export process to a given workspace. A number of milestones must be reached in order to complete a successful import or export. These milestones are defined using the following states:

  1. Draft: An Airlock request has been created but has not yet started. The TRE User/Researcher has access to a storage container and must upload the data to be processed. At this point the airlock import/export processes allow a single file to be processed. However a compressed file may be used (zip).
  2. Submitted: The request was submitted by the researcher (not yet processed).
  3. In-Review: The request is ready to be reviewed. This state can be reached directly from Submitted state or after going through a successful security scan (found clean).
  4. Approval In-progress: The Airlock request has been approved, however data movement is still ongoing.
  5. Approved: The Airlock request has been approved. Data has been securely verified and manually reviewed. The data is now in its final location. For an import process the data is available in the TRE workspace and can be accessed by the requestor from within the workspace.
  6. Rejection In-progress: The Airlock request has been rejected, however data movement is still ongoing.
  7. Rejected: The Airlock request has been rejected. The data was rejected manually by the Airlock Manager.
  8. Cancelled: The Airlock request was manually cancelled by the requestor, a Workspace Owner, or a TRE administrator. Cancellation is allowed while the request is not actively moving data — that is, in Draft or In-Review state.
  9. Blocking In-progress: The Airlock request has been blocked, however data movement is still ongoing.
  10. Blocked By Scan: The Airlock request has been blocked. The security analysis found issues in the submitted data and consequently quarantined the data.
graph TD
  A[Researcher wants to export data from TRE Workspace] -->|Request created| B[Request in state Draft]
  B-->|Researcher gets link to storage container and uploads data| B
  B-->|Request submitted| C[Submitted]
  C--> D{Security issues found?}
  D-->|Yes| E[Blocking In-progress]
  D-->|No| G[In-Review]
  E:::temporary--> F((Blocked By Scan))
  G-->|Human Review| H{Is data appropriate to export?}
  H-->|Approve| I[Approval In-progress]
  H-->|Reject| J[Rejection In-progress]
  I:::temporary-->K((Approved))
  J:::temporary-->L((Rejected))
  B-->|Request Canceled| X((Canceled))
  G-->|Request Canceled| X
  H-->|Request Canceled| X
  classDef temporary stroke-dasharray: 5 5

Airlock state flow diagram for an export request. Import follows the same flow.

When an airlock process is created the initial state is Draft and the airlock processor creates a storage container with the appropriate stage metadata. The user receives a link to this container (URL + SAS token) that they can use to upload data.

For import, the container is created in core storage (stalairlock) with metadata stage=import-external. For export, the container is created in global workspace storage (stalairlockg) with metadata stage=export-internal, accessible only from within the workspace via private endpoint.

The user uploads a file using any tool of their preference: Azure Storage Explorer or AzCopy.

The user submits the request (TRE API call). On submission the processor seals the draft: it copies the draft container's file into a new, immutable container named after the request ID (with stage=import-in-progress / export-in-progress) and deletes the writable draft container (<request-id>-draft). This structurally revokes the researcher's upload SAS, so the data under review can no longer be modified. The airlock request is now in state Submitted.

If enabled, malware scanning is started using Microsoft Defender for Storage (see Microsoft Defender for Storage documentation). If security flaws are found, the container metadata is updated to blocked status and the request is finalised with state Blocked By Scan. If no issues are found, the metadata is updated to in-review status and the request state becomes In-Review. A notification is sent to the Airlock Manager.

The Security Scanning can be disabled, changing the request state from Submitted straight to In-Review.

The Airlock Manager manually reviews the data using tools available in the TRE workspace. Once review is completed, the Airlock Manager approves or rejects the request through a TRE API call. For approval, data is copied to the final destination. For rejection, only metadata is updated.

Data Movement

For any airlock process, there is data movement either into a TRE workspace (import) or from a TRE workspace (export). The data movement guarantees that data is automatically verified for security flaws and manually reviewed before being placed inside or taken outside the TRE Workspace.

Metadata-based stage management means most transitions are near-instantaneous metadata updates. Data is physically copied in only two places:

  • On submit (seal): the writable draft container (<request-id>-draft) is copied into a new immutable <request-id> container on the same account and the draft is deleted. This revokes the researcher's upload SAS structurally, so content under review cannot be altered.
  • On approval (boundary crossing): Core storage → Workspace storage for imports, Workspace storage → Core storage for exports (one server-side copy).

All other transitions — submitted→in-review, in-review→rejected/blocked — update metadata only, with no data movement.

The cross-account approval copy is driven by the StatusChangedQueueTrigger: it creates the destination container (with stage metadata) and starts a server-side copy with start_copy_from_url, waiting for it to finish (and aborting, so the Service Bus message retries, if it exceeds the copy timeout). Completion is signalled by a BlobCreated Event Grid event on the destination account: the BlobCreatedTrigger maps the destination approval stage to the completed step (V2_STAGE_COMPLETION_MAP) and emits the StepResult that advances the request to Approved, plus a data-deletion event for the source container.

Import Data Flow

graph LR
    subgraph External["External"]
        data("fa:fa-file Data to import")
    end

    subgraph CoreStorage["Core: stalairlock"]
        A{{"stage: import-external"}}
        B{{"stage: import-in-progress"}}
        D{{"stage: import-blocked"}}
        C{{"stage: import-rejected"}}
    end

    subgraph WorkspaceStorage["Workspace: stalairlockg"]
        E{{"stage: import-approved"}}
    end

    data -- "Upload via SAS" --> A
    A == "Submitted - seal (copy + delete draft)" ==> B
    B -. "Threat found - metadata only" .-> D
    B -. "Clean scan - metadata only" .-> review{"Review"}
    review -. "Rejected - metadata only" .-> C
    review == "Approved - cross-account copy" ==> E

    style External fill:#444,stroke:#333,color:#fff
    style CoreStorage fill:#2c5f9e,stroke:#1a3d6d,color:#fff
    style WorkspaceStorage fill:#8b5c00,stroke:#5c3d00,color:#fff
    style data fill:#0078d4,stroke:#005a9e,color:#fff
    style A fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style B fill:#4a6fa5,stroke:#2c5f9e,color:#fff
    style C fill:#b85450,stroke:#8b3e3b,color:#fff
    style D fill:#b85450,stroke:#8b3e3b,color:#fff
    style E fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style review fill:#6b5900,stroke:#4a3d00,color:#fff

Import data flow. Dashed lines = metadata-only transitions. Thick lines = data copies: the submit seal (same-account, deletes the writable draft) and the cross-account copy on approval. Hexagons = container metadata stages.

Export Data Flow

graph LR
    subgraph Workspace["TRE Workspace"]
        data("fa:fa-file Data to export")
    end

    subgraph WorkspaceStorage["Workspace: stalairlockg"]
        A{{"stage: export-internal"}}
        B{{"stage: export-in-progress"}}
        D{{"stage: export-blocked"}}
        C{{"stage: export-rejected"}}
    end

    subgraph CoreStorage["Core: stalairlock"]
        E{{"stage: export-approved"}}
    end

    data -- "Upload via PE" --> A
    A == "Submitted - seal (copy + delete draft)" ==> B
    B -. "Threat found - metadata only" .-> D
    B -. "Clean scan - metadata only" .-> review{"Review"}
    review -. "Rejected - metadata only" .-> C
    review == "Approved - cross-account copy" ==> E

    style Workspace fill:#1a5c1a,stroke:#0d330d,color:#fff
    style WorkspaceStorage fill:#8b5c00,stroke:#5c3d00,color:#fff
    style CoreStorage fill:#2c5f9e,stroke:#1a3d6d,color:#fff
    style data fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style A fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style B fill:#4a6fa5,stroke:#2c5f9e,color:#fff
    style C fill:#b85450,stroke:#8b3e3b,color:#fff
    style D fill:#b85450,stroke:#8b3e3b,color:#fff
    style E fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style review fill:#6b5900,stroke:#4a3d00,color:#fff

Export data flow. Dashed lines = metadata-only transitions. Thick lines = data copies: the submit seal (same-account, deletes the writable draft) and the cross-account copy on approval. Hexagons = container metadata stages.

Security Scan

Data in an airlock process is submitted to a security scan. If the scan identifies issues, the container metadata is updated to blocked status and a report is added to the process metadata. Both the requestor and Workspace Owner are notified. For a successful security scan, data remains accessible to the Workspace Owner for review.

  • The security scan is optional, behind a feature flag enabled by a script.
  • The outcome of the security scan will be either the in-progress metadata status or blocked metadata status.
  • An airlock process guarantees that the content being imported/exported is secure.

Access Control

The airlock uses Azure Attribute-Based Access Control (ABAC) to restrict access at the storage account level. This ensures that identities can only access containers matching specific stage metadata values.

graph LR
    api["fa:fa-key TRE API"]
    proc["fa:fa-cog Airlock Processor"]
    wspe["fa:fa-lock Workspace PE"]

    subgraph CoreStorage["Core: stalairlock"]
        cs_ie{{"stage: import-external"}}
        cs_eapp{{"stage: export-approved"}}
        cs_iip{{"stage: import-in-progress"}}
        cs_irej{{"stage: import-rejected"}}
        cs_iblk{{"stage: import-blocked"}}
    end

    subgraph WorkspaceStorage["Workspace: stalairlockg"]
        ws_iapp{{"stage: import-approved"}}
        ws_eint{{"stage: export-internal"}}
        ws_eip{{"stage: export-in-progress"}}
        ws_erej{{"stage: export-rejected"}}
        ws_eblk{{"stage: export-blocked"}}
    end

    api -- "ABAC: import-external OR export-approved" --> CoreStorage
    proc == "Unrestricted access" ==> CoreStorage
    proc == "Unrestricted access" ==> WorkspaceStorage
    wspe -- "ABAC: workspace_id + stage" --> WorkspaceStorage

    style api fill:#b85450,stroke:#8b3e3b,color:#fff
    style proc fill:#cc7000,stroke:#995300,color:#fff
    style wspe fill:#6a3d9a,stroke:#4a2b6d,color:#fff
    style CoreStorage fill:#2c5f9e,stroke:#1a3d6d,color:#fff
    style WorkspaceStorage fill:#8b5c00,stroke:#5c3d00,color:#fff
    style cs_ie fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style cs_eapp fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style cs_iip fill:#4a6fa5,stroke:#2c5f9e,color:#fff
    style cs_irej fill:#8b3e3b,stroke:#6b2e2b,color:#fff
    style cs_iblk fill:#8b3e3b,stroke:#6b2e2b,color:#fff
    style ws_iapp fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style ws_eint fill:#2d8a2d,stroke:#1a5c1a,color:#fff
    style ws_eip fill:#4a6fa5,stroke:#2c5f9e,color:#fff
    style ws_erej fill:#8b3e3b,stroke:#6b2e2b,color:#fff
    style ws_eblk fill:#8b3e3b,stroke:#6b2e2b,color:#fff

ABAC access control. The API can only access public stages (green). The Processor has full access. Workspace PEs are scoped by workspace_id.

Identity access summary:

Identity Core Storage Workspace Storage ABAC Condition
TRE API Storage Blob Data Contributor — Only import-external and export-approved stages
Airlock Processor Storage Blob Data Contributor Storage Blob Data Contributor None (unrestricted)
Workspace PE — Storage Blob Data Contributor workspace_id must match + stage restrictions

Network access:

  • Core storage allows public access for import-external and export-approved stages via SAS tokens (through the App Gateway).
  • Global workspace storage uses Deny as the default network action. Access is only possible via per-workspace private endpoints from within the workspace VNet.
  • The airlock processor has a private endpoint on the airlock storage subnet for internal processing on both accounts.
  • User Delegation SAS tokens inherit the ABAC restrictions of the signing identity, so even a valid SAS token cannot access stages outside the identity's ABAC scope.

Container Metadata Stages

Each container has a stage metadata key that tracks the current stage of the airlock request:

Core Storage (stalairlock):

Stage Description Access
import-external Initial upload location for imports Public via SAS
import-in-progress After submission, during review Processor only
import-rejected Import rejected by reviewer Processor only
import-blocked Import blocked by security scan Processor only
export-approved Final location for approved exports Public via SAS

Global Workspace Storage (stalairlockg):

Stage Description Access
import-approved Final location for approved imports Workspace PE
export-internal Initial upload location for exports Workspace PE
export-in-progress After submission, during review Processor only
export-rejected Export rejected by reviewer Processor only
export-blocked Export blocked by security scan Processor only

Approval Mechanism

The approval mechanism is bundled with any airlock process, providing a specific way to approve or reject the data. Airlock Managers can explicitly approve/reject the process after reviewing the data using tools available in a review TRE Workspace.

The only goal of the approval mechanism is to provide a cycle of revision, approval or rejection while tracking the decision.

  • It is envisioned that this mechanism will be more flexible and extensible.
  • The Airlock Manager is a role defined at the workspace instance level and assigned to identities.

Notifications

Throughout the airlock process, the notification mechanism notifies the relevant people. Both the requestor (TRE User/Researcher) and the Workspace Owner are notified by email of relevant process events.

Whenever the airlock process changes to a state of Draft, Submitted, Approved, Rejected, Approval In-progress, Rejection In-progress, Blocked By Scan or Cancelled, the process requestor gets notified. When the state changes to In-Review, the Workspace Owner (Airlock Manager) gets notified.

  • The notification mechanism is data-driven, allowing an organisation to extend the notifications behaviour. The mechanism is exemplified with a Logic App determining the notifications logic.
  • Notifications work with all TRE users being Microsoft Entra ID users (guests or not), with email defined — if not, notifications will not be sent.

API Endpoints

The TRE API exposes the following airlock endpoints:

Method Endpoint Description
POST /api/workspaces/{workspace_id}/requests Create an Airlock request (in Draft)
GET /api/workspaces/{workspace_id}/requests/{airlock_request_id}/link Get the url and token to access an Airlock Request
POST /api/workspaces/{workspace_id}/requests/{airlock_request_id}/submit Submit an Airlock request
POST /api/workspaces/{workspace_id}/requests/{airlock_request_id}/review Review an Airlock request
POST /api/workspaces/{workspace_id}/requests/{airlock_request_id}/cancel Cancel an Airlock request

Airlock Processor

The Airlock Processor is a set of Azure Functions that handle the events created throughout the airlock process:

  • StatusChangedQueueTrigger — Consumes status change events from the Service Bus queue and orchestrates container creation, sealing, metadata updates, and cross-account data copies. On submit it seals the draft (copies <request-id>-draft into an immutable <request-id> container and deletes the draft). For same-account transitions (in-review→rejected/blocked) it updates container metadata directly. For the cross-account approval copy it creates the destination container and runs a server-side copy, waiting for it to finish (aborting and letting the message retry on timeout).
  • BlobCreatedTrigger — Fires when a blob appears in a storage account (via EventGrid → Service Bus). For the v1 per-stage flow it signals each stage's copy completion. For v2 it handles only the cross-account approval copies (V2_STAGE_COMPLETION_MAP): when the copied blob lands in the approved container it emits the StepResult advancing the request to Approved and a data-deletion event for the source. Other v2 completions are emitted directly by the StatusChangedQueueTrigger.
  • ScanResultTrigger — Consumes malware scan results from Microsoft Defender for Storage. If threats are found, emits a StepResult to block the request. If clean, emits a StepResult to advance to in-review.
  • DataDeletionTrigger — Cleans up source containers after data has been copied to the destination.

This event-driven design keeps stage transitions responsive: same-account transitions are metadata-only (except the submit seal, a fast same-account copy that deletes the draft), while the less frequent cross-account approval copy is confirmed via a BlobCreated event before the request advances to Approved.

Airlock Flow

The following sequence diagram details the airlock feature and its event-driven behaviour:

sequenceDiagram
    participant R as Researcher
    participant API as TRE API
    participant CS as Core Storage<br/>(stalairlock)
    participant WS as Workspace Storage<br/>(stalairlockg)
    participant AP as Airlock Processor
    participant EG as Event Grid
    participant SB as Service Bus
    participant DB as Cosmos DB

    Note over R,DB: Creating a Draft Request (Import Example)
    R->>API: POST /requests (type=import)
    API->>DB: Save request (status: draft)
    API->>EG: StatusChangedEvent(draft)
    EG->>SB: Queue status change
    SB->>AP: Consume event
    AP->>CS: Create container with metadata stage=import-external
    API-->>R: OK + request details

    Note over R,DB: Getting Upload Link
    R->>API: GET /requests/{id}/link
    API->>CS: Generate User Delegation SAS (ABAC: import-external)
    API-->>R: SAS URL for container

    Note over R,DB: Uploading File
    R->>CS: Upload file via SAS token

    Note over R,DB: Submitting Request
    R->>API: POST /requests/{id}/submit
    API->>DB: Update status → submitted
    API->>EG: StatusChangedEvent(submitted)
    EG->>SB: Queue status change
    SB->>AP: StatusChangedQueueTrigger
    AP->>CS: Update metadata → import-in-progress

    Note over R,DB: Security Scan
    alt Malware Scanning Enabled
        CS-->>EG: Defender scan result
        EG->>SB: Queue scan result
        SB->>AP: ScanResultTrigger
        alt Threat Found
            AP->>EG: StepResult(blocking_in_progress)
            Note over AP,CS: StatusChangedQueueTrigger updates metadata → import-blocked
            AP->>EG: StepResult(blocked)
        else No Threat
            AP->>EG: StepResult(in-review)
        end
    else Malware Scanning Disabled
        AP->>EG: StepResult(submitted → in-review)
    end
    AP->>DB: Update status → in-review
    AP->>EG: NotificationEvent (to reviewer)

    Note over R,DB: Approval (Synchronous Copy)
    R->>API: POST /requests/{id}/review (approve)
    API->>DB: Update status → approval_in_progress
    API->>EG: StatusChangedEvent(approval_in_progress)
    EG->>SB: Queue status change
    SB->>AP: StatusChangedQueueTrigger
    AP->>WS: Create container with metadata stage=import-approved
    AP->>WS: Server-side copy from Core → Workspace storage
    Note over AP,WS: Trigger waits (polls) for copy to complete
    AP->>EG: StepResult(approved)
    AP->>DB: Update status → approved
    AP->>EG: DataDeletion event (source container)
    AP->>EG: NotificationEvent (to researcher)

Legacy Airlock

For details on the legacy airlock architecture (per-stage storage accounts) and migration guidance, see Legacy Airlock Architecture.

Configuration

Core Settings (config.yaml)

The following settings in config.yaml control the airlock infrastructure at the TRE core level:

# config.yaml
tre_id: mytre

# Set to false to remove legacy per-stage storage accounts.
# Default: true (keeps legacy accounts for backward compatibility)
enable_legacy_airlock: false
Setting Type Default Description
enable_legacy_airlock bool true When true, deploys legacy per-stage storage accounts alongside the consolidated accounts for backward compatibility. When false, only the consolidated accounts (stalairlock, stalairlockg) are deployed. See Legacy Airlock Architecture for details.

The consolidated storage accounts (stalairlock{tre_id} and stalairlockg{tre_id}) are always provisioned regardless of this setting.

Workspace Settings

The airlock is enabled per workspace via the following properties:

Property Type Default Values Description
enable_airlock bool false true / false Enables or disables the airlock feature for the workspace
airlock_version int 2 1 or 2 2 = Consolidated metadata-based storage (recommended), 1 = Legacy per-stage storage accounts

The airlock_version property only appears when enable_airlock is set to true.

Enabling airlock via the API:

PATCH /api/workspaces/{workspace_id}
{
  "properties": {
    "enable_airlock": true,
    "airlock_version": 2
  }
}

Enabling airlock via the UI:

When creating or updating a workspace, the airlock version is available as a dropdown under the airlock configuration section.

What Happens at Each Level

config.yaml                          Workspace Properties
┌─────────────────────────┐          ┌─────────────────────────────┐
│ enable_legacy_airlock:  │          │ enable_airlock: true        │
│   false → v2 infra only│          │ airlock_version: 2 → v2 TF │
└─────────────────────────┘          └─────────────────────────────┘
        Core Terraform                     Workspace Terraform
  • Core level (enable_legacy_airlock): Controls whether legacy per-stage storage accounts are also deployed (for backward compatibility only)
  • Workspace level (airlock_version): Controls which workspace Terraform module runs — airlock_v2/ for consolidated storage with ABAC

Cross-Workspace Isolation

A common question: if all workspaces share the same storage account (stalairlockg{tre_id}), what prevents Workspace A from accessing Workspace B's data?

The answer is three layers of isolation:

1. ABAC Conditions (Azure Attribute-Based Access Control)

Each workspace deployment creates a role assignment on the global workspace storage account with an ABAC condition that requires all three of the following to be true for blob operations:

  • The request must come through that workspace's specific private endpoint
  • The container's workspace_id metadata must match that workspace's ID
  • The container's stage metadata must be one of the allowed stages (import-approved, export-internal, export-in-progress)
ABAC condition (per workspace):
  @Environment[Microsoft.Network/privateEndpoints]
    == '/subscriptions/.../pe-sa-airlock-ws-global-{workspace_short_id}'
  AND
  @Resource[...containers/metadata:workspace_id]
    == '{workspace_id}'
  AND
  @Resource[...containers/metadata:stage]
    IN ('import-approved', 'export-internal', 'export-in-progress')

This means even if Workspace A somehow obtained a SAS token referencing Workspace B's container, the ABAC condition would deny the operation because the private endpoint wouldn't match.

2. Network Isolation (Private Endpoints)

Each workspace creates its own private endpoint to the global workspace storage account, connected to the workspace's VNet. The ABAC condition references this specific private endpoint ID, so requests from a different workspace's PE are rejected.

3. Container Metadata

The airlock processor stamps every container with workspace_id metadata at creation time. This metadata is immutable in practice (only the processor identity can modify it, and researcher identities have no direct access to the storage account).

graph TB
    subgraph WS_A["Workspace A"]
        pe_a["PE: pe-sa-airlock-ws-global-ab12"]
    end

    subgraph WS_B["Workspace B"]
        pe_b["PE: pe-sa-airlock-ws-global-cd34"]
    end

    subgraph GlobalStorage["Workspace: stalairlockg"]
        c1("req-001<br/>workspace_id: ws-ab12<br/>stage: import-approved")
        c2("req-002<br/>workspace_id: ws-cd34<br/>stage: export-internal")
    end

    pe_a -- "ABAC: ws-ab12 + PE match" --> c1
    pe_a -. "DENIED by ABAC" .-> c2
    pe_b -. "DENIED by ABAC" .-> c1
    pe_b -- "ABAC: ws-cd34 + PE match" --> c2

    style WS_A fill:#2c5f9e,stroke:#1a3d6d,color:#fff
    style WS_B fill:#8b5c00,stroke:#5c3d00,color:#fff
    style GlobalStorage fill:#444,stroke:#333,color:#fff
    style pe_a fill:#4a6fa5,stroke:#2c5f9e,color:#fff
    style pe_b fill:#cc7000,stroke:#995300,color:#fff
    style c1 fill:#4a6fa5,stroke:#2c5f9e,color:#fff
    style c2 fill:#cc7000,stroke:#995300,color:#fff

Cross-workspace isolation. Each workspace can only access containers matching its own workspace_id, through its own private endpoint. ABAC enforces both conditions at the Azure RBAC layer.