Clicky

Fun with the Service Bus (Part 1) - Jim O'Neil - Technology Evangelist - Site Home - MSDN Blogs

Fun with the Service Bus (Part 1)

Jim O'Neil

Technology Evangelist

E-mail  Twitter  LinkedIn  RSS Feed  About me

Check out my new blog at http:>//codocent.com

Fun with the Service Bus (Part 1)

Rate This
  • Comments 3

Last week, I filled in for Mike Benkovich on his weekly Cloud Computing: Soup to Nuts series with a presentation on the Windows Azure Service Bus.  The recording should be available soon, if not already, but I thought I’d follow up with a multi-part blog series that covers the material I presented.

Service Bus example The context for the talk was pretty simple, an application running “on-premises” and behind the firewall (your local machine, in this case) that echoes messages sent via a public website, hosted on Windows Azure (but it could be anywhere).  When the message arrives at the service host, a window pops up showing the message.

The ‘on-premises’ piece of this example is a very simple WPF application that listens for messages, either on a WCF endpoint or via the Windows Azure Service Bus..  Messages in this context have three fields: the message text, name of the sender, and a string indicating the color of the window that displays the message on-premises.  The result looks something like the following, where the browser on the left is the client, and the simple WPF application at the bottom right is the service host.  The Metro-esque windows are the result of three messages having been sent and processed by the service hosted by the WPF application.

This post covers a lot of ground, so here’s a quick outline to help you navigate through the material:

 

Service Contract

The contract for the WCF service is pretty simple and defined in a standalone assembly referenced by both the Web site client project and the WPF application that hosts the service endpoint.

using System.ServiceModel;

namespace RelayService.Interfaces
{
    [ServiceContract(Name = "IEchoContract", 
                     Namespace = "http://samples.microsoft.com/ServiceModel/Relay/")]
    public interface IEchoContract
    {
        [OperationContract]
        void Echo(string sender, string text, string color);
    }

    public interface IEchoChannel : IEchoContract, IClientChannel { }
}

On-Premises WCF Sample

Before we get to the Service Bus, let’s take a quick look at the implementation of this scenario using a self-hosted WCF application.

WPF Implementation

The WPF application in the sample project can either self-host a WCF service or open an endpoint exposed on the Service Bus; a simple checkbox on the main window controls the behavior. To keep things simple, the WCF service endpoint is set up with a simple BasicHttpBinding (that means no authentication on the service, not something you’d do in production!).

ServiceHost host = default(ServiceHost);

// local WCF service end point  
host = new ServiceHost(typeof(EchoService));
host.AddServiceEndpoint("RelayService.Interfaces.IEchoContract",
     new BasicHttpBinding(),
     http://localhost:7777/EchoService);
 

ASP.NET Client Implementation

The client implementation is straightforward, and here as in the rest of the examples in the series, I’m using code (versus configuration) to provide all of the details.  You can, of course, use the web.config file to specify the binding and endpoint.

ChannelFactory<IEchoContract> myChannelFactory =
    new ChannelFactory<IEchoContract>(
        new BasicHttpBinding(), "http://localhost:7777");

IEchoContract client = myChannelFactory.CreateChannel();
client.Echo(userName, txtMessage.Text, ddlColors.SelectedItem.Text);

((IClientChannel)client).Close();
myChannelFactory.Close();

 

Service Bus Relay Example

When you use a Service Bus relay, you’re essentially establishing an endpoint that’s hosted in the Windows Azure cloud, secured by the Access Control Service.  The main difference in the code is the endpoint configuration.  But before you can configure the endpoint in your application code, you’ll need to establish that relay endpoint in the cloud, so let’s walkthrough that now.

Configuring a Service Bus namespace

You create Service Bus namespaces via the Windows Azure portal; it’s a six step process.

Configuring a Service Bus endpoint

  1. Select the Service Bus, Access Control & Caching portion of the portal.
  2. Select Service Bus
  3. Select New on the ribbon to create a new namespace. This will bring up the Create a new Service Namespace dialog. The same namespace can be used for Access Control, Service Bus, and Cache, but here we need only the Service Bus.
  4. Enter a unique namespace name; the resulting Service Bus endpoint will be uniquenamespacename.servicebus.windows.net, which being a public endpoint must be unique across the internet.
  5. Select the Windows Azure data center you wish to host your service.  As of this writing, the East and West US data centers do not yet support the Service Bus functionality, so you’ll see only six options.
  6. Create the namespace.  This will set up a Service Bus namespace at uniquenamespacename.servicebus.windows.net and an Access Control Service namespace at uniquenamespacename-sb.accesscontrol.windows.net. A service identity is also created with the name of owner and the privileges (claims) to manage, listen, and send (messages) on that Service Bus endpoint.

Access Control Service for the Service Bus

Controlling Access to the Service Bus namespace

Authentication and authorization of the Service Bus is managed by the Windows Azure Access Control Service (ACS) via the namespace (the one with a –sb suffix) that is set up when you create the Service Bus namespace.  You can manage the associated ACS namespace from the Window Azure portal by highlighting the desired Service Bus namespace and selecting the Access Control Service option from the ribbon.

That will bring you to yet another part of the portal with a number of somewhat daunting options:

  • Trust relationships is where you configure identity providers, such as Windows Live ID, Google, Yahoo!, Facebook, or Active Directory Federation Services 2.0.  You can use these providers to authenticate and authorize access to the Service Bus endpoint.  The Service Bus endpoint itself is a relying party application, namely an application that is secured by ACS.  Each relying party application has at least one set of rules associated with it; these rules essentially define what operations a given identity can perform on the relying party application. 

    For a Service Bus endpoint, there are three operations that can be carried out:
    • Send – send messages to an endpoint, queue or topic.
    • Listen – receive messages from an endpoint, queue, or subscription.
    • Manage – set up queues, topics, and subscriptions.
  • Service settings is where you manage certificates and keys as well as service identities that are not managed by other identity providers (like Live ID or Google). For the Service Bus, the ACS automatically manages the certificates and keys (so you don’t ever worry about that part), and it also creates a service identity with the name owner and claims that allow owner to perform all three of the operations (send, listen, and manage). You can think of owner as the sa, root, or superuser of the ACS namespace, and from that it should follow that you’ll rarely – if ever – want to use owner as part of your application. Instead you should create additional user(s) and apply the principle of least privilege.
  • Administration is where you control who can administer the ACS endpoint.  The default administrator will be the Service Administrator of the subscription under which the namespace was created.  That last statement has specific implications when co-administrators of an Windows Azure subscription are in play.  The co-admin can create the Service Bus namespace, but she cannot manage its access and will be greeted by the rather cryptic error below if she tries:

The remedy is to add the co-admin as a portal administrator, by logging into the ACS portal with the identity of the subscription’s Service Administrator and adding the Live ID of the co-admin:

Adding co-admin for ACS

  • The Development section provides guidance and code snippets to integrate the ACS ‘magic’ into your applications.

Adding a new Service Identity

So what’s the workflow to granting access to a new user?  There are two different answers here, one for creating a service identity and the other for handling identity federation via a third-party identity provider (like Live ID).  We’ll tackle the first (and easier) case here, and we’ll look at the federation scenario in a subsequent post.

First you need a new service identity,so select the Service identities option on the sidebar. (Note that the auto-generated owner identity is in place)

Service identity listing

Select the Add link, and create a new identity:

Add service identity

  1. Specify the user identity, use ‘guest’ here to mesh with the default identity assumed by the sample code.
  2. Provide an optional description visible only in the portal.
  3. Select “Symmetric Key” for the credential type. There are two other options: Password and X.509 Certificate, but both require a bit more code and configuration to implement.  Consult the ACS samples on CodePlex for scenarios that make use of these alternative credential types.
  4. Generate a key for the guest identity.  You’ll also need to add this key value in the web.config file for the RelayServiceClient application.  While you’re at it, you may as well change the SBNamespace value to the Service Bus namespace you just created.

    web.config setting

  5. Save the new identity. By default the credentials will expire in a year.

Adding Authorization Claims for the New Service Identity

What we’ve done so far is create a new service identity with the name guest, but that identity has no privileges, so if you try using it (with code we haven’t quite looked at yet), you’d get an HTTP 403 (Forbidden) error.  That’s in contrast to a 401 (Unauthorized) error that you would get when attempting to access the Service Bus with a non-existent user.  So let’s set the guest user up with the ability to make calls against the Service Bus namespace.

First let’s create a new rule group. Strictly speaking we could just use the default group (which includes the authorization rules for owner), but a new group can provide a bit more flexibility and reinforce that the owner identity is “special.”

Rule groups

Enter a name for the group:

Add rule group

After you save the new group, a list of all of the rules associated with this group appears.  Of course, there are none yet, so you’ll need to add at least one.  There is an option to generate rules based on identity providers you’ve associated with the Service Bus namespace, but we’re using service identities, so you’ll need to use the Add link to set up the authorization rules.

Edit rule group

What you’ll be adding here is a new claim rule.  The concept behind claims is actually rather simple, although the user interface belies that!  A claim is some statement about a user or identity that is vouched for by a given identity provider. For instance, when you successfully login via your Google ID, your application is passed back a list of claims associated with your identity that Google is willing to vouch for, so if your relying party application is willing to trust Google, then it can trust the bits of information that are returned as claims. 

Claims can be just about any statement or fact about the user/identity that the identity provider is storing and willing to share.  Google, for instance, will provide a name, nameidentifier, and email address as claims; Windows Live ID provides only a nameidentifier (a tokenized version of your Live ID).  The types and number of claims emitted by different identity providers vary, and that’s where claim rules come in.  You want to insulate your relying application from the vagaries of all the identity provider options, so rather than tapping into the raw claims that each of those providers sends, you set up claims that are meaningful to your application and then map the various incoming claims to the contextual claims your application understands. 

In this case, the relying party is the Service Bus, and it understands three claims: Send, Manage, and Listen.  Our specific goal though is to allow the guest identity only the capability to Send messages on the Service Bus, not manage or listen, so you’ll need to create one claim that authorizes that function. Each claim rule is essentially an IF-THEN statement:.

Add claim rule

  1. The input claim issuer refers to the identifying party.  The dropdown contains whatever identity providers you have enabled for the ACS controlling the Service Bus endpoint (via the Identity providers option under Trust Relationships in the left sidebar (not shown)).  In this case, we’re using a service identity (guest) that is managed by the Access Control Service.
  2. The input claim type refers to one of potentially many claims that the identity provider is willing to offer up about the user/identity that it has authenticated.  The list of claims relevant to the selected provider is prepopulated.  For ACS-controlled service identities, the user name is provided as a nameidentifier claim; you’ll need to consult your identity provider’s documentation to determine what each claim type actually contains.  Claims that aren’t pre-populated can be specified manually in the Enter type: text box.  If you only care that the identity provider authenticates a user, you can select Any for the claim type, which simply indicates you don’t care about the specifics of the claims.
  3. To complete the “IF statement,” you specify what the specific claim value should be in order to fire the rule. If you don’t care about the specific value a claim has (and only that the identity provider offers up the given claim), you can specify Any here.  In our case, the requestor must be guest, so that’s added as a specific value.  If you have multiple other identities that you want to allow access, you would create additional rules.  And if you have more complex rules (with “AND” conditions) you can add a second input claim as a compound condition to govern the claim rule.

So that’s the IF part!  Now you need to map that input claim to output claims that the relying party (Service Bus) is interested in.  We don’t really care at this point that the user’s name is guest, but we do want to know what that user is allowed to do. Granted you could do all that checking in code – if (user == ‘guest’ ) then let user call methods, etc. – but that’s horribly hard-coded and very difficult to maintain from a code and security management perspective (what happens if the guest password is compromised? how do you revoke access?)  So what you’ll do instead is map the input claim to an output claim, namely one that gives Send privileges to guest.

Claim rule output

  1. The output claim type needed here is unique to the Service Bus, so the various options in the drop down aren’t applicable, and we specify the type net.windows.servicebus.action explicitly.
  2. There are three possible values: Send, Listen, and Manage; of course, we want just Send for guest.
  3. You can specify a description for the claim here; it will appear in the list of rules for the given Rule Group.  It’s a good idea to put something meaningful here, because the Rule Group page listing will show only the output claim type.  If you have multiple claims for different action values, they won’t otherwise be distinguishable in the listing.
  4. Save the rule!

Rule group after adding new Send rule

Note that the Used by the following relying party application text box is still empty; that’s because we haven’t associated this rule group with the relying party (the Service Bus).  There’s essentially a many-to-many relationship between relying party applications and the rule groups, so we’ll need to revisit the Relying party applications entry screen to associate the new rule group with the Service Bus.  Note that the default rule group created (for owner) is already in selected.

Associating rule group with relying party application

The sample code associated with this blog series assumes that you’ve created a service identity with the name guest and provided the claim rule as detailed above. The code also requires a second service identity with the name wpfsample that has an output claim rule of Listen; that is the user that initiates listening on the Service Bus endpoint within the WPF client application.  You can follow the same steps above to create this second identity and associate it with a new (or existing rule group).  You can use the default owner as well, but per the earlier recommendation, it’s best to reserve that service identity for administrative use and not incorporate it into your application logic or configuration.

Locking down the Service Bus with ACS was a admittedly a bit of work and somewhat of a diversion from our real goal (that’s likely why so many example just use owner, even though they shouldn’t!). Armed with the new identities guest and wpfsample and their requisite claims, let’s next look at the code for using the Service Bus endpoint instead of the WCF endpoint we started with.

Modifying WPF ServiceHost Implementation to Use Service Bus Relay

Here’s the code to have the WPF application listen on the Service Bus endpoint versus setting up a WCF endpoint on premises:

   1:  ServiceHost host = default(ServiceHost);
   2:   
   3:  // ServiceBusEndpoint
   4:  host = new ServiceHost(typeof(EchoService));
   5:  host.AddServiceEndpoint("RelayService.Interfaces.IEchoContract",
   6:      new BasicHttpRelayBinding(),
   7:      ServiceBusEnvironment.CreateServiceUri("https",
   8:          Properties.Settings.Default.SBNamespace,
   9:          "EchoService"));
  10:             
  11:  // Add the Service Bus credentials to all endpoints specified in configuration.
  12:  foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
  13:  {
  14:      endpoint.Behaviors.Add(new TransportClientEndpointBehavior()
  15:          {
  16:              TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(
  17:                  "wpfsample",
  18:                  Properties.Settings.Default.SBListenerCredentials)
  19:          });
  20:  }

It starts out much like the pure WCF sample, but there are some notable differences:

  • Line 6:  we use a new binding type, BasicHttpRelayBinding, which you can think of as the BasicHttpBinding tuned and adapted for the Service Bus.  There are a number other Service Bus bindings that correspond to traditional on-premises WCF bindings, as well as some specific to the cloud environment.
  • Lines 7-9: the CreateServiceUri method is the preferred mechanism for constructing the Service Bus endpoint URI; however, in this case, you could simply construct a string like “https://yoursbnamespace.servicebus.windows.net/EchoService.” The SBNamespace property in Line 8 refers to a setting you provide in the WPF application (App.config) which specifies the name of the Service Bus namespace you created earlier.
  • Lines 12 – 20 is where the Service Bus credentials are added to the endpoints exposed by the Service Bus.  Line 17 shows the name of the service identity (hardcoded to wpfsample), and Line 18 specifies the symmetric key associated with that service identity in the ACS portal.  You’ll need to copy and paste the credential from the portal to the SBListenerCredentials setting in the App.config file of WPF application (or via the properties dialog in Visual Studio).

And that’s it, now when the Start Service button is clicked (and the Use Service Bus checkbox selected on the WPF UI), the client will open a relay point in whatever Windows Azure data center hosts that Service Bus endpoint.  Depending on the location, you may notice a few seconds of latency as the connection is established.

Now that the WPF application is listening for requests on the endpoint, let’s send some messages via the ASP.NET client.

Targeting the Service Bus Endpoint in the ASP.NET Client

   1:  // ServiceBus endpoint constructed using App.config setting
   2:  ChannelFactory<IEchoContract> myChannelFactory =
   3:      new ChannelFactory<IEchoContract>(
   4:          new BasicHttpRelayBinding(),
   5:          new EndpointAddress(ServiceBusEnvironment.CreateServiceUri(
   6:              "https", 
   7:              ConfigurationManager.AppSettings["SBNameSpace"], 
   8:              "EchoService")));
   9:   
  10:  // add credentials for user (hardcoded in App.config for demo purposes)
  11:  myChannelFactory.Endpoint.Behaviors.Add(
  12:      new TransportClientEndpointBehavior()
  13:      {
  14:          TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(
  15:              userName, ConfigurationManager.AppSettings["SBGuestCredentials"])
  16:      }
  17:  );
  18:   
  19:  // traditional WCF invocation
  20:  IEchoContract client = myChannelFactory.CreateChannel();
  21:  client.Echo(txtUser.Text, txtMessage.Text, ddlColors.SelectedItem.Text);
  22:   
  23:  ((IClientChannel)client).Close();
  24:  myChannelFactory.Close();

Above is the code the client (ASP.NET) application uses to send a message to the Service Bus to be relayed to the WPF application hosted ‘on-premises.’ You can run the Web site locally (by hitting F5 in Visual Studio) and you’ll still be exercising the Service Bus in the cloud; for more fun though, you may want to deploy the ASP.NET application as a Windows Azure cloud service so you can have others bring up the site and send message to your local machine.  A cloud services project is provided for you, but you’ll need to configure the publication settings for your own cloud account.

  • Lines 2-8 set up the client ChannelFactory.  Like in the WPF application, BasicHttpRelayBinding is used, and the Service Bus endpoint is constructed via the CreateServiceUri method.  You’ll need to modify the app.config file to provide your Service Bus endpoint name as the SBNamespace setting.
  • Lines 11-17 add the Service Bus credentials for the identity passed in via userName (by default it’s the value guest). The credential key, which you configured in the ACS portal earlier, needs to be provided in the app.config file as the SBGuestCredentials setting.
  • Lines 18-24 are no different from the traditional WCF invocation; here the Echo method is invoked passing in the values provided by the user in the ASP.NET form.

Give it a Spin!

At this point, you’re (finally) ready to run the sample.  Once you’ve loaded the solution into Visual Studio, you can just hit F5 to run it.  The ASP.NET web site and the WPF application are set to be the startup projects, so you won’t (and don’t) need to run the Windows Azure emulator to see the Service Bus in action.  Here’s a play-by-play of the user interaction; keep in mind that the use-case here is two different machines on completely different networks, separated by firewalls.

  1. Check the Use Service Bus checkbox; this will have the ServiceHost within the WPF application open an endpoint on the Service Bus (using the namespace you’ve set up, the name of which you’ve also specified the App.config file).
  2. Click the Start Service button (it won’t say Stop Service until the service is running).  If this fails, there’s likely some problem with the wpfsample service identity you set up (you did that one up, right?)
  3. In the ASP.NET client, pick your favorite color from the list.
  4. Enter the message you want to send via the Service Bus.
  5. Select the option to invoke the message via the Service Bus endpoint, versus the local WCF endpoint.
  6. Send the message, and….
  7. You should see the message box appear on the same machine that’s running the WPF application.

A working example is great, but I tend to learn more from applications that fail!  Here are some scenarios to consider:

  • Try to send a message under the user name of “joe”.  You should see a 401 error displayed at the top of the page because there is no such identity known to the Service Bus.
  • Specify a user name of “owner.”  owner does exist (you got that free when the namespace was created), but the credentials you put into the app.config won’t match.  You’ll likewise get a 401 error.
  • Specify a user name of “wpfsample” and change the credentials in app.config to be the symmetric key associated with wpfsample (you can get the value from the ACS portal). You should see a FaultException indicating the Send action is not permitted; that’s because wpfsample only has Listen privileges.
  • Stop the service via the button on the WPF application, and try to send a message. In this case the client gets either a FaultException, with the message “No services is hosted at the specified address”, or a ProtocolException and buried within that exception’s message is an indication that there were no active listeners registered for the endpoint.

That last bit of behavior – where the entire operation depends on both the client and the server being up and listening at the same time – can be a bit constraining, especially if you have independent entities chatting across the Service Bus.  What if one of the parties is down for maintenance?  It’s incumbent on the sender to catch the faults and retry later or provide some other remediation.  Wouldn’t it be great if you could guarantee the messages would arrive and be processed (asynchronously) even if both parties weren’t ‘on line’ at the same time?  Well you can, and that’s where Service Bus queues come in – the topic of the next installment in this series.

Join the Conversation
Leave a Comment
  • Please add 1 and 7 and type the answer here:
  • Post
Read What Other's Think
  • Can't it work if I use owner identity instead of creating a new one? like 'guest' in this sample.

  • Robin, yes it will work with "owner", since that identity has the manage, listen, and send claim. I created another identity to emphasize that you don't generally want to use "owner" for any application interaction - it's akin to a "sa" for SQL Server. Unfortunately, a lot of the sample code you'll see does use "owner," and I'm an advocate of the principle of least privilege :)

  • I've tried with javascript node.js and it doesn't work. guest has only Listen rule why?

    I post my code:

    azure = require('azure')

    process.env.AZURE_SERVICEBUS_NAMESPACE = ""

    process.env.AZURE_SERVICEBUS_ACCESS_KEY = ""

    serviceBusService = azure.createServiceBusService()

    subscription = ""

    topic = ""

    serviceBusService.receiveSubscriptionMessage(topic, subscription, function(error2, serverMessage){

    if(!error2){

            console.log(serverMessage);

    }

    else

    {

    console.log(error2);

    }

    });

    I've removed credentials, subscription and topic name

Page 1 of 1 (3 items)