Creating a local drive mapped with an Azure File Share for your files in the cloud

File Management in the cloud is always an hot topic when using SaaS services like Dynamics 365 Business Central.

A common request that I always see popping up on forums or from partners and customers is if it’s possible to automatically save a file on a local drive from Dynamics 365 Business Central (or more in general, from SaaS applications). As you can imagine, from a SaaS tenant you don’t have access to the local file system (so you cannot directly save a file into your C: drive or on your local network share folder). I’ve talked in the past about this topic (our Mastering Dynamics 365 Business Central book provides a full solution for this task) and I’ve also shared with you a solution on this blog that permits you to save a file from Dynamics 365 Business Central to a cloud storage (like Azure Blob Storage) and then also to an SFTP server.

I know that many of you are using the solution provided in our book, but a question that sometimes I receive is: can I map this cloud storage to a local drive, so that my users can manage files transparently by working exactly as they are used to do with the files on their local machine?

As standard, you cannot map an Azure Blob Storage container as a local drive. For this scope, you can use Azure Files that has support for mapping drives to both local and azure hosted systems. Usage is exactly the same as I’ve described in my book or in my blog post linked above.

Azure Files and Azure Blob storage both offer ways to store large amounts of data in the cloud, but they are useful for slightly different purposes.

Azure Blob storage is useful for massive-scale, cloud-native applications that need to store unstructured data. To maximize performance and scale, Azure Blob storage is a simpler storage abstraction than a true file system. You can access Azure Blob storage only through REST-based client libraries (or directly through the REST-based protocol).

Azure Files is specifically a file system in the cloud. Azure Files has all the file abstracts that you know and love from years of working with on-premises operating systems. Like Azure Blob storage, Azure Files offers a REST interface and REST-based client libraries. Unlike Azure Blob storage, Azure Files offers SMB access to Azure file shares. By using SMB, you can mount an Azure file share directly on Windows, Linux, or macOS, either on-premises or in cloud VMs, without writing any code or attaching any special drivers to the file system. You also can cache Azure file shares on on-premises file servers by using Azure File Sync for quick access, close to where the data is used.

So, what’s the full solution that normally I use to satisfy this requirement? To upload a file to an Azure File Share, simply modify the UploadFile Azure Function from the previous post in order to call a new UploadBlobAsyncToFileShare function instead of the previous UploadBlobAsync. The new UploadBlobAsyncToFileShare function is defined as follow (it works with a CloudFileShare object instead of a CloudBlobContainer object, please not that to reduce the code lines here I’ve not handled logging and exceptions):

public static async Task<Uri> UploadBlobAsyncToFileShare(string base64String, string fileName, string fileType, string fileExtension, string folderName)
        {
            string fileShareConnectionString = "CONNECTION_STRING";
            string shareName = "share_name_lowercase";
            string contentType = fileType;
            byte[] fileBytes = Convert.FromBase64String(base64String);

            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(fileShareConnectionString);
            CloudFileClient client = storageAccount.CreateCloudFileClient();
            CloudFileShare share = client.GetShareReference(shareName);

            //Create the share if it doesn not exists
            await share.CreateIfNotExistsAsync();

            //Reference to the root directory
            CloudFileDirectory rootDirectory = share.GetRootDirectoryReference();
            CloudFileDirectory fileDirectory = null;

            if (string.IsNullOrWhiteSpace(folderName))
            {
                // There is no folder specified, so return a reference to the root directory.
                fileDirectory = rootDirectory;
            }
            else
            {
                // There was a folder specified, so return a reference to that folder.
                fileDirectory = rootDirectory.GetDirectoryReference(folderName);
                await fileDirectory.CreateIfNotExistsAsync();
            }

            // Set a reference to the file.
            CloudFile file = fileDirectory.GetFileReference(fileName);            

            using (Stream stream = new MemoryStream(fileBytes, 0, fileBytes.Length))
            {
                await file.UploadFromStreamAsync(stream).ConfigureAwait(false);
            }

            return file.Uri;
        }

Then, you need to map the file share as a local drive. You can do this task directly from the Azure Portal or you can use Azure Powershell for that. I normally prefer the Powershell way, and the script that creates the Azure File Share and them map it as a network drive is explained here.

Creating the Azure File Share instance is obviously a one-time operation. First you need to create a storage account, then you can create a file share instance on that storage account and then you can map those instance to a local drive. The Powershell script that performs all this for you is as follows:

When the file share is created, you have an endpoint like the following:

You can map this endpoint to a local drive letter by using the New-PSDrive cmdlet:

The New-PSDrive cmdlet creates temporary and persistent drives that are mapped to or associated with a location in a data store. Here I’m using the -Persist option in order o create a persistent drive. A persistent drive will remain active also when you close the Powershell session (otherwise temporary drives exist only in the current PowerShell session and in sessions that you create in the current session and you can’t access them by using File Explorer).

When mounted, you can see a new local disk (here mapped to the X: letter) and now you can use it as a normal drive:

Your users can now work with files in your SaaS tenant exactly like in a local drive.

The complete Powershell script that I’m using is available here.

5 Comments

  1. Hi Stefano,

    I’m not sure that I understand if we mapped File Share with some local drive, whether then files will be automatically upload/download when we copy file to local drive/send file from BC to Azure File Share?

    Why do you use azure function for uploading files? Is it the same purpose if we have windows service that will listen on that machine where the local drive is and upload/download files from Azure File Share.

    Tnx

    Like

    1. 1) Yes, if you mount the Azure File Share as a local drive, you can drag&drop files from your local machine and these files are uploaded to Azure (same for download).
      2) The Azure Function is used if you want a solution that permits you to automatically upload a file generated from Dynamics 365 Business Central SaaS. You cannot directly save the file into the local drive, so the function is used to pass a stream of a file and then saves it to the Azure File Share.

      Like

  2. Hi Stefano

    Thanks you for this great article. We have a senario in our company that seems like it is impossible to solve.
    We are in BC SaaS and we receive many files from our customers that we need to link them to SO. We like an Action in BC that create a folder in Azure cloud storage using SO as a name of folder and store the link in BC. Then using your solution here we open the folder in local drive and add all those files to it. So far all seems possible. But we like when our user click on link in BC it opens the folder in File Explore in local PC. Many users need to review these files during MFG process and downloading them each time or searching for folder each time is not an easy task.

    Do you think this is possible at all?

    Thanks

    Like

    1. From a link in SaaS you cannot open a local folder or drive. The Azure Function sample I’ve provided uploads a file from BC to Azure Storage and returns the file url that you can save for opening the file from a BC page. Mapping the drive as show in this post permits you to open the file locally. If a user moves a file to the mapped drive, the file is saved to Azure. To return the url to BC I use an Azure Function too.

      Like

      1. Thank you so much for your response. You described our challenge correctly. Our problem is our files are CAD files and we need to open the folder (even Azure folder) to access them and open them. But seems like we cannot click on the link and open the folder in File Explorer instead of web.

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.