I'm using Azure File Service to persist some files that a program, installed on a role instance, will access.
I have managed to map a drive, to the file service share, through remote desktop, using a PowerShell script. However, when I try to execute that same script, in the OnStart() of the worker role, it doesn't work. I can't figure out what's wrong.
So, this is what I do:
public override bool OnStart()
{
ServicePointManager.DefaultConnectionLimit = 12;
_slsPath = RoleEnvironment.GetLocalResource("StartupLocalStorage").RootPath;
var settingstring = CloudConfigurationManager.GetSetting("StorageConnectionString");
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(settingstring);
var blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("zipfiles");
ICloudBlob blob = container.GetBlobReferenceFromServer("Program.zip");
//copy blob from cloud to local gallery
blob.DownloadToFile(_slsPath + "Program.zip", FileMode.Create);
// get powershell script for mounting drive
blob = container.GetBlobReferenceFromServer("mountdrive.ps1");
blob.DownloadToFile(_slsPath + "mountdrive.ps1", FileMode.Create);
// small exe for executing powershell script for mounting drive
blob = container.GetBlobReferenceFromServer("Programinstaller.exe");
blob.DownloadToFile(_slsPath + "Programinstaller.exe", FileMode.Create);
ZipFile.ExtractToDirectory(_slsPath + @"\Program.zip", _slsPath);
var share = storageAccount
.CreateCloudFileClient()
.GetShareReference("someshare");
share.CreateIfNotExists();
var filesPath = @"Z:\Folder";
// Mounting drive
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.FileName = _slsPath + @"Programinstaller.exe";
startInfo.WindowStyle = ProcessWindowStyle.Normal;
startInfo.Arguments = _slsPath + @"\ " + filesPath;
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
int count = 0;
var res = exeProcess.StandardOutput.ReadLine();
System.IO.File.WriteAllText(_slsPath + @"ResultOfDriveMount.txt", res + Environment.NewLine);
while (count < 30)
{
Thread.Sleep(1000);
res = exeProcess.StandardOutput.ReadLine();
System.IO.File.AppendAllText(_slsPath + @"ResultOfDriveMount.txt", res + Environment.NewLine);
count++;
}
}
}
Allright, so, the Programinstaller.exe is just this:
static void Main(string[] args)
{
Console.WriteLine(args[0]);
Thread t = new Thread(() =>
{
ExecutePowerShell(args[0] + "mountdrive.ps1", args[0]);
});
t.Start();
Thread.Sleep(20000);
InstallProgram(args[0], args[1]);
}
private static void ExecutePowerShell(string scriptfile, string SlsPath)
{
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
//Here's how you add a new script with arguments
Command myCommand = new Command(scriptfile);
//CommandParameter testParam = new CommandParameter("key", "value");
//myCommand.Parameters.Add(testParam);
pipeline.Commands.Add(myCommand);
// Execute PowerShell script
var results = pipeline.Invoke();
foreach (var res in results)
System.IO.File.WriteAllText(SlsPath + @"ResultOfPowerShell.txt", res.ToString());
}
/// <summary>
/// Launch the legacy application with some options set.
/// </summary>
static void InstallProgram(string folderPath, string pathTwo)
{
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.FileName = folderPath + @"ProgramAuthor.ProgramName.exe";
startInfo.WindowStyle = ProcessWindowStyle.Normal;
startInfo.Arguments = folderPath + [someargument] + pathTwo;
try
{
// Start the process with the info we specified.
// Read output lines, store to txt
using (Process exeProcess = Process.Start(startInfo))
{
int count = 0;
var result = exeProcess.StandardOutput.ReadLine();
System.IO.File.WriteAllText(folderPath + @"ResultOfProgramInstall.txt", result + Environment.NewLine);
while (count < 300)
{
Thread.Sleep(1000);
result = exeProcess.StandardOutput.ReadLine();
System.IO.File.AppendAllText(folderPath + @"ResultOfProgramInstall.txt", result + Environment.NewLine);
count++;
}
}
}
catch (Exception ex)
{
System.IO.File.WriteAllText(folderPath + @"Error2.txt", ex.Message + " ... " + ex.StackTrace);
}
}
And the Ps1-file goes like this:
Set-Executionpolicy -Scope CurrentUser -ExecutionPolicy UnRestricted
cmdkey /add:[account].file.core.windows.net /user:[account] /pass:[key]
net use z: \\[account].file.core.windows.net\someshare
Strange thing is that running the Programinstaller.exe file, that was downloaded from the blob, from command prompt - with same parameters - in remote desktop in the worker role, will mount the mapped drive and install the program. But calling that same file from code after deploying (i.e. from OnStart()) doesn't mount the drive. The ProgramAuthor.ProgramFile.exe is executed, and tries to install in the mapped drive, but it was never mapped so it fails.
What's up with this?