How to run Azure cmdlets from a powershell script

2019-08-10 09:05发布

问题:

I have a VS 2012 MVC 4 web role project and I successfully call a powershell script from the bin directory of the role, after I run Set-ExecutionPolicy Unrestricted from a startup cmd. The script contains some azure cmdlets and when I run the project locally the script works perfectly. However when I publish the project on Azure, the same azure cmdlets in the script do not run. For example:

param([string]$websiteName="itudkcloudqwerdfs", [string]$serverLogin="user", [string]$serverPass="paSS123!@#", [string]$location="North Europe")

# Create new Website
#

$website = New-AzureWebsite -Location $location -Name $websiteName

if ($website -eq $null)
{
    throw "Website creation error"
    exit
}

creates an Azure website successfully when run locally but throws and exits when published and run from the cloud service.

Of course I googled around for a solution and I found that I have to pre-install Azure Powershell to the Web Role in order to run Azure cmdlets there. To do that, I installed the web platform installer 4.5, copied its command line tool executable with the Microsoft.Web.PlatformInstaller dll and my Azure publishsettings file to my Web Role's bin folder and I created a cmd that installs the Azure Powershell from webpicmd to a special folder in the Web Role's bin directory and imports my publishsettings file on Web Role startup:

md "%~dp0appdata"
cd "%~dp0appdata"
cd..

reg add "hku\.default\software\microsoft\windows\currentversion\explorer\user shell folders" /v "Local AppData" /t REG_EXPAND_SZ /d "%~dp0appdata" /f

"%~dp0\WebpiCmd.exe" /Install /AcceptEula /Products:WindowsAzurePowershell /log:%~dp0WindowsAzurePowershell.log

reg add "hku\.default\software\microsoft\windows\currentversion\explorer\user shell folders" /v "Local AppData" /t REG_EXPAND_SZ /d %%USERPROFILE%%\AppData\Local /f

powershell Import-AzurePublishSettingsFile
exit /b 0

It still didn't work, so I remotely logged in to the instance to see for myself what could be wrong. I tried to run the cmd script above and it required the .NET Framework 3.5 feature enabled, which looks weird to me since the instance already has the .NET 4.5 feature enabled by default (Windows Server 2012). Anyway, I enabled it from the server manager and the script ran successfully with no errors. I opened the powershell and I tested some Azure cmdlets and they worked. However, even after that, when I invoke the script again from the web app, it still doesn't run the azure cmdlets and exits.

What am I missing? And why does webpicmd requires the .NET 3.5 feature? Is there an alternative solution to this?

Update: After Gaurav's comment, I added this into my servicedefinition.csdef under the WebRole tag to run the webrole in an elevated execution context but the azure cmdlets are still not running in the script:

<Runtime executionContext="elevated" />

Update: Here's the C# code that invokes the script

//create command
string path = HttpContext.Server.MapPath("/bin/InitializeResources.ps1");
PSCommand cmd = new PSCommand();

cmd.AddCommand(path);
cmd.AddParameter("websiteName", websiteName);
cmd.AddParameter("serverLogin", username);
cmd.AddParameter("serverPass", password);
cmd.AddParameter("location", location);

//set the command into a powershell object and invoke it
Runspace runspace = RunspaceFactory.CreateRunspace();
PowerShell ps = PowerShell.Create();
ps.Commands = cmd;

try
{
    runspace.Open();
    RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runspace);
    runSpaceInvoker.Invoke("Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force");
    ps.Runspace = runspace;

    Collection<PSObject> commandResults = ps.Invoke();

    //save results
    WebsiteInfo websiteInfo = new WebsiteInfo();
    foreach (PSObject result in commandResults)
    {
        websiteInfo.Connectionstring = (string)result.Members["ConnectionString"].Value;
        websiteInfo.WebsiteURL = (string)result.Members["WebsiteUrl"].Value;
        websiteInfo.ftpUrl = (string)result.Members["SelfLink"].Value;
        websiteInfo.ftpUrl = websiteInfo.ftpUrl.Substring(8);
        int index = websiteInfo.ftpUrl.IndexOf(":");
        if (index > 0)
            websiteInfo.ftpUrl = websiteInfo.ftpUrl.Substring(0, index);
        websiteInfo.ftpUrl = websiteInfo.ftpUrl.Replace("api", "ftp");
        websiteInfo.publishUsername = (string)result.Members["Repository"].Value + "\\" + (string)result.Members["PublishUsername"].Value;
        websiteInfo.publishPassword = (string)result.Members["PublishPassword"].Value;
    }

    runspace.Dispose();
    runspace = null;
    ps.Dispose();
    ps = null;
}

回答1:

I'm facing a similar problem as you can see in this thread I couldn't see any exception in Event Viewer as you, but adding these lines after each shell invoke I could see that my errors comes in the Import-Module line.

if (shell1.Streams.Error.Count > 0)
{
   for (int i = 0; i < shell1.Streams.Error.Count; i++)
   {
       ResultBox.Text += "Error: " + shell1.Streams.Error[i] + "\r\n";
   }
}


回答2:

At the time when we did that project with almamouz, it was not possible to generate resources via Azure REST API nor it was possible to do that via powershell scripts from webrole due to security considerations. We ended up using web and worker roles and the powershell script was running in worker role. It contained all the sensitive server connection info.