Running Powershell Web Jobs on Azure websites


Update: Web Jobs now natively support PowerShell scripts.  You can upload a zip file containing a .ps1 file and it will be executed without further configurations.

Just last week we announced a series of new features for the Windows Azure platform.  One of those features is Web Jobs support for Windows Azure Web Sites.  Web Jobs enable you to run custom jobs on your web site, either on-demand, scheduled or continuously.  However, some people have already wondered why there is no support for Powershell scripts (.ps1).  In this post I’m going to show how you can use Powershell scripts to run as a Web Job on Windows Azure Web Sites.

Setting up an Azure Web Site

The first step is to quick create an empty Azure Web Site – for the purpose of this post, we don’t need actual web content. So, just create an Azure Web Site through the Azure Management Portal.

image

Once the site is created (amazing it takes less than 10 seconds!), you should now see the ‘Web Jobs’ (preview) section.

image

In this post, we’re going to be using a very basic PowerShell script to be executed on the Web Site – the script will just retrieve the list of actively running processes on the server by invoking the Get-Process cmdlet.

Verifying it doesn’t work

Of course we want to push our luck and upload a .ps1 file to create a Web Job. When you do this, the operation will fail and you’ll be presented with the following error message in the portal:

image

Running a PowerShell Web Job

We now have validated that we cannot use a PowerShell script to create a Web Job, but how can we achieve this?  As you may know, Powershell can be invoked from the commandline, by means of PowerShell.exe.  This is exactly what we’re going to do to use PowerShell as a Web Job.

First step is to create a Windows command file (.cmd or .bat) which will invoke PowerShell.exe and providing it our PowerShell commands. This is the contents of our ListProcessesPS.cmd file:

PowerShell.exe -Command "& {Get-Process}"

After zipping up this file we can now create a Web Job through the Azure Management portal:

image

The Job is created successfully and we are ready to launch it. Once the Last Run Result shows ‘Success’ we can view the outcome of the Job by navigating to the logs.

image

Below screenshot shows the outcome of running the PowerShell command on our Azure Web Site!

image

Using a separate PowerShell script – Take 1

Of course, this solution is not optimal if you have multi-line, complex PowerShell scripts.  Ideally we want to be able to create a PowerShell script file that is separate from the command script. Looking at the PowerShell.exe command-line reference, there is a parameter that allows us to invoke a PowerShell script, namely the –File parameter. So we adapt the command script accordingly:

PowerShell.exe -File Get-Processes.ps1  

We then create a zip file containing both the ps1 and cmd files and upload it to create a new Web Job. However, when you run the Web Job, we don’t get the outcome we expected. We receive an error file, which indicates that the PowerShell script we’re trying to execute is not digitally signed and therefore cannot be executed.

image

Using a separate PowerShell script – Take 2

Reverting back to the PowerShell.exe command-line reference, another command-line parameter comes to the rescue to get around this digital signature issue: –ExecutionPolicy. We update the Windows command file as follows:

PowerShell.exe -ExecutionPolicy RemoteSigned -File Get-Processes.ps1

What’s happening here is that we tell PowerShell to run all local scripts and only require a digital signature for scripts that are being downloaded from the Internet.

We update the zip file with the updated Windows command file and the PowerShell script and create a new Web Job.  When running the Job, we finally get the expected results!

image

Conclusion

As you’ve seen, there is actually a way to run PowerShell scripts as Web Jobs on Windows Azure Web Sites. You currently need to invoke it through an intermediate Windows command file instead of natively supporting .ps1 files, but in the end we got a workable solution.

Did you know that you can try Windows Azure for free?

Comments (10)

  1. Fardau says:

    I'm trying to run powershell with the Azure cmdlet i.e. Get-AzureRole but it's not working. The error file contains:

    Get-AzureRole : The term 'Get-AzureRole' is not recognized as the name of a

    [02/25/2014 13:51:00 > 782685: ERR ] cmdlet, function, script file, or operable program. Native PS code runs fine. But I want to use Azure Powershell cmdlet. Is there a way to get that working inside a web job?

  2. Andy Ball says:

    Hi Nick

    This is cool stuff. Is there anyway from an uploaded Powershell script  / job to get to the Connection Strings for the Web site ?

    We have a pair of Web Sites / SQL Azure DBs , ie for UAT and Production , so the websites have different SQL connection strings.

    Is there a way to get to the Connection String for the Web site from Powershell , ie so it runs against correct DB in my case ?

    I can work around by looking @ APPSETTING_WEBSITE_SITE_ env var and setting relevant connection string , but obviously means in 2 places / would have to reupload the script etc

    cheers

    Andy

  3. nicktrog says:

    @Andy There's different options. Either you access the web.config straight from your PS script. However, from PowerShell the easiest solution would be to leverage the Get-AzureWebSite cmdlet:

    Get-AzureWebsite -Name MySite | select ConnectionStrings

  4. nicktrog says:

    @Fardau You could add an Import-Module Azure.ps1 in your PS script and include the Azure.ps1 script (from Azure PowerShell cmdlets) inside the zip file.

  5. Matthew Belk says:

    I am trying to use the WebJobs with PS to invoke a REST method using the Invoke-RestMethod cmdlet.  I believe the command runs successfully, but I get an error in the run log:

    [03/04/2014 15:31:33 > e21e5b: SYS INFO] Status changed to Initializing

    [03/04/2014 15:31:33 > e21e5b: SYS INFO] Run script 'sendfirstregreminder.cmd' with script host – 'WindowsScriptHost'

    [03/04/2014 15:31:33 > e21e5b: SYS INFO] Status changed to Running

    [03/04/2014 15:31:33 > e21e5b: INFO]

    [03/04/2014 15:31:33 > e21e5b: INFO] C:DWASFilesSitesmyezregTempjobstriggeredSendFirstRegistrationRemindercqo15k1z.sxh>Powershell.exe -Command "& {Invoke-RestMethod -URI myezreg.azurewebsites.net/…/sendfirstregistrationreminder -TimeoutSec 120}"

    [03/04/2014 15:31:47 > e21e5b: ERR ] Invoke-RestMethod : Win32 internal error "The handle is invalid" 0x6 occurred

    [03/04/2014 15:31:47 > e21e5b: ERR ] while reading the console output buffer. Contact Microsoft Customer Support

    [03/04/2014 15:31:47 > e21e5b: ERR ] Services.

    [03/04/2014 15:31:48 > e21e5b: ERR ] At line:1 char:4

    [03/04/2014 15:31:48 > e21e5b: ERR ] + & {Invoke-RestMethod -URI

    [03/04/2014 15:31:48 > e21e5b: ERR ] myezreg.azurewebsites.net/…/sendfirstr

    [03/04/2014 15:31:48 > e21e5b: ERR ] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    [03/04/2014 15:31:48 > e21e5b: ERR ] ~~~

    [03/04/2014 15:31:48 > e21e5b: ERR ]     + CategoryInfo          : ReadError: (:) [Invoke-RestMethod], HostExceptio

    [03/04/2014 15:31:48 > e21e5b: ERR ]    n

    [03/04/2014 15:31:48 > e21e5b: ERR ]     + FullyQualifiedErrorId : ReadConsoleOutput,Microsoft.PowerShell.Commands.

    [03/04/2014 15:31:48 > e21e5b: ERR ]    InvokeRestMethodCommand

    [03/04/2014 15:31:48 > e21e5b: ERR ]  

    [03/04/2014 15:31:48 > e21e5b: SYS INFO] Status changed to Success

    The job reports that it was successful, though.  All it returns is a small JSON fragment.

    Any ideas?

    Thanks,

    Matthew

  6. Matthew Belk says:

    Just to prove that everything "works," I created a webjob just like the example in this post and it worked like one would expect. So what's different about the output of the Invoke-RestMethod cmdlet than the output of the Get-Process cmdlet that it deals with output buffers differently?  I'm invoking them the same way, via the Powershell.exe -Command "& {<command>}" syntax.  FWIW, when I didn't use the "& {<command>}", the job would just report as failed, but with the same error output.  Now it reports as success.

  7. nicktrog says:

    @Matthew I've tried running an Invoke-RestMethod myself and got it working correctly – the output shows the results of the REST call.  This is the command I'm running:

    PowerShell.exe -Command "& {Invoke-RestMethod -Uri http://www.discoposse.com/index.php/feed -Method Get}"

    Just wondering, what does your command output when run locally from a cmd shell? Feel free to DM me on Twitter (@nicktrog)

  8. Matthew Belk says:

    Same error messages in the log file when I just upload a zipped up .ps1 file that has the commands to run.

  9. nicktrog says:

    @Matthew the problem seems to be related to the Invoke-RestMethod launching a progress-bar and the fact that you don't have console access when running in Azure webjobs. A possible workaround is to do a REST request in PowerShell 2.0 style:

    $url = "<your URL>"

    $req = [System.Net.WebRequest]::Create($url)

    $req.Method ="GET"

    $req.ContentLength = 0

    $resp = $req.GetResponse()

    $reader = new-object System.IO.StreamReader($resp.GetResponseStream())

    $reader.ReadToEnd()

    You can get the results of that last line in a variable, otherwise it's sent to the console.

  10. Matthew Belk says:

    So I figured out the problem with the Invoke-RestMethod (and the Invoke-WebRequest, too, I think) and this phantom progress bar.  There is a variable $ProgressPreference the value of which needs to be changed from the default ("Continue") to "SilentlyContinue" before executing your actual script that spawns the progress bar and this takes care of it.  I went ahead and set it back to its default after my script was finished.

    Phew!