Persisting connections to Microsoft Azure Files

We recently blogged about our preview release of Azure Storage Files here. The post contained information on Azure Files, how to get started by applying for preview and explained some tools that we have to help you create the share and transfer data. This post will concentrate on how you can create a persistent connection to an Azure File share so that after a VM reboots, the share will be available for your scheduled tasks, applications, or logged in user.

Windows IaaS VMs

By default, Windows attempts to persist connections to SMB shares across reboots. However, it will not automatically persist your Azure Files credentials across reboots, so it will fail to reconnect to an Azure Files share after a reboot. There are several ways to persist these credentials, some of which are detailed below.

Persisting Credentials

CmdKey

The easiest way to establish a persistent connections is to save your storage account credentials into windows using the “CmdKey” command line utility. The following is an example command line for persisting your storage account credentials into your VM:

C:\>cmdkey /add:<yourstorageaccountname>.file.core.windows.net /user:<domainname>\<yourstorageaccountname> /pass:<YourStorageAccountKeyWhichEndsIn==>

Note: yourstorageaccountname is not your live id but the name in the endpoint. domainname here will be "AZURE"

CmdKey will also allow you to list the credentials it stored:

C:\>cmdkey /list

Currently stored credentials:

Target: Domain:target=filedemo.file.core.windows.net
Type: Domain Password
User: AZURE\filedemo

Once the credentials have been persisted, you no longer have to supply them when connecting to your share. Instead you can connect without specifying any credentials:

C:\>net use * \\filedemo.file.core.windows.net\demo1

Drive Z: is now connected to \\filedemo.file.core.windows.net\demo1.

The command completed successfully.

Then you can reboot your VM (this will disconnect you from your VM):

shutdown –t 0 –r

When your VM has restarted and you reconnect, you can open up another command window and confirm that your connection has been automatically reconnected:

C:\>net use

New connections will be remembered.

Status    Local    Remote       Network

-----------------------------------------------------------------------------

OK        Z:       \\filedemo.file.core.windows.net\demo1

                                Microsoft Windows Network

The command completed successfully.

Credential Manager

The credential manager (located under Control Panel\User Accounts) will also allow you to persist your storage account credentials.

image

User Contexts

Windows maintains different contexts for each user that is running on a VM, and sometimes it will have different contexts for the same user on the same VM at the same time. Each context can be independently connected to a different set of SMB shares, and each context will have its own drive letter mapping to the shares it is connected to.

The credentials persisted by CmdKey are available to the user who ran “CmdKey”. The connections remembered by “net use” are also available to the user who ran net use. Therefore, if you have an application that runs under a different user name, you may want to persist the credentials and connections for other user’s as well. To do that, you can use the “runas” command:

runas /user:<username> cmd.exe

That command will open a new command window. The title of the command window will read “cmd.exe (running as COMPUTERNAME\username)”. If you run “net use” in that command window, you will notice this user is not connected to any shares:

C:\>net use

New connections will be remembered.

There are no entries in the list.

You can run “CmdKey” and “net use” as above for that user, and persist both your storage credentials and connections to Azure File for that user.

Administrator Contexts

If you create a new local user on your VM, and add that user to the administrators group, then you can run commands for that user in both elevated and non-elevated contexts. Connections are not shared between elevated and non-elevated contexts, so you may want to connect separately in each context by executing “net use”. However, the persisted credentials are shared, so you only need to run “CmdKey” in one of the contexts.

Handling Scheduled Tasks

You can create scheduled tasks that run under any user on the VM, and credentials persistent with CmdKey for that user will be available to the schedule tasks. However, those scheduled tasks may run in a different user context than the logged in user, so connections to SMB shares will not be automatically re-connected in the user context that the task is running under.

For example, if you created a schedule task that runs a script that calls “net use” and writes the output to a local file, and that user had previously created a persistent connection to an Azure File share and also had persistent the credentials for that share, the output would contain:

Status      Local    Remote       Network

-----------------------------------------------------------------------------

Unavailable Z:       \\filedemo.file.core.windows.net\demo1

                                  Microsoft Windows Network

The command completed successfully.

However, the credentials are available in that context to reconnect to the share, so adding the following command to your script will re-establish the network connection:

net use z: \\filedemo.file.core.windows.net\demo1

Alternatively, the script can access files using the full UNC path rather than the mapped drive letter:

dir \\filedemo.file.core.windows.net\demo1

Also note that since the scheduled task is running in a different context that the logged in user, connections created by the schedule task may not be connected for the context of the logged in user.

Windows PaaS Roles

Persistent connections are the opposite of what you want for PaaS roles. For PaaS roles, you need to ensure that your code can connect automatically whether the system has started a fresh instance, or if your instance has just been restart.

PInvoke WNetAddConnection2

You can map a drive letter in your PaaS role startup code by pinvoking WNetAddConnection2. The following code declares a set of structures you need to establish a mapping from an Azure Files share to a local drive letter.

 [DllImport("Mpr.dll",
             EntryPoint = "WNetAddConnection2",
             CallingConvention = CallingConvention.Winapi)]
 private static extern int WNetAddConnection2(NETRESOURCE lpNetResource,
                                              string lpPassword,
                                              string lpUsername,
                                              System.UInt32 dwFlags);
 
 [DllImport("Mpr.dll",
            EntryPoint = "WNetCancelConnection2",
            CallingConvention = CallingConvention.Winapi)]
 private static extern int WNetCancelConnection2(string lpName,
                                                 System.UInt32 dwFlags,
                                                 System.Boolean fForce);
 
 [StructLayout(LayoutKind.Sequential)]
 private class NETRESOURCE
 {
     public int dwScope;
     public ResourceType dwType;
     public int dwDisplayType;
     public int dwUsage;
     public string lpLocalName;
     public string lpRemoteName;
     public string lpComment;
     public string lpProvider;
 };
 
 public enum ResourceType
 {
     RESOURCETYPE_DISK = 1,
 };

Then you can write a method that will mount the share on a given drive letter:

 public static void MountShare(string shareName,
                               string driveLetterAndColon,
                               string username,
                               string password)
 {
     if (!String.IsNullOrEmpty(driveLetterAndColon))
     {
         // Make sure we aren't using this driveLetter for another mapping
         WNetCancelConnection2(driveLetterAndColon, 0, true);
     }
 
     NETRESOURCE nr = new NETRESOURCE();
     nr.dwType = ResourceType.RESOURCETYPE_DISK;
     nr.lpRemoteName = shareName;
     nr.lpLocalName = driveLetterAndColon;
 
     int result = WNetAddConnection2(nr, password, username, 0);
 
     if (result != 0)
     {
         throw new Exception("WNetAddConnection2 failed with error " + result);
     }
 }

Then you can call this method from the “OnStart()” method of your role:

 MountShare("\\\\filedemo.file.core.windows.net\\demo1",
            "z:",
            "filedemo",
            "<YourStorageAccountKeyWhichEndsIn==>");

From that point forward in your worker role, you will be able to read and write files to the Azure File share using either the drive letter, or the full UNC path:

 File.Create("z:\\WNetAddConnection2.txt");
 File.Create(\\\\filedemo.file.core.windows.net\\demo1\\UNC.txt);

Web Roles and User Contexts

The OnStart() method of an Azure Web Role runs in a different user context than is used to render pages of the web site. Therefore, if you want to reference your Azure Files shares from the code that renders the pages, you should put the code from above in your Global.Application_Start() method rather than WebRole.OneStart().

Linux VM

Linux has a variety of methods for automatically mounting shares during startup, but we only experimented with one method, and only on Ubuntu 14.04 LTS.

Persist Connections with Fstab

Linux has a file called “fstab” in /etc that can be used to mount drives and shares during startup. One way to automatically mount an Azure Files share during boot is to add a line to /etc/fstab. The following text should be placed on a single line in the file:

//<yourstorageaccountname>.file.core.windows.net/demo1 /home/azureuser/smb cifs vers=2.1,dir_mode=0777,file_mode=0777,username=AZURE\<yourstorageaccountname>,password=<YourStorageAccountKeyWhichEndsIn==>

There are several parts of this line, described below in this table:

Part Example Description
Share URL //filedemo.file.core.windows.net/demo1 The URL of an Azure Files share that you previously created
Mount point /home/azureuser/smb The path to an empty directory on your Linux VM that you previously created where you want the share to be mounted.
File system Cifs The type of file system you want to mount. For Azure Files, this will be ‘cifs’.
Mount Parameters vers=2.1 The version of SMB to use, in this case version 2.1
dir_mode=0777 The permissions mask used for directories in the Azure Files share, in this case full permissions.
file_mode=0777 The permissions mask used for files in the Azure Files share, in this case full permissions.
username=filedemo For Azure Files, this username needs to be you storage account name.
password=StorageAccountKey== For Azure Files, this password needs to be your full storage account key.

There are many other options that can be included in your fstab file. For more information see the relevant document for the Linux distribution you are using.

Summary

Our previous post introduced Azure Files and this post concentrates on steps to help you create persistent connections to Azure File shares so that connections to shares are available after a reboot. As always we would love to hear feedback via comments on this blog, Azure Storage MSDN forum, or send email to mastoragequestions@microsoft.com.

Andrew Edwards

Please see these links for more information:

Azure Files 2014-04-14 version Introducing Microsoft Azure File Service AzCopy Azure File PowerShell Cmdlets (CTP) Storage .NET Client Library 4.0 for 2014-04-14 version