Differences in Azure November 2009 SDK

I have been playing around with the new SDK and VS Tools for Azure when I have had time and have noticed a few changes to the development experience, so thought it would be a good to note them down here.

The first thing is while Azure Development Fabric and Azure Development Storage still install separately and can be run separately they are combined into the “Azure Simulation Environment”.

Azure Simulation Environment

When first starting up a new Azure project, there is a new dialogue which allows you to select roles, and is also based on language, so it would be possible to have a Web Role in VB.net and a Worker role in C# if you wished.  It also allows multiple Web Roles and Worker Roles, rather than instances, it allows a completely different code base to each one.  Therefore allowing, for example, a dedicated Worker Role to execute batch inputting, while another will deal with batch requests, and thus being able to configure the number of instances of each separately, i.e. if you know there are going to be a lot of requests to be processed but a small amount of data input, you could ramp up the number of worker roles dedicated to processing requests without increasing the number of worker roles for processing data input.

New Project

The next big change I have noticed is to the properties of the service configurations within the azure project.  By double clicking on the specific role under the “Roles” folder in the Azure project, it allows extra control over how the service is to be run, without messing around in the service configuration file.  Also allowing a change in the VM size the role will be deployed to when within the cloud and the number of instances at first deploy.

WebRoleConfiguration

There is a section within these properties for changing the settings available in the cloud configuration file.ServiceConfiguration1 ServiceConfiguration2

 

This properties also allows the configuration of certificates to be used by the roles implemented.

CertConfig1

CertConfig2

It also seems that Worker Roles now too have a concept of an endpoint, which can also be configured here, which have a type “Input” or “Internal”.

I am sure that I will find further differences as I deeper explore the new depths of Azure, and as one final point to look out for in the SDK is the “PowershellRole” sample which shows an example of how to use Powershell within both a Worker Role context:

 public class WorkerRole : PowershellRole.Common.PowershellRole
 {
     public WorkerRole()
         : base("start.ps1")
     {
     }
  
     protected override bool ShouldRecycle()
     {
         // wait ten seconds then recycle
         Thread.Sleep(10000);
  
         return true;
     }
 }
 //
 // <copyright file="PowershellRole.cs" company="Microsoft">
 //     Copyright (c) Microsoft Corporation.  All rights reserved.
 // </copyright>
 //
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading;
 using Microsoft.WindowsAzure.ServiceRuntime;
 using Microsoft.WindowsAzure.Diagnostics;
  
 namespace Microsoft.Samples.PowershellRole.Common
 {
     public abstract class PowershellRole : RoleEntryPoint
     {
         private string _startPath;
  
         public PowershellRole(string startPath)
         {
             _startPath = startPath;
         }
  
         public PowershellRunspace Runspace { get; private set; }
  
         protected virtual void PrepareRunspace()
         {
         }
  
         protected virtual bool ShouldRecycle()
         {
             return true;
         }
  
         public override bool OnStart()
         {
             DiagnosticMonitor.Start("DiagnosticsConnectionString");
  
             return base.OnStart();
         }
  
         public override void Run()
         {
             try
             {
                 while (true)
                 {
                     var script = File.ReadAllText(_startPath);
                     Runspace = new Microsoft.Samples.PowershellRole.Common.PowershellRunspace();
  
                     PrepareRunspace();
  
                     Trace.TraceInformation(String.Format("Running '{0}'...", _startPath));
  
                     var output = Runspace.RunScript(script);
  
                     Trace.TraceWarning("Script returned. Dumping output...");
  
                     foreach (var line in output.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries))
                         Trace.TraceWarning(line);
  
                     if (!ShouldRecycle())
                         Thread.Sleep(Timeout.Infinite);
                 }
             }
             catch (Exception ex)
             {
                 Trace.TraceError(ex.ToString());
             }
         }
     }
 }

and also how to initiate this from a Web Role context:

 public partial class _Default : System.Web.UI.Page
 {
     protected void Page_Load(object sender, EventArgs e)
     {
  
     }
  
     protected void ExecuteButton_Click(object sender, EventArgs e)
     {
         using (var runspace = new PowershellRunspace())
         {
             ResultLabel.Text = runspace.RunScript(ScriptText.Text);
         }
  
         ResultPanel.Visible = true;
     }
 }
 //
 // <copyright file="PowershellRunspace.cs" company="Microsoft">
 //     Copyright (c) Microsoft Corporation.  All rights reserved.
 // </copyright>
 //
 using System;
 using System.Collections.Generic;
 using System.Configuration;
 using System.Linq;
 using System.Management.Automation;
 using System.Management.Automation.Runspaces;
 using System.Reflection;
 using System.Security;
 using System.Text;
 using Microsoft.WindowsAzure.ServiceRuntime;
  
 namespace Microsoft.Samples.PowershellRole.Common
 {
     public class PowershellRunspace : IDisposable
     {
         private bool _disposed = false;
         private PowershellHost _host;
         private Runspace _runspace;
         private PowershellConfiguration _config;
  
         public PowershellRunspace()
         {
             _host = new PowershellHost();
             _config = new PowershellConfiguration();
             _runspace = RunspaceFactory.CreateRunspace(_host,_config);
             _runspace.Open();
         }
  
         private void ensureRunspaceReady()
         {
             if (_runspace == null)
             {
                 _runspace = RunspaceFactory.CreateRunspace(_host, _config);
  
                 _runspace.Open();
             }
         }
  
         public string RunScript(string script)
         {
             if (_disposed)
                 throw new ObjectDisposedException("Cannot run scripts on a disposed runspace.");
  
             ensureRunspaceReady();
  
             using (var pipeline = _runspace.CreatePipeline())
             {
                 var command = new Command(script, true);
  
                 command.MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
  
                 pipeline.Commands.Add(command);
                 pipeline.Commands.Add("out-default");
  
                 pipeline.Invoke();
             }
  
             return _host.GetOutput();
         }
  
         public object GetVariable(string name)
         {
             ensureRunspaceReady();
  
             return _runspace.SessionStateProxy.GetVariable(name);
         }
  
         public void SetVariable(string name, object value)
         {
             ensureRunspaceReady();
  
             _runspace.SessionStateProxy.SetVariable(name, value);
         }
  
         public void AddCmdlet(Type cmdletType)
         {
             var attribute = cmdletType.GetCustomAttributes(typeof(CmdletAttribute), true).Single() as CmdletAttribute;
  
             if (attribute == null)
                 throw new ArgumentException("The type passed to AddCmdlet must have a CmdletAttribute on it.", "cmdletType");
  
             _config.Cmdlets.Append(new CmdletConfigurationEntry(attribute.VerbName + "-" + attribute.NounName, cmdletType, ""));
         }
  
         public void AddAssembly(string name, string fileName)
         {
             _config.Assemblies.Append(new AssemblyConfigurationEntry(name, fileName));
         }
  
         #region IDisposable Members
         public void Dispose()
         {
             Dispose(true);
         }
  
         protected virtual void Dispose(bool disposing)
         {
             if (disposing)
             {
                 _runspace.Dispose();
             }
         }
         #endregion
     }
 }
 //
 // <copyright file="PowershellHost.cs" company="Microsoft">
 //     Copyright (c) Microsoft Corporation.  All rights reserved.
 // </copyright>
 //
 using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Management.Automation;
 using System.Management.Automation.Host;
 using System.Runtime.InteropServices;
 using System.Text;
 using Microsoft.Win32.SafeHandles;
  
 namespace Microsoft.Samples.PowershellRole.Common
 {
     class PowershellHost : PSHost
     {
         private Guid _instanceId = Guid.NewGuid();
         private PowershellUI _ui = new PowershellUI();
         private int _appCount;
         private SafeHandle _outHandle;
         private SafeHandle _errorHandle;
  
         public PowershellHost()
         {
             AllocConsole();
         }
  
         public override CultureInfo CurrentCulture
         {
             get { return CultureInfo.CurrentCulture; }
         }
  
         public override CultureInfo CurrentUICulture
         {
             get { return CultureInfo.CurrentUICulture; }
         }
  
         public override void EnterNestedPrompt()
         {
         }
  
         public override void ExitNestedPrompt()
         {
         }
  
         public override Guid InstanceId
         {
             get { return _instanceId; }
         }
  
         public override string Name
         {
             get { return "Azure Host"; }
         }
  
         public override void NotifyBeginApplication()
         {
             if (_appCount == 0)
             {
                 _outHandle = new SafeFileHandle(NativeMethods.GetStdHandle(StandardHandle.Output), false);
                 _errorHandle = new SafeFileHandle(NativeMethods.GetStdHandle(StandardHandle.Error), false);
  
                 NativeMethods.SetStdHandle(StandardHandle.Output, _ui.ApplicationOutHandle);
                 NativeMethods.SetStdHandle(StandardHandle.Error, _ui.ApplicationOutHandle);
             }
  
             _appCount++;
         }
  
         public override void NotifyEndApplication()
         {
             _appCount--;
  
             if (_appCount == 0)
             {
                 NativeMethods.SetStdHandle(StandardHandle.Output, _outHandle);
                 NativeMethods.SetStdHandle(StandardHandle.Error, _errorHandle);
  
                 _ui.FlushApplicationOut();
             }
         }
  
         public override void SetShouldExit(int exitCode)
         {
         }
  
         public override PSHostUserInterface UI
         {
             get { return _ui; }
         }
  
         public override Version Version
         {
             get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; }
         }
  
         public string GetOutput()
         {
             return _ui.GetOutput();
         }
  
         [System.Runtime.InteropServices.DllImport("kernel32.dll")]
         private static extern bool AllocConsole();
     }
 }