Azure Storage FileShare

Create and Manage Azure FileShare Files and Directories.

IT Services · Azure Storage FileShare

Details

IDAzure Storage FileShare
ProviderMicrosoft
CategoryIT Services
From Version6.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix 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:

  1. ‘File’ and ‘Blob’ services.
  2. ‘Service’, ‘Container’ and ‘Object’ resource types.
  3. ‘Read’, ‘Write’, ‘Delete’, ‘List’, ‘Create’, ‘Add’, ‘Update’ and ‘Immutable storage’ permissions.
  4. ‘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 exist

Directories

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 settings
  • insecure — Trust any certificate (not secure)

Commands (9)

  • azure-storage-fileshare-content-list

    List files and directories under the specified share or directory.

  • azure-storage-fileshare-create

    Create a new Azure file share under the specified account.

  • azure-storage-fileshare-delete

    Delete file share under the specified account.

  • azure-storage-fileshare-directory-create

    Create a new directory under the specified share or parent directory.

  • azure-storage-fileshare-directory-delete

    Delete the specified empty directory. Note that the directory must be empty before it can be deleted.

  • azure-storage-fileshare-file-create

    Creates a new file in Share.

  • azure-storage-fileshare-file-delete

    Delete file from Share.

  • azure-storage-fileshare-file-get

    Retrieve file from Share.

  • azure-storage-fileshare-list

    list 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()