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

Comments

  • Anonymous
    July 24, 2014
    Any suggestions on how to do this in a PHP web role in a cloud service. There is no equivalent to Application_Start in php and it's expensive to check for drive letter and map it all the time?

  • Anonymous
    July 24, 2014
    Keep getting WNetAddConnection2 error 86 for PaaS. We don't have domains and network users, only could of cloud service instances and would like to have shared file system in between. So, everything that documentation refers afterwards is having some network enabled accounts on PaaS instances ? Could you please post complete example of persistent connection to file service location? Something of combined example from introduction but that doesn't rely on usage of net use (since it isn't persistent). Thanks

  • Anonymous
    July 28, 2014
    @Dave, as you also mentioned, there is no equivalent of Application_Start in PHP. Therefore, we recommend checking for existence of a file in your Azure Files share and mapping the share to a drive letter if the file cannot be found. It will mount the share only the first time a request comes in and then simply check a file’s existence in all other requests. For example; <?php if (!file_exists("z:\test.php")) {   system("net use z: \account.file.core.windows.net\share /u:account key 2>nul 1>nul"); } ?>

  • Anonymous
    August 12, 2014
    Do you think Azure Files would be a viable solution for storing ASP.Net website files that use multiple Azure VMs as IIS front-ends? Will the servers be able to reliably re-connect to the Share in order to serve the content?

  • Anonymous
    August 23, 2014
    Hi Corgalore, I use Azure Files to create files on Linux virtual machines and make them available via Windows IIS and it works very well. So from my experience, the answer to your question is yes.

  • Anonymous
    September 08, 2014
    I am calling MountShare() from my Global.Application_Start() in my Azure website as you suggest and I am getting "WNetAddConnection2 failed with error 5" - which I believe is a permissions thing. Is it that the account doesn't have local admin privileges? How might I go about resolving this?

  • Anonymous
    September 08, 2014
    Hi Spike, Mounting an Azure File Share from an Azure Website is not currently supported.  Azure File Shares can only be connected to Azure Virtual Machines. Thanks Andrew Edwards Microsoft Azure Storage

  • Anonymous
    September 12, 2014
    The comment has been removed

  • Anonymous
    September 15, 2014
    Hi "2nd try", ASP.NET web pages run under the "Network Service" context, so you need to run "cmdkey" or "net use" under that context.  As mentioned above, one way to do that is by running from global.asax.  Another way is to use psexec (technet.microsoft.com/.../bb897553.aspx) and run "net use" from a startup script:    psexec.exe -accepteula -u "NT AUTHORITYNETWORK SERVICE" net use z: \act.file.core.windows.nettest /u:act key==    echo "done" (Note the call to echo after psexec.  For your role to start successfully, the last command in the script must return '0') You also need to add the startup script to your ServiceDefinition.csdef:    <Startup>      <Task commandLine="Startup.cmd" executionContext="elevated" taskType="simple"/>    </Startup> And add both "psexec.exe" and "startup.cmd" to your webrole's bin directory. Thanks Andrew Edwards Microsoft Azure Storage

  • Anonymous
    September 16, 2014
    I ended up creating an account with the user name of the Azure file account and key as the password. What led me down that path was the need for a virtual directory pointing to some static files on the shared drive. Creating a local account on the VM with same name as the Azure file account allowed me to do a "Connect As.." when setting up the virtual directory in IIS. As it turns out setting that account as the app pool identity seems to allow access for the ASP.NET app as well. Thanks again for the feedback. Through scouring blogs like this one I've been able to set up a 2VM scenario with SQL Azure as the back end and shared session/file access.

  • Anonymous
    September 23, 2014
    The comment has been removed

  • Anonymous
    October 03, 2014
    What are the performance implications of having all shares under one storage account versus creating one share/storage account in pairs?

  • Anonymous
    October 03, 2014
    @Naziq, It is better to have multiple shares under single storage account and there is no perf implications. However, please ensure that your ingress/egress and request/sec is within the limits of a single storage account (see msdn.microsoft.com/.../dn249410.aspx) and use multiple storage accounts if you need to scale beyond the limits.

  • Anonymous
    January 14, 2015
    Is it possible to create syspreped image which will have file share automatically mounted when new VMs are created from it?

  • Anonymous
    January 14, 2015
    Hi Vladimir, Connections to Azure Files are per user, not per VM.  So you can't create a sysprep'd image with a shre automatically mounted.  Instead, you should have your service/application mount the share from the user context in which it runs or will run. Thanks Andrew Edwards Microsoft Azure Storage

  • Anonymous
    January 15, 2015
    The comment has been removed

  • Anonymous
    March 05, 2015
    Storage Team - I need some help.  I have been using azure files preview for several months and it really fits our use case well.  But I am experiencing a major roadblock that is preventing us from fully adopting a solution that would include azure files. We are mounting Azure File shares from Azure Ubuntu 14.04 LTS VMs and all is fine - for a while.  We have found that the share connection fails after some unknown amount of time.  A reboot always fixes the problem.  I have tried to troubleshoot this on my own, but have not been able to pinpoint the exact issue.  I'm not sure if the problem is Ubuntu, cifs, networking, ... This issue is very consistent.  A have previsioned several Azure Ubuntu 14.04 LTS VMs each mounted to a different share on the same storage account, and they all eventually fail in this way. Azure files is a great feature, and is exactly what we are looking for, but this issue has me very concerned about going forward on a large scale.  Please help. This is what I am seeing in /var/log/kern.log: Mar  4 23:50:42 <MY-VM> kernel: [1751285.617707] CIFS VFS: Server <MY-STORAGE-ACCOUNT>.file.core.windows.net has not responded in 120 seconds. Reconnecting... Mar  5 02:07:56 <MY-VM> kernel: [1759519.155512] CIFS VFS: Server <MY-STORAGE-ACCOUNT>.file.core.windows.net has not responded in 120 seconds. Reconnecting... Mar  5 05:28:07 <MY-VM> kernel: [1771530.473580] CIFS VFS: Server <MY-STORAGE-ACCOUNT>.file.core.windows.net has not responded in 120 seconds. Reconnecting... Running the command cat /proc/fs/cifs/DebugData gives the following result: CIFS Version 2.02 Features: dfs fscache lanman posix spnego xattr acl Active VFS Requests: 0 Servers:

  1. entry for <MY-STORAGE-ACCOUNT-IP> not fully displayed        TCP status: 4        Local Users To Server: 1 SecMode: 0x3 Req On Wire: 0        Shares:        1) &lt;MY-STORAGE-ACCOUNT>.file.core.windows.netssd Mounts: 1 DevInfo: 0x20 Attributes: 0x6        PathComponentMax: 255 Status: 0x1 type: DISK    DISCONNECTED        MIDs:
  2. entry for <MY-STORAGE-ACCOUNT-IP> not fully displayed        TCP status: 4        Local Users To Server: 1 SecMode: 0x3 Req On Wire: 0        Shares:        1) &lt;MY-STORAGE-ACCOUNT>.file.core.windows.netssd Mounts: 1 DevInfo: 0x20 Attributes: 0x6        PathComponentMax: 255 Status: 0x1 type: DISK    DISCONNECTED        MIDs:
  3. entry for <MY-STORAGE-ACCOUNT-IP> not fully displayed        TCP status: 4        Local Users To Server: 1 SecMode: 0x3 Req On Wire: 0        Shares:        1) &lt;MY-STORAGE-ACCOUNT>.file.core.windows.netssd Mounts: 1 DevInfo: 0x20 Attributes: 0x6        PathComponentMax: 255 Status: 0x1 type: DISK    DISCONNECTED        MIDs:
  4. entry for <MY-STORAGE-ACCOUNT-IP> not fully displayed        TCP status: 4        Local Users To Server: 1 SecMode: 0x3 Req On Wire: 0        Shares:        1) &lt;MY-STORAGE-ACCOUNT>.file.core.windows.netssd Mounts: 1 DevInfo: 0x20 Attributes: 0x6        PathComponentMax: 255 Status: 0x1 type: DISK    DISCONNECTED        MIDs: ....
  • Anonymous
    March 16, 2015
    The comment has been removed
  • Anonymous
    July 23, 2015
    Are there plans to allow access to Azure File shares from Azure WebApps any time soon?