Azure Storage FileShare
Create and Manage Azure FileShare Files and Directories.
IT Services · Azure Storage FileShare
Details
| ID | Azure Storage FileShare |
|---|---|
| Provider | Microsoft |
| Category | IT Services |
| From Version | 6.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Azure Storage FileShare
Create and Manage Azure FileShare Files and Directories.
This integration was integrated and tested with version “2020-10-02” of Azure Storage FileShare
Configure Azure Storage FileShare in Cortex
| Parameter | Required |
|---|---|
| Storage account name | True |
| Account SAS Token | True |
| Use system proxy settings | False |
| Trust any certificate (not secure) | False |
Shared Access Signatures (SAS) Permissions
In order to use the integration use-cases,
please make sure your SAS token contains the following permissions:
- ‘File’ and ‘Blob’ services.
- ‘Service’, ‘Container’ and ‘Object’ resource types.
- ‘Read’, ‘Write’, ‘Delete’, ‘List’, ‘Create’, ‘Add’, ‘Update’ and ‘Immutable storage’ permissions.
- ‘Blob versioning permissions’
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
azure-storage-fileshare-create
Create a new Azure file share under the specified account.
Base Command
azure-storage-fileshare-create
Input
| Argument Name | Description | Required |
|---|---|---|
| share_name | The name of the new Share to create. Rules for naming shares can be found here: https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-shares–directories–files–and-metadata. | Required |
Context Output
There is no context output for this command.
Command Example
!azure-storage-fileshare-create share_name="test-xsoar"
Human Readable Output
Share test-xsoar successfully created.
azure-storage-fileshare-delete
Delete file share under the specified account.
Base Command
azure-storage-fileshare-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| share_name | The name of the Share to delete. | Required |
Context Output
There is no context output for this command.
Command Example
!azure-storage-fileshare-delete share_name="test-xsoar"
Human Readable Output
Share test-xsoar successfully deleted.
azure-storage-fileshare-list
list Azure file shares under the specified account.
Base Command
azure-storage-fileshare-list
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | Number of shares to retrieve. Default is 50. Default is 50. | Optional |
| prefix | Filters the results to return only shares whose name begins with the specified prefix. | Optional |
| page | Page number. Default is 1. Default is 1. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| AzureStorageFileShare.Share.Name | String | Share name. |
Command Example
!azure-storage-fileshare-list prefix="test-xsoar" limit="1"
Context Example
{
"AzureStorageFileShare": {
"Share": {
"Name": "test-xsoar"
}
}
}
Human Readable Output
Shares List
Current page size: 1
Showing page 1 out others that may exist
Name test-xsoar
azure-storage-fileshare-content-list
List files and directories under the specified share or directory.
Base Command
azure-storage-fileshare-content-list
Input
| Argument Name | Description | Required |
|---|---|---|
| prefix | Filters the results to return only files and directories whose name begins with the specified prefix. | Optional |
| limit | Number of directories and files to retrieve. Default is 50. Default is 50. | Optional |
| share_name | The name of the Share in which the directories ans files are located. | Required |
| directory_path | The path to the parent directory of the directories and files to retrieve. A path name is composed of one or more directory name components separated by the forward-slash (/) character. If the parent directory path is omitted, the directory will be referred to the first level of the specified share. | Optional |
| page | Page number. Default is 1. Default is 1. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| AzureStorageFileShare.Share.Name | String | Share name. |
| AzureStorageFileShare.Share.Content.Path | String | Directory path.. |
| AzureStorageFileShare.Share.Content.DirectoryId | String | Directory ID. |
| AzureStorageFileShare.Share.Content.File.FileId | String | File ID. |
| AzureStorageFileShare.Share.Content.File.Name | String | File name. |
| AzureStorageFileShare.Share.Content.File.Property.Content-Length | String | File size in bytes. |
| AzureStorageFileShare.Share.Content.File.Property.CreationTime | Date | File creation time. |
| AzureStorageFileShare.Share.Content.File.Property.LastAccessTime | Date | File last access time. |
| AzureStorageFileShare.Share.Content.File.Property.LastWriteTime | Date | File last write time. |
| AzureStorageFileShare.Share.Content.File.Property.ChangeTime | Date | File change time. |
| AzureStorageFileShare.Share.Content.File.Property.Last-Modified | Date | File last modified time. |
| AzureStorageFileShare.Share.Content.Directory.FileId | String | Directory ID. |
| AzureStorageFileShare.Share.Content.Directory.Name | String | Directory name. |
| AzureStorageFileShare.Share.Content.Directory.Property.CreationTime | Date | File creation time. |
| AzureStorageFileShare.Share.Content.Directory.Property.LastAccessTime | Date | File last access time. |
| AzureStorageFileShare.Share.Content.Directory.Property.LastWriteTime | Date | File last write time. |
| AzureStorageFileShare.Share.Content.Directory.Property.ChangeTime | Date | File change time. |
| AzureStorageFileShare.Share.Content.Directory.Property.Last-Modified | Date | File last modified time. |
Command Example
!azure-storage-fileshare-content-list limit="50" share_name="myfileshare" directory_path="mydirectorytest" page="1"
Context Example
{
"AzureStorageFileShare": {
"Share": {
"Content": {
"Directory": [
{
"FileId": "13835084443561230336",
"Name": "tttttt",
"Property": {
"ChangeTime": "2021-08-12T07:11:50",
"CreationTime": "2021-08-12T07:11:50",
"Last-Modified": "2021-08-12T07:11:50",
"LastAccessTime": "2021-08-12T07:11:50",
"LastWriteTime": "2021-08-12T07:11:50"
}
},
{
"FileId": "16140971433240035328",
"Name": "yehuda123",
"Property": {
"ChangeTime": "2021-08-06T13:44:04",
"CreationTime": "2021-08-06T13:44:04",
"Last-Modified": "2021-08-06T13:44:04",
"LastAccessTime": "2021-08-06T13:44:04",
"LastWriteTime": "2021-08-06T13:44:04"
}
}
],
"DirectoryId": "11529285414812647424",
"File": [
{
"FileId": "13835137220119363584",
"Name": "testepoccreation.txt",
"Property": {
"ChangeTime": "2021-08-12T05:26:11",
"Content-Length": "11",
"CreationTime": "2021-08-12T05:26:11",
"Last-Modified": "2021-08-15T09:39:17",
"LastAccessTime": "2021-08-12T05:26:11",
"LastWriteTime": "2021-08-12T05:26:11"
}
},
{
"FileId": "13835060254305419264",
"Name": "testsasss.txt",
"Property": {
"ChangeTime": "2021-08-15T09:44:16",
"Content-Length": "11",
"CreationTime": "2021-08-15T09:44:16",
"Last-Modified": "2021-08-15T09:44:16",
"LastAccessTime": "2021-08-15T09:44:16",
"LastWriteTime": "2021-08-15T09:44:16"
}
}
],
"Path": "mydirectorytest"
},
"Name": "myfileshare"
}
}
}
Human Readable Output
Directories and Files List:
Current page size: 50
Showing page 1 out others that may existDirectories
Name File Id tttttt 13835084443561230336 yehuda123 16140971433240035328 Files
Name File Id testepoccreation.txt 13835137220119363584 testsasss.txt 13835060254305419264
azure-storage-fileshare-directory-create
Create a new directory under the specified share or parent directory.
Base Command
azure-storage-fileshare-directory-create
Input
| Argument Name | Description | Required |
|---|---|---|
| share_name | The name of the Share in which the new directory will be created. | Required |
| directory_name | The name of the new directory. | Required |
| directory_path | The path to the parent directory where the new directory will be created. A path name is composed of one or more directory name components separated by the forward-slash (/) character. If the parent directory path is omitted, the directory will be referred to the first level of the specified share. | Optional |
Context Output
There is no context output for this command.
Command Example
!azure-storage-fileshare-directory-create share_name="test-xsoar" directory_name="xsoar-directory"
Human Readable Output
xsoar-directory Directory successfully created in test-xsoar.
azure-storage-fileshare-directory-delete
Delete the specified empty directory. Note that the directory must be empty before it can be deleted.
Base Command
azure-storage-fileshare-directory-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| share_name | The name of the Share in which the directory is located. | Required |
| directory_name | The name of the directory to delete. | Required |
| directory_path | The path to the parent directory of the directory to delete. A path name is composed of one or more directory name components separated by the forward-slash (/) character. If the parent directory path is omitted, the directory will be referred to the first level of the specified share. | Optional |
Context Output
There is no context output for this command.
Command Example
!azure-storage-fileshare-directory-delete share_name="test-xsoar" directory_name="xsoar-directory"
Human Readable Output
xsoar-directory Directory successfully deleted from test-xsoar.
azure-storage-fileshare-file-create
Creates a new file in Share.
Base Command
azure-storage-fileshare-file-create
Input
| Argument Name | Description | Required |
|---|---|---|
| share_name | The name of the Share in which the new file will be created. | Required |
| file_entry_id | The entry ID of the file to upload as a new file. Available from XSOAR war room while the context data contains file output. | Required |
| directory_path | The path to the parent directory where the new file will be created. A path name is composed of one or more directory name components separated by the forward-slash (/) character. If the parent directory path is omitted, the directory will be created within first level of the specified share. | Optional |
| file_name | The name of the new file to create. Default is XSOAR file name. The file suffix should be specified. for example: test.txt. | Optional |
Context Output
There is no context output for this command.
Command Example
!azure-storage-fileshare-file-create share_name="test-xsoar" directory_path="xsoar-directory" file_name="AzureStorage.txt" file_entry_id="16488@b5e40781-86c8-4799-8f10-ace443e93234"
Human Readable Output
File successfully created in test-xsoar.
azure-storage-fileshare-file-get
Retrieve file from Share.
Base Command
azure-storage-fileshare-file-get
Input
| Argument Name | Description | Required |
|---|---|---|
| share_name | The name of the Share in which the file is located. | Required |
| file_name | The name of the file to retrieve. The file suffix should be specified. for example: test.txt. | Required |
| directory_path | The path to the parent directory of the file to retrieve. A path name is composed of one or more directory name components separated by the forward-slash (/) character. If the parent directory path is omitted, the directory will be referred to the first level of the specified share. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| File.Size | Number | The size of the file. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA256 hash of the file. |
| File.Name | String | The name of the file. |
| File.SSDeep | String | The SSDeep hash of the file. |
| File.EntryID | String | The entry ID of the file. |
| File.Info | String | File information. |
| File.Type | String | The file type. |
| File.MD5 | String | The MD5 hash of the file. |
| File.Extension | String | The file extension. |
Command Example
!azure-storage-fileshare-file-get share_name="test-xsoar" file_name="AzureStorage.txt" directory_path="xsoar-directory"
Context Example
{
"File": {
"EntryID": "16572@b5e40781-86c8-4799-8f10-ace443e93234",
"Extension": "txt",
"Info": "text/plain; charset=utf-8",
"MD5": "950eb0708854a661313dd150a643af8b",
"Name": "AzureStorage.txt",
"SHA1": "2f82d9a13f948a1ced93f9da85323d45fb2eedf8",
"SHA256": "150296c0c1a1ca044fc132010b6049342c460f4a68386ab24de9b6a167e54765",
"SHA512": "6d9deaacce47943767d19c8de846c9e57692543c90b4f45c3abbe4123380bd1665dc4bd9b4d0a99d09d3a0f8c67da842826181834223362f1c75f5f9e6c358ef",
"SSDeep": "3:h8Kpl:Rpl",
"Size": 11,
"Type": "ASCII text, with no line terminators"
}
}
Human Readable Output
azure-storage-fileshare-file-delete
Delete file from Share.
Base Command
azure-storage-fileshare-file-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| share_name | The name of the Share in which the file is located. | Required |
| file_name | The name of the file to delete. The file suffix should be specified. for example: test.txt. | Required |
| directory_path | The path to the parent directory of the file to delete. A path name is composed of one or more directory name components separated by the forward-slash (/) character. If the parent directory path is omitted, the directory will be referred to the first level of the specified share. | Optional |
Context Output
There is no context output for this command.
Command Example
!azure-storage-fileshare-file-delete share_name="test-xsoar" file_name="AzureStorage.txt" directory_path="xsoar-directory"
Human Readable Output
File AzureStorage.txt successfully deleted from test-xsoar.
Configuration parameters
credentials— Storage account name (required)proxy— Use system proxy settingsinsecure— Trust any certificate (not secure)
Commands (9)
-
azure-storage-fileshare-content-listList files and directories under the specified share or directory.
-
azure-storage-fileshare-createCreate a new Azure file share under the specified account.
-
azure-storage-fileshare-deleteDelete file share under the specified account.
-
azure-storage-fileshare-directory-createCreate a new directory under the specified share or parent directory.
-
azure-storage-fileshare-directory-deleteDelete the specified empty directory. Note that the directory must be empty before it can be deleted.
-
azure-storage-fileshare-file-createCreates a new file in Share.
-
azure-storage-fileshare-file-deleteDelete file from Share.
-
azure-storage-fileshare-file-getRetrieve file from Share.
-
azure-storage-fileshare-listlist Azure file shares under the specified account.
import copy import shutil from collections.abc import Callable import defusedxml.ElementTree as defused_ET import demistomock as demisto # noqa: F401 import urllib3 from CommonServerPython import * # noqa: F401 from requests import Response GENERAL_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" DATE_FORMAT = "%a, %d %b %Y %H:%M:%S GMT" account_sas_token = "" storage_account_name = "" class Client: """ API Client """ def __init__(self, server_url, verify, proxy, account_sas_token, storage_account_name, api_version): self.ms_client = MicrosoftStorageClient(server_url, verify, proxy, account_sas_token, storage_account_name, api_version) def create_share_request(self, share_name: str) -> Response: """ Create a new Azure file share under the specified account. Args: share_name (str): Share name. Returns: Response: API response from Azure. """ params = assign_params(restype="share") response = self.ms_client.http_request( method="PUT", url_suffix=f"{share_name}", params=params, return_empty_response=True ) return response def delete_share_request(self, share_name: str) -> Response: """ Delete file share under the specified account. Args: share_name (str): Share name. Returns: Response: API response from Azure. """ params = assign_params(restype="share") response = self.ms_client.http_request( method="DELETE", url_suffix=f"{share_name}", params=params, return_empty_response=True ) return response def list_shares_request(self, limit: str = None, prefix: str = None, marker: str = None) -> str: """ list Azure file shares under the specified account. Args: limit (str): Number of shares to retrieve. prefix (str): Filters the results to return only shares whose name begins with the specified prefix. marker (str): Identifies the portion of the list to be returned. Returns: str: API response from Azure. """ params = assign_params(comp="list", maxresults=limit, prefix=prefix, marker=marker) response = self.ms_client.http_request(method="GET", url_suffix="", params=params, resp_type="text") return response def list_directories_and_files_request( self, share_name: str, directory_path: str = None, prefix: str = None, limit: str = None, marker: str = None ) -> str: """ List files and directories under the specified share or directory. Args: share_name (str): Share name. directory_path (str): The path to the directory. prefix (str): Filters the results to return only files and directories whose name begins with the specified prefix. limit (str): Number of directories and files to retrieve. marker (str): Identifies the portion of the list to be returned. Returns: str: API response from Azure. """ params = assign_params( restype="directory", comp="list", include="Timestamps", prefix=prefix, maxresults=limit, marker=marker ) url_suffix = f"{share_name}/{directory_path}" if directory_path else f"{share_name}" response = self.ms_client.http_request(method="GET", url_suffix=url_suffix, params=params, resp_type="text") return response def create_directory_request(self, share_name: str, directory_name: str, directory_path: str = None) -> Response: """ Create a new directory under the specified share or parent directory. Args: share_name (str): Share name. directory_name (str): New directory name. directory_path (str): The path to the directory. Returns: Response: API response from Azure. """ params = assign_params(restype="directory") headers = { "x-ms-file-permission": "inherit ", "x-ms-file-attributes": "None", "x-ms-file-creation-time": "now", "x-ms-file-last-write-time": "now", } url_suffix = f"{share_name}/{directory_path}/{directory_name}" if directory_path else f"{share_name}/{directory_name}" response = self.ms_client.http_request( method="PUT", url_suffix=url_suffix, params=params, headers=headers, return_empty_response=True ) return response def delete_directory_request(self, share_name: str, directory_name: str, directory_path: str) -> Response: """ Delete the specified empty directory. Args: share_name (str): Share name. directory_name (str): Directory name. directory_path (str): The path to the directory. Returns: Response: API response from Azure. """ params = assign_params(restype="directory") url_suffix = f"{share_name}/{directory_path}/{directory_name}" if directory_path else f"{share_name}/{directory_name}" response = self.ms_client.http_request(method="DELETE", url_suffix=url_suffix, params=params, return_empty_response=True) return response def create_file_request(self, share_name: str, file_entry_id: str, file_name: str, directory_path: str = None) -> Response: """ Create a New empty file in Share from War room file Entry ID. Note that this operation only initializes the file. To add content to a file, we have to call the Put Range operation. Args: share_name (str): Share name. file_entry_id (str): File War room Entry ID. file_name (str): File name. Default is XSOAR file name. directory_path (str): The path to the directory where the file should be created. Returns: Response: API response from Azure. """ xsoar_file_data = demisto.getFilePath(file_entry_id) # Retrieve XSOAR system file path and name, given file entry ID. xsoar_system_file_path = xsoar_file_data["path"] new_file_name = file_name if file_name else xsoar_file_data["name"] create_file_headers = { "x-ms-type": "file", "x-ms-file-permission": "Inherit", "x-ms-file-attributes": "None", "x-ms-file-creation-time": "now", "x-ms-file-last-write-time": "now", } create_file_url = f"{share_name}/{directory_path}/{new_file_name}" if directory_path else f"{share_name}/{new_file_name}" try: shutil.copy(xsoar_system_file_path, new_file_name) except FileNotFoundError: raise Exception( "Failed to prepare file for upload. The process of importing and copying the file data from XSOAR failed." ) try: with open(new_file_name, "rb") as file: file.seek(0, 2) content_length = file.tell() create_file_headers["x-ms-content-length"] = str(content_length) create_file_response = self.ms_client.http_request( method="PUT", url_suffix=create_file_url, headers=create_file_headers, return_empty_response=True ) finally: shutil.rmtree(new_file_name, ignore_errors=True) return create_file_response def add_file_content_request( self, share_name: str, file_entry_id: str, file_name: str, directory_path: str = None ) -> Response: """ Write a range of bytes to a file. Note that this operation not initializes the file, but add content to a file. Args: share_name (str): Share name. file_entry_id (str): File War room Entry ID. file_name (str): File name. Default is XSOAR file name. directory_path (str): The path to the directory where the file should be created. Returns: Response: API response from Azure. """ xsoar_file_data = demisto.getFilePath(file_entry_id) # Retrieve XSOAR system file path and name, given file entry ID. xsoar_system_file_path = xsoar_file_data["path"] new_file_name = file_name if file_name else xsoar_file_data["name"] try: shutil.copy(xsoar_system_file_path, new_file_name) except FileNotFoundError: raise Exception( "Failed to prepare file for upload. The process of importing and copying the file data from XSOAR failed." ) try: with open(new_file_name, "rb") as file: file.seek(0, 2) content_length = file.tell() file.seek(0) max_range = int(content_length) - 1 bytes_range = f"bytes=0-{max_range}" put_rang_headers = { "x-ms-write": "update", "x-ms-range": bytes_range, "Content-Length": str(content_length), "x-ms-type": "file", } params = {"comp": "range"} put_range_url = ( f"{share_name}/{directory_path}/{new_file_name}" if directory_path else f"{share_name}/{new_file_name}" ) put_range_response = self.ms_client.http_request( method="PUT", url_suffix=put_range_url, headers=put_rang_headers, params=params, return_empty_response=True, data=file, ) finally: shutil.rmtree(new_file_name, ignore_errors=True) return put_range_response def get_file_request(self, share_name: str, file_name: str, directory_path: str = None) -> Response: """ Get file from Share. Args: share_name (str): Share name. file_name (str): File name. directory_path (str): The path to the file directory. Returns: Response: API response from Azure. """ url_suffix = f"{share_name}/{directory_path}/{file_name}" if directory_path else f"{share_name}/{file_name}" response = self.ms_client.http_request(method="GET", url_suffix=url_suffix, resp_type="response") return response def delete_file_request(self, share_name, file_name, directory_path): """ Delete file from Share. Args: share_name (str): Share name. file_name (str): File name. directory_path (str): The path to the file directory. Returns: Response: API response from Azure. """ url_suffix = f"{share_name}/{directory_path}/{file_name}" if directory_path else f"{share_name}/{file_name}" response = self.ms_client.http_request(method="DELETE", url_suffix=url_suffix, return_empty_response=True) return response def create_share_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Create a new Azure file share under the specified account. Args: client (Client): Azure FileShares Storage API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ share_name = args["share_name"] share_name_regex = "^[a-z0-9](?!.*--)[a-z0-9-]{1,61}[a-z0-9]$" # Rules for naming shares can be found here: # https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-shares--directories--files--and-metadata if not re.search(share_name_regex, share_name): raise Exception("The specified share name is invalid.") client.create_share_request(share_name) command_results = CommandResults( readable_output=f"Share {share_name} successfully created.", ) return command_results def delete_share_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Delete file share under the specified account. Args: client (Client): Azure FileShares Storage API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ share_name = args["share_name"] client.delete_share_request(share_name) command_results = CommandResults( readable_output=f"Share {share_name} successfully deleted.", ) return command_results def get_pagination_next_marker_element(limit: str, page: int, client_request: Callable, params: dict) -> str: """ Get next marker element for request pagination. 'marker' is a string value that identifies the portion of the list to be returned with the next list operation. The operation returns a NextMarker element within the response body if the list returned was not complete. This value may then be used as a query parameter in a subsequent call to request the next portion of the list items. Args: limit (str): Number of elements to retrieve. page (str): Page number. client_request (Callable): Client request function. params (dict): Request params. Returns: str: Next marker. """ offset = int(limit) * (page - 1) response = client_request(limit=str(offset), **params) tree = ET.ElementTree(defused_ET.fromstring(response)) root = tree.getroot() return root.findtext("NextMarker") # type: ignore def list_shares_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ list Azure file shares under the specified account. Args: client (Client): Azure FileShares Storage API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ limit = args.get("limit") or "50" prefix = args.get("prefix") page = arg_to_number(args.get("page") or "1") marker = "" readable_message = f"Shares List:\n Current page size: {limit}\n Showing page {page} out others that may exist" if page > 1: # type: ignore marker = get_pagination_next_marker_element( limit=limit, page=page, # type: ignore client_request=client.list_shares_request, params={"prefix": prefix}, ) if not marker: return CommandResults( readable_output=readable_message, outputs_prefix="AzureStorageFileShare.Share", outputs=[], raw_response=[] ) response = client.list_shares_request(limit, prefix, marker=marker) tree = ET.ElementTree(defused_ET.fromstring(response)) root = tree.getroot() raw_response = [] outputs = [] for element in root.iter("Share"): data = handle_content_properties_information(element) raw_response.append(data) outputs.append({"Name": element.findtext("Name")}) readable_output = tableToMarkdown(readable_message, outputs, headers=["Name"], headerTransform=pascalToSpace) command_results = CommandResults( readable_output=readable_output, outputs_prefix="AzureStorageFileShare.Share", outputs_key_field="Name", outputs=outputs, raw_response=raw_response, ) return command_results def handle_content_properties_information(element: object) -> dict: """ Handle API response 'properties' information. Args: element (object): An XML element hierarchy data. Returns: dict: Transformed dictionary of properties information. """ data = {"Name": element.findtext("Name")} # type: ignore properties = {} for share_property in element.findall("Properties"): # type: ignore for attribute in share_property: properties[attribute.tag] = attribute.text data["Properties"] = properties # type: ignore return data def handle_directory_content_response(response: str) -> dict: """ Convert XML schema directory content to dictionary data structure. Args: response (str): XML schema string response. Returns: dict: Raw response. """ tree = ET.ElementTree(defused_ET.fromstring(response)) root = tree.getroot() xml_path = ["Directory", "File"] raw_response = {"Directory": [], "File": [], "DirectoryId": root.findtext("DirectoryId")} # type: ignore for path in xml_path: for element in root.iter(path): data = handle_content_properties_information(element) data["FileId"] = element.findtext("FileId") raw_response[path].append(data) # type: ignore return raw_response def create_directory_content_output(share_name: str, raw_response: dict, directory_path: str = "") -> dict: """ Create XSOAR context output for list directory command. Args: share_name (str): Share name. raw_response (dict): Request raw response. directory_path (str): Source directory path. Returns: dict: XSOAR command context output. """ xml_path = ["Directory", "File"] outputs = {"Name": share_name, "Content": {"Path": directory_path, "DirectoryId": raw_response["DirectoryId"]}} time_headers = ["CreationTime", "LastAccessTime", "LastWriteTime", "ChangeTime"] for path in xml_path: for element in raw_response.get(path): # type: ignore for header in time_headers: str_time = element["Properties"].get(header) # type: ignore str_time = str_time[:-2] + "Z" element["Properties"][header] = FormatIso8601( # type: ignore datetime.strptime(str_time, GENERAL_DATE_FORMAT) ) # type: ignore element["Properties"]["Last-Modified"] = FormatIso8601( # type: ignore datetime.strptime(element["Properties"]["Last-Modified"], DATE_FORMAT) ) # type: ignore element["Property"] = element.pop("Properties") # type: ignore outputs["Content"].update(raw_response) # type: ignore return outputs def create_content_readable_output(outputs: dict, prefix: str = "") -> str: """ Create readable output for list directory content command. Args: outputs (dict): Command outputs. prefix (str): Readable output prefix. Returns: str: Command readable output. """ directories_outputs = tableToMarkdown( "Directories:", outputs["Content"]["Directory"], headers=["Name", "FileId"], headerTransform=pascalToSpace ) files_outputs = tableToMarkdown( "Files:", outputs["Content"]["File"], headers=["Name", "FileId"], headerTransform=pascalToSpace ) return prefix + "\n" + directories_outputs + "\n" + files_outputs def list_directories_and_files_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ List files and directories under the specified share or directory. Args: client (Client): Azure FileShares Storage API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ prefix = args.get("prefix") limit = args.get("limit") or "50" share_name = args["share_name"] directory_path = args.get("directory_path", "") page = arg_to_number(args.get("page") or "1") marker = "" readable_message = f"Directories and Files List:\n Current page size: {limit}\n Showing page {page} out others that may exist" if page > 1: # type: ignore marker = get_pagination_next_marker_element( limit=limit, page=page, # type: ignore client_request=client.list_directories_and_files_request, params={"prefix": prefix, "share_name": share_name, "directory_path": directory_path}, ) if not marker: return CommandResults( readable_output=readable_message, outputs_prefix="AzureStorageFileShare.Share", outputs=[], raw_response=[] ) response = client.list_directories_and_files_request(share_name, directory_path, prefix, limit, marker) raw_response = handle_directory_content_response(response) response_copy = copy.deepcopy(raw_response) outputs = create_directory_content_output(share_name, response_copy, directory_path) readable_output = create_content_readable_output(outputs, readable_message) command_results = CommandResults( readable_output=readable_output, outputs_key_field="Name", outputs_prefix="AzureStorageFileShare.Share", outputs=outputs, raw_response=raw_response, ) return command_results def validate_characters(string: str, invalid_characters: str) -> bool: """ Validate that string does not contain invalid characters. Args: string (str): String validate. invalid_characters (str): Characters to validate. Returns: bool: True if the string is valid , otherwise False. """ return all(character not in string for character in invalid_characters) def create_directory_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Create a new directory under the specified share or parent directory. Args: client (Client): Azure FileShares Storage API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ share_name = args["share_name"] directory_name = args["directory_name"] directory_path = args.get("directory_path") if not validate_characters(directory_name, r'"\/:|<>*?'): raise Exception("The specified directory name is invalid.") client.create_directory_request(share_name, directory_name, directory_path) command_results = CommandResults( readable_output=f"{directory_name} Directory successfully created in {share_name}.", ) return command_results def delete_directory_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Delete the specified empty directory. Args: client (Client): Azure FileShares Storage API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ share_name = args["share_name"] directory_name = args["directory_name"] directory_path = args.get("directory_path") client.delete_directory_request(share_name, directory_name, directory_path) # type: ignore command_results = CommandResults(readable_output=f"{directory_name} Directory successfully deleted from {share_name}.") return command_results def create_file_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Create a file in Share from War room file Entry ID. Args: client (Client): Azure FileShares Storage API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ share_name = args["share_name"] file_entry_id = args["file_entry_id"] directory_path = args.get("directory_path") file_name = args.get("file_name") client.create_file_request(share_name, file_entry_id, file_name, directory_path) # type: ignore client.add_file_content_request(share_name, file_entry_id, file_name, directory_path) # type: ignore command_results = CommandResults(readable_output=f"File successfully created in {share_name}.") return command_results def get_file_command(client: Client, args: Dict[str, Any]) -> fileResult: # type: ignore """ Get file from Share. Args: client (Client): Azure FileShares Storage API client. args (dict): Command arguments from XSOAR. Returns: fileResult: XSOAR File Result. """ share_name = args["share_name"] file_name = args["file_name"] directory_path = args.get("directory_path") response = client.get_file_request(share_name, file_name, directory_path) return fileResult(filename=file_name, data=response.content) def delete_file_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Delete file from Share. Args: client (Client): Azure FileShares Storage API client. args (dict): Command arguments from XSOAR. Returns: CommandResults: outputs, readable outputs and raw response for XSOAR. """ share_name = args["share_name"] file_name = args["file_name"] directory_path = args.get("directory_path") client.delete_file_request(share_name, file_name, directory_path) command_results = CommandResults( readable_output=f"File {file_name} successfully deleted from {share_name}.", ) return command_results def test_module(client: Client) -> None: """ Tests API connectivity and authentication. Args: client (Client): Azure FileShares Storage API client. Returns: str : 'ok' if test passed, anything else will fail the test. """ try: client.list_shares_request() except Exception as exception: if "Error in API call" in str(exception): return return_results("Authorization Error: make sure API Credentials are correctly set") if "Error Type" in str(exception): return return_results( "Verify that the storage account name is correct and that you have access to the server from your host." ) raise exception return_results("ok") return None def main() -> None: """ Main function """ params: Dict[str, Any] = demisto.params() args: Dict[str, Any] = demisto.args() verify_certificate: bool = not params.get("insecure", False) proxy = params.get("proxy", False) global account_sas_token global storage_account_name account_sas_token = params["credentials"]["password"] storage_account_name = params["credentials"]["identifier"] api_version = "2020-10-02" base_url = f"https://{storage_account_name}.file.core.windows.net/" command = demisto.command() demisto.debug(f"Command being called is {command}") try: urllib3.disable_warnings() client: Client = Client(base_url, verify_certificate, proxy, account_sas_token, storage_account_name, api_version) commands = { "azure-storage-fileshare-create": create_share_command, "azure-storage-fileshare-delete": delete_share_command, "azure-storage-fileshare-list": list_shares_command, "azure-storage-fileshare-content-list": list_directories_and_files_command, "azure-storage-fileshare-directory-create": create_directory_command, "azure-storage-fileshare-directory-delete": delete_directory_command, "azure-storage-fileshare-file-create": create_file_command, "azure-storage-fileshare-file-get": get_file_command, "azure-storage-fileshare-file-delete": delete_file_command, } if command == "test-module": test_module(client) elif command in commands: return_results(commands[command](client, args)) else: raise NotImplementedError(f"{command} command is not implemented.") except Exception as e: return_error(str(e)) from MicrosoftAzureStorageApiModule import * # noqa: E402 if __name__ in ["__main__", "builtin", "builtins"]: main()