Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
260a1be
Redesign airlock storage account architecture
marrobi Aug 17, 2026
63e8ab0
Merge branch 'main' into copilot/copilotredesign-airlock-storage-acco…
marrobi Aug 17, 2026
d64ec28
Fix airlock v2 defaults, scan verdict persistence, and processor edge…
marrobi Aug 18, 2026
53118a7
Airlock v2: allow API-issued SAS to read import-in-progress for revie…
marrobi Aug 18, 2026
bf205c1
Consolidate airlock v2 changelog into a single enhancement with doc l…
marrobi Aug 18, 2026
0f477db
Store early airlock scan verdict on request instead of container meta…
marrobi Aug 18, 2026
6e7909e
Address PR #5048 review: per-workspace airlock DNS, request migration…
marrobi Aug 18, 2026
963df68
Restore per-workspace airlock SAS signer + version-change guard from …
marrobi Aug 18, 2026
560175f
Finish legacy-airlock guards and address review comments (#5048)
marrobi Aug 18, 2026
19609d4
Version-aware import-review: restore v1 (stalimip) connectivity behin…
marrobi Aug 18, 2026
8d5c8f7
Backfill airlock_version in the startup guard before the block check …
marrobi Aug 18, 2026
d1751f5
Address review: v2 submit file handling, version-change/patch guards,…
marrobi Aug 18, 2026
cf281ac
Address PR #5048 review #4963131058: guard/blob-created/review-worksp…
marrobi Aug 18, 2026
6fc28b6
Fix airlock review #4963547500 + live-found isPrivateLink ABAC bug
marrobi Aug 18, 2026
7a0eb3c
Address suppressed review #4964016031 (K1/K2/K3)
marrobi Aug 18, 2026
8ac8674
Make airlock CHANGELOG entry concise
marrobi Aug 18, 2026
6f9fee5
Add breaking-change note to set enable_legacy_airlock explicitly
marrobi Aug 18, 2026
9c38eba
update changelog
marrobi Aug 18, 2026
a61df5f
Create the airlock SAS signer for every workspace, including manual auth
marrobi Aug 18, 2026
9db39d4
Revert unintended terraform lock file provider bump
marrobi Aug 18, 2026
db13c7d
Correct stale airlock docs/comments after manual-auth v2 support
marrobi Aug 18, 2026
c87fe95
Make the airlock container stage update atomic and stop dead-letterin…
marrobi Aug 18, 2026
413d32d
Delete a workspace's airlock containers when the workspace is deleted
marrobi Aug 18, 2026
8fa83a1
Give core its own DNS zone for the shared global airlock account
marrobi Aug 18, 2026
a304c4f
Remove the App Gateway airlock storage proxy
marrobi Aug 18, 2026
fdc7030
Address review 4965636608: legacy-safe version defaults, retry fields…
marrobi Aug 18, 2026
61c6dc6
Check an airlock request belongs to the workspace in the path
marrobi Aug 18, 2026
0c9651b
Address review feedback: sovereign-cloud signer issuer, airlock_versi…
marrobi Aug 18, 2026
b331528
Make submission file validation authoritative over an early scan verdict
marrobi Aug 19, 2026
47e76ab
Wire ENABLE_LEGACY_AIRLOCK through the core deployment workflow
marrobi Aug 19, 2026
af61381
Refactor airlock logic and documentation for clarity and accuracy
marrobi Aug 19, 2026
53d07d9
Record the airlock scan verdict as a fact and decide the submission o…
marrobi Aug 19, 2026
ac37848
Report submission files only after the container stage is locked
marrobi Aug 19, 2026
b1de06c
Remove airlock_version backfills, the unused import-review identity a…
marrobi Aug 19, 2026
0a9db05
Remove unreachable legacy routing, unused helpers and the dead in-fli…
marrobi Aug 19, 2026
9c2e5f0
Restrict researcher access to export data once it leaves draft
marrobi Aug 19, 2026
0327329
Seal submissions by copying out of a draft container and deleting it
marrobi Aug 19, 2026
b869f0c
Preserve v1 terraform state addresses when gating legacy airlock behi…
marrobi Aug 19, 2026
ea6122c
Enumerate the draft container when validating submitted files
marrobi Aug 19, 2026
57e7613
Add e2e coverage for the draft container seal, file count validation …
marrobi Aug 19, 2026
eb18ef8
Add airlock e2e selector and /test-airlock PR command
marrobi Aug 19, 2026
c37d4f2
Assert in-progress airlock data is refused from the public internet
marrobi Aug 19, 2026
66a9329
Fix late scan verdicts stranding requests and sovereign cloud token e…
marrobi Aug 19, 2026
c9dbcdd
Only block airlock version changes for in-flight requests
marrobi Aug 19, 2026
9b8f709
Gate review on the submitted copy's scan verdict and acknowledge dupl…
marrobi Aug 19, 2026
b8d9369
Fail closed on malformed verdicts and unknown request types, remove d…
marrobi Aug 19, 2026
690162f
Default new workspaces to airlock v2 and stamp pre-v2 workspaces via …
marrobi Aug 20, 2026
a58ced4
Persist late file enumeration results instead of discarding them
marrobi Aug 20, 2026
d9a17db
Cover the malware-scanning-disabled submit path
marrobi Aug 20, 2026
55c10b1
Block airlock version downgrade and abort a copy left pending at timeout
marrobi Aug 20, 2026
ffd0b0b
Fix markdownlint line-length and code-block-style in airlock-legacy docs
marrobi Aug 20, 2026
3633fd2
Correct docs: upgrading a workspace to v2 deletes completed v1 reques…
marrobi Aug 20, 2026
303f85f
Acknowledge file-only step results as facts without republishing a st…
marrobi Aug 20, 2026
aeb69ac
Retry transient processor errors and remove dead terminal-stage skip …
marrobi Aug 20, 2026
f943efb
Tag the airlock workspace-global DNS A record with tre_id
marrobi Aug 20, 2026
21180ea
Include cancelled requests in workspace deletion airlock cleanup
marrobi Aug 20, 2026
2c0ee29
Allow cancelling a Submitted airlock request
marrobi Aug 20, 2026
b6c539b
Reject airlock request creation on v1 workspace when legacy airlock d…
marrobi Aug 20, 2026
509dbd0
Version base workspace as minor 2.11.0 so v1 workspaces can upgrade i…
marrobi Aug 20, 2026
150ff05
Guard blob/event parsing and fix malformed scan-result log statement
marrobi Aug 20, 2026
17749fe
Sync airlock docs with current code: submit seal/immutability, cancel…
marrobi Aug 20, 2026
ee7d3bd
Remove spurious v1 airlock moved blocks for already-counted resources
marrobi Aug 20, 2026
76e6233
Revert Submitted->Cancelled (async ordering race); version import-rev…
marrobi Aug 20, 2026
44f8921
Docs: revert cancel-from-submitted to match reverted transition
marrobi Aug 20, 2026
7928af7
Wrap long markdown lines to satisfy MD013 (400 char limit)
marrobi Aug 20, 2026
41f92fc
Derive workspace airlock_version from template; make legacy defaults …
marrobi Aug 20, 2026
252486a
Airlock robustness + e2e SAS log fixes from PR review
marrobi Aug 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,4 @@ validation.txt

/index.html
.DS_Store
*_old.tf
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ENHANCEMENTS:
* Add data science tooling (Azure CLI, VS Code, Storage Explorer, Git, Python/JupyterLab, R/RStudio) to Guacamole Windows VMs via a shared `vm_config.ps1` bootstrap pulled through Nexus. Existing `tre-service-guacamole-windowsvm`, `tre-service-guacamole-import-reviewvm`, and `tre-service-guacamole-export-reviewvm` resources **must not be upgraded** to these new versions — redeploy instead. Upgrade the Nexus shared service to `sonatype-nexus` 3.9.0 before deploying the new Windows VM templates to ensure the required proxy repositories are available. (`tre-service-guacamole-windowsvm` 3.0.0, `tre-service-guacamole-import-reviewvm`/`tre-service-guacamole-export-reviewvm` 2.0.0, `sonatype-nexus` 3.9.0) ([#4981](https://github.com/microsoft/AzureTRE/pull/4981))

ENHANCEMENTS:
* Add per-workspace `airlock_version` property (1=legacy, 2=consolidated) for backwards-compatible airlock storage migration. Add core-level `enable_legacy_airlock` toggle. Remove `USE_METADATA_STAGE_MANAGEMENT` environment variable. ([#4853](https://github.com/microsoft/AzureTRE/pull/4853), [#4358](https://github.com/microsoft/AzureTRE/issues/4358))
* Upgrade FastAPI to 0.139.2, Starlette to 1.3.1, and compatible OpenTelemetry dependencies, with routing updates for newer FastAPI and Pydantic v2 compatibility.
* Enable graceful upgrading of the Nexus shared service: modified or added repository configs and the container image (now `3.94.0`) are applied to the existing VM on upgrade without recreating it. Removed the non-functional `snapcraft` proxy (dead remote URL) and skip repositories left in a failed state so they don't block upgrades. (`sonatype-nexus` 3.10.0) ([#2721](https://github.com/microsoft/AzureTRE/issues/2721))
* Specify default_outbound_access_enabled = false setting for all subnets ([#4757](https://github.com/microsoft/AzureTRE/pull/4757))
Expand Down
61 changes: 61 additions & 0 deletions airlock_processor/BlobCreatedTrigger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
from shared_code.blob_operations import get_blob_info_from_topic_and_subject, get_blob_client_from_blob_info


# Mapping from v2 container metadata stage to (completed_step, new_status)
V2_STAGE_COMPLETION_MAP = {
constants.STAGE_IMPORT_APPROVED: (constants.STAGE_APPROVAL_INPROGRESS, constants.STAGE_APPROVED),
constants.STAGE_IMPORT_REJECTED: (constants.STAGE_REJECTION_INPROGRESS, constants.STAGE_REJECTED),
constants.STAGE_IMPORT_BLOCKED: (constants.STAGE_BLOCKING_INPROGRESS, constants.STAGE_BLOCKED_BY_SCAN),
constants.STAGE_EXPORT_APPROVED: (constants.STAGE_APPROVAL_INPROGRESS, constants.STAGE_APPROVED),
constants.STAGE_EXPORT_REJECTED: (constants.STAGE_REJECTION_INPROGRESS, constants.STAGE_REJECTED),
constants.STAGE_EXPORT_BLOCKED: (constants.STAGE_BLOCKING_INPROGRESS, constants.STAGE_BLOCKED_BY_SCAN),
}


def main(msg: func.ServiceBusMessage,
stepResultEvent: func.Out[func.EventGridOutputEvent],
dataDeletionEvent: func.Out[func.EventGridOutputEvent]):
Expand All @@ -23,6 +34,12 @@ def main(msg: func.ServiceBusMessage,
topic = json_body["topic"]
request_id = re.search(r'/blobServices/default/containers/(.*?)/blobs', json_body["subject"]).group(1)

# Check if this event is from a v2 consolidated storage account
if constants.STORAGE_ACCOUNT_NAME_AIRLOCK_CORE in topic or constants.STORAGE_ACCOUNT_NAME_AIRLOCK_WORKSPACE_GLOBAL in topic:
_handle_v2_blob_created(json_body, topic, request_id, stepResultEvent, dataDeletionEvent)
return

# Legacy v1 handling below
# message originated from in-progress blob creation
if constants.STORAGE_ACCOUNT_NAME_IMPORT_INPROGRESS in topic or constants.STORAGE_ACCOUNT_NAME_EXPORT_INPROGRESS in topic:
try:
Expand Down Expand Up @@ -55,6 +72,9 @@ def main(msg: func.ServiceBusMessage,
elif constants.STORAGE_ACCOUNT_NAME_IMPORT_BLOCKED in topic or constants.STORAGE_ACCOUNT_NAME_EXPORT_BLOCKED in topic:
completed_step = constants.STAGE_BLOCKING_INPROGRESS
new_status = constants.STAGE_BLOCKED_BY_SCAN
else:
logging.warning(f"Unknown storage account in topic: {topic}")
return

# reply with a step completed event
stepResultEvent.set(
Expand Down Expand Up @@ -88,3 +108,44 @@ def send_delete_event(dataDeletionEvent: func.Out[func.EventGridOutputEvent], js
data_version=constants.DATA_DELETION_EVENT_DATA_VERSION
)
)


def _handle_v2_blob_created(json_body, topic, request_id, stepResultEvent, dataDeletionEvent):
"""Handle BlobCreated events from v2 consolidated storage accounts.

In v2, cross-account copies (e.g., import approval: core → workspace-global)
fire BlobCreated events. Container metadata determines the stage and appropriate
step result, matching the v1 pattern where BlobCreatedTrigger signals copy completion.
"""
storage_account_name, _, _ = get_blob_info_from_topic_and_subject(
topic=json_body["topic"], subject=json_body["subject"])

from shared_code.blob_operations_metadata import get_container_metadata
try:
metadata = get_container_metadata(storage_account_name, request_id)
except Exception:
logging.warning(f"Could not read container metadata for request {request_id} on {storage_account_name}, skipping")
return
Comment thread
marrobi marked this conversation as resolved.
Outdated

stage = metadata.get('stage', '')
logging.info(f"V2 BlobCreated for request {request_id}: stage={stage}, account={storage_account_name}")

if stage in V2_STAGE_COMPLETION_MAP:
completed_step, new_status = V2_STAGE_COMPLETION_MAP[stage]
logging.info(f"V2 copy completed for request {request_id}: {completed_step} -> {new_status}")

stepResultEvent.set(
func.EventGridOutputEvent(
id=str(uuid.uuid4()),
data={"completed_step": completed_step, "new_status": new_status, "request_id": request_id},
subject=request_id,
event_type="Airlock.StepResult",
event_time=datetime.datetime.now(datetime.UTC),
data_version=constants.STEP_RESULT_EVENT_DATA_VERSION))

# Send delete event for the source container (same as v1)
send_delete_event(dataDeletionEvent, json_body, request_id)
else:
# Non-terminal stages (e.g., import-external from user upload, export-internal)
# are not copy completions — ignore them
logging.info(f"V2 BlobCreated for non-terminal stage '{stage}' on request {request_id}, no action needed")
113 changes: 99 additions & 14 deletions airlock_processor/StatusChangedQueueTrigger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from exceptions import NoFilesInRequestException, TooManyFilesInRequestException

from shared_code import blob_operations, constants
from shared_code import blob_operations, constants, airlock_storage_helper, parsers
from pydantic import BaseModel, TypeAdapter


Expand All @@ -19,6 +19,8 @@ class RequestProperties(BaseModel):
previous_status: Optional[str] = None
type: str
workspace_id: str
review_workspace_id: Optional[str] = None
airlock_version: int = 1


class ContainersCopyMetadata:
Expand Down Expand Up @@ -56,13 +58,25 @@ def handle_status_changed(request_properties: RequestProperties, stepResultEvent

logging.info('Processing request with id %s. new status is "%s", type is "%s"', req_id, new_status, request_type)

# Check if using metadata-based stage management (v2) or legacy per-stage accounts (v1)
use_metadata = request_properties.airlock_version >= 2

if new_status == constants.STAGE_DRAFT:
account_name = get_storage_account(status=constants.STAGE_DRAFT, request_type=request_type, short_workspace_id=ws_id)
blob_operations.create_container(account_name, req_id)
if use_metadata:
from shared_code.blob_operations_metadata import create_container_with_metadata
account_name = airlock_storage_helper.get_storage_account_name_for_request(request_type, new_status, ws_id, airlock_version=request_properties.airlock_version)
stage = airlock_storage_helper.get_stage_from_status(request_type, new_status)
create_container_with_metadata(account_name, req_id, stage, workspace_id=ws_id, request_type=request_type)
else:
account_name = get_storage_account(status=constants.STAGE_DRAFT, request_type=request_type, short_workspace_id=ws_id)
blob_operations.create_container(account_name, req_id)
return

if new_status == constants.STAGE_CANCELLED:
storage_account_name = get_storage_account(previous_status, request_type, ws_id)
if use_metadata:
storage_account_name = airlock_storage_helper.get_storage_account_name_for_request(request_type, previous_status, ws_id, airlock_version=request_properties.airlock_version)
else:
storage_account_name = get_storage_account(previous_status, request_type, ws_id)
container_to_delete_url = blob_operations.get_blob_url(account_name=storage_account_name, container_name=req_id)
set_output_event_to_trigger_container_deletion(dataDeletionEvent, request_properties, container_url=container_to_delete_url)
return
Expand All @@ -71,11 +85,74 @@ def handle_status_changed(request_properties: RequestProperties, stepResultEvent
set_output_event_to_report_request_files(stepResultEvent, request_properties, request_files)

if (is_require_data_copy(new_status)):
logging.info('Request with id %s. requires data copy between storage accounts', req_id)
containers_metadata = get_source_dest_for_copy(new_status=new_status, previous_status=previous_status, request_type=request_type, short_workspace_id=ws_id)
blob_operations.create_container(containers_metadata.dest_account_name, req_id)
blob_operations.copy_data(containers_metadata.source_account_name,
containers_metadata.dest_account_name, req_id)
if use_metadata:
# Metadata mode: Update container stage instead of copying
from shared_code.blob_operations_metadata import update_container_stage, create_container_with_metadata

# For import submit, use review_workspace_id so data goes to review workspace storage
effective_ws_id = ws_id
if new_status == constants.STAGE_SUBMITTED and request_type.lower() == constants.IMPORT_TYPE and request_properties.review_workspace_id:
effective_ws_id = request_properties.review_workspace_id

# Get the storage account (might change from core to workspace or vice versa)
source_account = airlock_storage_helper.get_storage_account_name_for_request(request_type, previous_status, ws_id, airlock_version=request_properties.airlock_version)
dest_account = airlock_storage_helper.get_storage_account_name_for_request(request_type, new_status, effective_ws_id, airlock_version=request_properties.airlock_version)
new_stage = airlock_storage_helper.get_stage_from_status(request_type, new_status)

if source_account == dest_account:
# Same storage account - just update metadata
logging.info(f'Request {req_id}: Updating container stage to {new_stage} (no copy needed)')
update_container_stage(source_account, req_id, new_stage, changed_by='system')

# In v2, same-account transitions don't fire BlobCreated events.
# For SUBMITTED, v1 relies on BlobCreatedTrigger to handle the malware scanning gate
# (skip to in_review when scanning is disabled). We handle this inline for v2.
if new_status == constants.STAGE_SUBMITTED:
try:
enable_malware_scanning = parsers.parse_bool(os.environ["ENABLE_MALWARE_SCANNING"])
except KeyError:
logging.error("environment variable 'ENABLE_MALWARE_SCANNING' does not exist. Cannot continue.")
raise
if not enable_malware_scanning:
logging.info(f'Request {req_id}: Malware scanning disabled, skipping to in_review')
stepResultEvent.set(
func.EventGridOutputEvent(
id=str(uuid.uuid4()),
data={"completed_step": constants.STAGE_SUBMITTED, "new_status": constants.STAGE_IN_REVIEW, "request_id": req_id},
subject=req_id,
event_type="Airlock.StepResult",
event_time=datetime.datetime.now(datetime.UTC),
data_version=constants.STEP_RESULT_EVENT_DATA_VERSION))
else:
logging.info(f'Request {req_id}: Malware scanning enabled, waiting for scan result')
Comment thread
marrobi marked this conversation as resolved.
Outdated
elif new_status in [constants.STAGE_REJECTION_INPROGRESS, constants.STAGE_BLOCKING_INPROGRESS]:
# Terminal transitions: emit StepResult immediately since no BlobCreated event will fire
final_status = constants.STAGE_REJECTED if new_status == constants.STAGE_REJECTION_INPROGRESS else constants.STAGE_BLOCKED_BY_SCAN
logging.info(f'Request {req_id}: Emitting StepResult for terminal transition {new_status} -> {final_status}')
stepResultEvent.set(
func.EventGridOutputEvent(
id=str(uuid.uuid4()),
data={"completed_step": new_status, "new_status": final_status, "request_id": req_id},
subject=req_id,
event_type="Airlock.StepResult",
event_time=datetime.datetime.now(datetime.UTC),
data_version=constants.STEP_RESULT_EVENT_DATA_VERSION))
else:
# Different storage account (e.g., core → workspace on import approval,
# workspace → core on export approval) - need to copy.
# BlobCreatedTrigger will fire when the copy completes and emit the StepResult,
# matching the v1 async pattern for large data transfers.
logging.info(f'Request {req_id}: Copying from {source_account} to {dest_account}')
create_container_with_metadata(dest_account, req_id, new_stage, workspace_id=effective_ws_id, request_type=request_type)
blob_operations.copy_data(source_account, dest_account, req_id)
else:
# Legacy mode: Copy data between storage accounts
logging.info('Request with id %s. requires data copy between storage accounts', req_id)
review_ws_id = request_properties.review_workspace_id
containers_metadata = get_source_dest_for_copy(new_status=new_status, previous_status=previous_status, request_type=request_type, short_workspace_id=ws_id, review_workspace_id=review_ws_id)
blob_operations.create_container(containers_metadata.dest_account_name, req_id)
blob_operations.copy_data(containers_metadata.source_account_name,
containers_metadata.dest_account_name, req_id)
return

# Other statuses which do not require data copy are dismissed as we don't need to do anything...
Expand Down Expand Up @@ -105,7 +182,7 @@ def is_require_data_copy(new_status: str):
return False


def get_source_dest_for_copy(new_status: str, previous_status: str, request_type: str, short_workspace_id: str) -> ContainersCopyMetadata:
def get_source_dest_for_copy(new_status: str, previous_status: str, request_type: str, short_workspace_id: str, review_workspace_id: str = None) -> ContainersCopyMetadata:
# sanity
if is_require_data_copy(new_status) is False:
raise Exception("Given new status is not supported")
Expand All @@ -118,7 +195,7 @@ def get_source_dest_for_copy(new_status: str, previous_status: str, request_type
raise Exception(msg)

source_account_name = get_storage_account(previous_status, request_type, short_workspace_id)
dest_account_name = get_storage_account_destination_for_copy(new_status, request_type, short_workspace_id)
dest_account_name = get_storage_account_destination_for_copy(new_status, request_type, short_workspace_id, review_workspace_id=review_workspace_id)
return ContainersCopyMetadata(source_account_name, dest_account_name)


Expand Down Expand Up @@ -154,12 +231,14 @@ def get_storage_account(status: str, request_type: str, short_workspace_id: str)
raise Exception(error_message)


def get_storage_account_destination_for_copy(new_status: str, request_type: str, short_workspace_id: str) -> str:
def get_storage_account_destination_for_copy(new_status: str, request_type: str, short_workspace_id: str, review_workspace_id: str = None) -> str:
tre_id = _get_tre_id()

if request_type == constants.IMPORT_TYPE:
if new_status == constants.STAGE_SUBMITTED:
return constants.STORAGE_ACCOUNT_NAME_IMPORT_INPROGRESS + tre_id
# Import submit: copy to review workspace storage, or tre_id for legacy compatibility
dest_id = review_workspace_id if review_workspace_id else tre_id
return constants.STORAGE_ACCOUNT_NAME_IMPORT_INPROGRESS + dest_id
Comment thread
marrobi marked this conversation as resolved.
Outdated
elif new_status == constants.STAGE_APPROVAL_INPROGRESS:
return constants.STORAGE_ACCOUNT_NAME_IMPORT_APPROVED + short_workspace_id
elif new_status == constants.STAGE_REJECTION_INPROGRESS:
Expand Down Expand Up @@ -228,7 +307,13 @@ def set_output_event_to_trigger_container_deletion(dataDeletionEvent, request_pr


def get_request_files(request_properties: RequestProperties):
storage_account_name = get_storage_account(request_properties.previous_status, request_properties.type, request_properties.workspace_id)
use_metadata = request_properties.airlock_version >= 2
if use_metadata:
storage_account_name = airlock_storage_helper.get_storage_account_name_for_request(
request_properties.type, request_properties.previous_status, request_properties.workspace_id,
airlock_version=request_properties.airlock_version)
else:
storage_account_name = get_storage_account(request_properties.previous_status, request_properties.type, request_properties.workspace_id)
return blob_operations.get_request_files(account_name=storage_account_name, request_id=request_properties.request_id)


Expand Down
2 changes: 1 addition & 1 deletion airlock_processor/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.8.12"
__version__ = "0.8.13"
Loading
Loading