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.
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
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?
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
@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");
}
?>
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?
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.
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?
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
Is there a cleaner/no-code solution for ASP.NET apps than referencing a Windows API call? I want to be able to deploy an ASP.NET app to two Azure VMs with only a configuration file change (pointing to \uncpath… in production, c:localfolder on developer PCs). I started with a fresh Windows 2012 VM, a new Azure file service, and a one page ASP.NET app that does File.ReadAllText() from the UNC path of the azure file service. Cmdkey saved credentials successfully and I could access the share interactively, either as my app pool account with runas or the local admin account. However, ASP.NET can never connect to the UNC path ("user name or password is incorrect"). I've tried running the app pool as local Administrator, granting rights under local policies, using mklink to create a symbolic link – nothing works and it's frustrating. What I did get to work was WNetAddConnection2 in the global.asax, but this seems like a dirty hack. I'm using all Microsoft technology, would like to see a solution in IIS or even native code. The Azure file service only gets me about 90% of where I need to be. Can we specify credentials on the file share or have a setting for no authentication from designated VM IPs?
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
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.
I spend a lot of time trying to make my shared folder accessible to PHP running on my VM with IIS.
I finally figured it out and think it might be usefull to share my solution.
1) create a windows user and affect it to IUSR group. lets call it azureIISUser
2) open powershell console and execute the following script: like explained above in "user context"
a) runas /user:<SERVERNAME>azureIISUser cmd.exe ( replace <SERVERNAME> with the name of your server)
That command will open a new DOS console running under the azureIISUser user.
b)in the DOS console, execute the following script:( if you have not already registered the shared credential)
cmdkey /add:<yourstorageaccountname>.file.core.windows.net /user:<yourstorageaccountname> /pass:<YourStorageAccountKeyWhichEndsIn==>
c) Then map a drive to the shared folder for the new user (here 'azureIISUser')
In the DOS console : net use z: \<yourstorageaccountname>.file.core.windows.net<sharedfolder>
3) Set Up IIS to use the new user:
open IIS, select the site that need access to the mapped shared
a) edit Authentification settings: Authentification settings -> anonymous authentification ->edit->select aplication pool identity
b) You can now affect the new user to the application pool used by the site. If you affect the user to the application pool without changing the setting of anonymous acces, the site will keep running under IUSER and will not have access to the shared folder.
What are the performance implications of having all shares under one storage account versus creating one share/storage account in pairs?
@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.
Is it possible to create syspreped image which will have file share automatically mounted when new VMs are created from it?
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
@Andrew Edwards thanks for your answer.
It would be nice if I could sysprep the image with the script which would mount the file share automatically. So when I create a VM out of that image, file share is automatically available. I could put a net use line in a script.
But I'm not sure if setting up a script which would execute at Windows Startup (like a Windows service?) is possible? If we talk about syspreped image.
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) \<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) \<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) \<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) \<MY-STORAGE-ACCOUNT>.file.core.windows.netssd Mounts: 1 DevInfo: 0x20 Attributes: 0x6
PathComponentMax: 255 Status: 0x1 type: DISK DISCONNECTED
MIDs:
….
developerBillCooper, we will need to look into the account in more detail to see what is going on. Can you send some details in a separate email to ascl @ microsoft.com? We will need the following details:
1. Storage account name (do NOT send the storage account key)
2. Time when you saw the connection drop (I cant tell the timezone from your logs below)
Thanks,
Atul
Are there plans to allow access to Azure File shares from Azure WebApps any time soon?