Andrew Connell [MVP SharePoint]
1725 Posts |  46 Articles |  5277 Comments
.NET  |  MCMS  |  SharePoint  |  Office System
AC's Blog Quick Links
SharePoint Quick Links
Article Categories
Archives
January, 2012 (11)
December, 2011 (6)
November, 2011 (3)
October, 2011 (17)
September, 2011 (8)
August, 2011 (6)
July, 2011 (7)
June, 2011 (13)
May, 2011 (9)
April, 2011 (15)
March, 2011 (1)
February, 2011 (6)
January, 2011 (5)
December, 2010 (11)
November, 2010 (6)
October, 2010 (12)
September, 2010 (5)
August, 2010 (4)
July, 2010 (5)
June, 2010 (6)
May, 2010 (11)
April, 2010 (11)
March, 2010 (9)
February, 2010 (9)
January, 2010 (3)
December, 2009 (10)
November, 2009 (15)
October, 2009 (15)
September, 2009 (7)
August, 2009 (4)
July, 2009 (10)
June, 2009 (8)
May, 2009 (2)
April, 2009 (9)
March, 2009 (6)
February, 2009 (16)
January, 2009 (6)
December, 2008 (12)
November, 2008 (12)
October, 2008 (27)
September, 2008 (13)
August, 2008 (14)
July, 2008 (14)
June, 2008 (12)
May, 2008 (23)
April, 2008 (12)
March, 2008 (15)
February, 2008 (13)
January, 2008 (12)
December, 2007 (10)
November, 2007 (8)
October, 2007 (15)
September, 2007 (20)
August, 2007 (21)
July, 2007 (16)
June, 2007 (8)
May, 2007 (25)
April, 2007 (16)
March, 2007 (18)
February, 2007 (18)
January, 2007 (12)
December, 2006 (16)
November, 2006 (13)
October, 2006 (18)
September, 2006 (22)
August, 2006 (27)
July, 2006 (23)
June, 2006 (23)
May, 2006 (23)
April, 2006 (9)
March, 2006 (17)
February, 2006 (15)
January, 2006 (23)
December, 2005 (31)
November, 2005 (32)
October, 2005 (38)
September, 2005 (53)
August, 2005 (30)
July, 2005 (63)
June, 2005 (30)
May, 2005 (59)
April, 2005 (29)
March, 2005 (74)
February, 2005 (27)
January, 2005 (22)
December, 2004 (32)
November, 2004 (42)
October, 2004 (39)
September, 2004 (20)
August, 2004 (14)
July, 2004 (27)
June, 2004 (40)
May, 2004 (5)
April, 2004 (6)
March, 2004 (16)
February, 2004 (26)
January, 2004 (23)
December, 2003 (7)
November, 2003 (14)
October, 2003 (20)
September, 2003 (4)
Post Categories
Add to Technorati Favorites

Managed Windows Shared Hosting
Saturday, January 21, 2012

When I saw the presser months ago with Jeff Bezos, someone I highly respect in this business and one of the few true visionaries, I jumped and did what I try not to do and make an impulse purchase on something that expensive. When I got it I loved it, gave it a few days and wrote a review I posted here. Now after two months, I wanted to post some more thoughts.

First, I still really like the device and I’m glad I got it. But I have to say that I don’t love it. You can’t beat the price for what you get, but there are a few aspects to it that I would need to see resolved for it to be a good iPad killer:

  • Performance – I’d gladly pay a little more to have a better processor in it. The keyboard is sluggish, too sluggish to take notes with or be active on email. Even with the bigger screen, I’d rather use my phone to do mobile messaging. At first I thought this was a software issue, but maybe it isn’t as Amazon hasn’t fixed it. I don’t notice the sluggishness in all apps so that says software to me.
  • Storage – Again, I’d pay more for more space… even if it was a removable card. I like to put movies on my tablet to watch when I travel. Streaming from Amazon Prime isn’t going to solve that “want”. You pay for metered bandwidth if you travel (I have a Verizon Mifi 4GLTE hotspot, but I don’t want to use 80% of my monthly usage on movies… I use it for work). Hotel connectivity sucks… even the internet connection in this airport lounge I’m in as I write this sucks (Paris’s Charles de Gaul airport).
    • I have ripped some movies and put them on my Kindle Fire, but I only have room for one or two at a time unfortunately. But it does play them great!
  • All Cloud – This ties into storage. I like the idea of having my stuff in the cloud… hell I love the cloud… but I want to sync my stuff down to my device. You aren’t always connected when you want your content or want to pay through the nose for bandwidth.
  • Reading – I put a ton of work related PDF books and articles on it… and for that it’s great. But if I’m reading a novel, I’m using my Kindle 2nd Generation and the eInk. Nothing surpasses that for quality reading.
  • Battery – Sure isn’t nearly as good as the regular Kindle due to the great screen… but I expected more after having an iPad for a while.

The other thing I love about the Kindle Fire is the form factor. I pickup an iPad now and it feels massive… way too big.

The most glaring thing that the Kindle Fire has shown me is how crappy the Android OS is. This feels like it is so clobbled together… apps freeze, you have to force close them, the UI sucks… I’m not a fan. Microsoft and Apple have it right when their mobile OS’. I’m more of a fan of Windows Phone than iOS, but maintaining control and blocking customization of the OS is definitely the way to go.

Is it an iPad replacement? Maybe for some… maybe for me (see above: I haven’t picked it up since I got the Kindle Fire). I just can’t get past the fact the entry point for an iPad is $500 and goes up to $830! Sorry Apple, but that’s absurd… the iPad isn’t 2x as good (when you factor in the closest model to the Fire… WIFI only & 16GB @ $499) price wise as the Kindle Fire.

All in all, it’s a great device and you’re getting what you pay for: a very inexpensive & versatile tablet.

I do like the Kindle Fire more than my iPad (1st generation)… in fact I haven’t picked up my iPad since I got the Kindle Fire (and I don’t miss it). It’s turned into the family iPad and my son uses it the most. I’m very eager, and will get, a tablet based on Windows 8 when it comes out mid year. Not saying Windows 8 will be great, just that it will be interesting how well it, and the devices that are out for it, executes. So far, I like what I see.

So for me, Kindle Fire > iPad… but we’ve yet to hit that tablet sweet spot IMHO.

Friday, January 13, 2012

A month or so ago I blogged about a new offering we have at Critical Path Training, a 2-day SharePoint 2010 Developer Roadshow. Our first delivery will be in Boston, February 15-16, 2012 at the Museum of Science.

After the first day of the event (February 15) head over to Mr. Dooley’s Boston Tavern for drinks and to shoot the breeze … it’s a SharePint after all!

Registration for the event runs $599 but if you register before the end of the weekend (January 15) you’ll get $100 off to make it $499!

Wednesday, January 11, 2012

This post is part of a series on Office365/SharePoint Online, Windows Azure and Authentication. The other posts in this series are as follows:

In this last post in my Office365/SharePoint Online + Windows Azure + Authorization blog series, I want to introduce a little helper project I am using. To make life easier I created a little O365 authorization helper library that does a lot of the heavy lifting for you. It covers two of the three things I outlined in my series as workarounds.

Code samples I show in this post were taken from a code sample CPT's Office365/SharePoint Online Claims Authentication Helper Library… you can get the code from the Critical Path Training site’s Members sectionlook in the Code Samples section.

Introducing the Claims Auth Friendly ClientContext: ClaimsClientContext

First it creates a special Client Site Object Model (CSOM) ClientContext object. This object has a few properties needed for authenticating with Microsoft Online (MSO) to obtain the SAML token. It then rewrires the ClientContext so that every request includes the SAML token:

   1: namespace CriticalPathTraining.Office365.AuthLibrary {
   2:   public class ClaimsClientContext : ClientContext {
   3:     public string MsoUsername { get; set; }
   4:     public string MsoPassword { get; set; }
   5:     public string MsoRootSiteCollectionUrl { get; set; }
   6:  
   7:     public ClaimsClientContext(string webFullUrl) : base(webFullUrl) { }
   8:     public ClaimsClientContext(Uri webFullUrl) : base(webFullUrl) { }
   9:  
  10:     private MsOnlineClaimsHelper _claimsHelper;
  11:     /// <summary>
  12:     /// Microsoft Online claims helper used to authenticate to 
  13:     /// SharePoint Online.
  14:     /// </summary>
  15:     private MsOnlineClaimsHelper MsftOnlineClaimsHelper {
  16:       get {
  17:         if (_claimsHelper == null) {
  18:           _claimsHelper = new MsOnlineClaimsHelper(
  19:                                     MsoUsername, 
  20:                                     MsoPassword, 
  21:                                     MsoRootSiteCollectionUrl);
  22:         }
  23:         return _claimsHelper;
  24:       }
  25:     }
  26:  
  27:     /// <summary>
  28:     /// Rewire event for client context so that 
  29:     /// every request includes authenticated cookies.
  30:     /// </summary>
  31:     protected override void OnExecutingWebRequest(WebRequestEventArgs args) {
  32:       args.WebRequestExecutor.WebRequest.CookieContainer = 
  33:         MsftOnlineClaimsHelper.CookieContainer;
  34:     }
  35:   }
  36: }

Usage is very simple… the downloadable library includes a test project that shows the usage:

   1: [TestMethod]
   2: public void CliamsClientContextTest() {
   3:   using (var context = new ClaimsClientContext(MSO_SPSITE_URL) {
   4:     MsoUsername = MSO_USERNAME,
   5:     MsoPassword = MSO_PASSWORD,
   6:     MsoRootSiteCollectionUrl = MSO_ROOT_SPSITE_URL
   7:   }) {
   8:     // get the web
   9:     var web = context.Web;
  10:     context.Load(web, w => w.Title);
  11:     context.ExecuteQuery();
  12:  
  13:     Assert.IsNotNull(web.Title);
  14:     Assert.IsTrue(web.Title.Length > 0);
  15:  
  16:     Console.WriteLine("Retrieved site title:" + web.Title);
  17:   }
  18:  
  19: }

Introducing the Claims Friendly Web Client: ClaimsWebClient

The other thing I give you is a special version of the WebClient class that’s makes working with claims a bit easier. It has a single property where you specify the CookieContainer that will contain the SAML token. The library exposes the samples Wictor Wilen provided on to do the authentication for you and generate the CookieContainer:

   1: namespace CriticalPathTraining.Office365.AuthLibrary {
   2:   public class ClaimsWebClient : WebClient {
   3:     /// <summary>
   4:     /// Cookies that should be included on every Web request.
   5:     /// </summary>
   6:     public CookieContainer CookieContainer { get; set; }
   7:  
   8:     /// <summary>
   9:     /// Override base GetWebRequest() method to always include 
  10:     /// cookies if they were specified.
  11:     /// </summary>
  12:     protected override WebRequest GetWebRequest(Uri address) {
  13:       var request = base.GetWebRequest(address);
  14:       if (request is HttpWebRequest && CookieContainer != null)
  15:         ((HttpWebRequest)request).CookieContainer = CookieContainer;
  16:  
  17:       return request;
  18:     }
  19:   }
  20: }

In the associated test project you’ll also find the usage for this as well:

   1: [TestMethod]
   2: public void ClaimsWebClientTest() {
   3:   // file to download
   4:   string fileToDownload = "/_layouts/images/siteIcon.png";
   5:  
   6:   var claimsHelper = new MsOnlineClaimsHelper(
   7:                             MSO_USERNAME, 
   8:                             MSO_PASSWORD, 
   9:                             MSO_ROOT_SPSITE_URL);
  10:   using (var webClient = new ClaimsWebClient() {
  11:     CookieContainer = claimsHelper.CookieContainer
  12:   }) {
  13:     // get the file
  14:     var fileStream = ((ClaimsWebClient)webClient).OpenRead(
  15:         string.Format("{0}{1}", MSO_SPSITE_URL, fileToDownload)
  16:     );
  17:         
  18:     // download & write local        
  19:     string tempFilePath = Path.GetTempFileName();
  20:     var tempFile = File.Open(tempFilePath, FileMode.OpenOrCreate);
  21:     fileStream.CopyTo(tempFile);
  22:     fileStream.Close();
  23:     tempFile.Close();
  24:  
  25:     Console.WriteLine("Downloaded file to:" + tempFilePath);
  26:  
  27:     // make sure file exists & bigger than 0 bytes
  28:     Assert.IsTrue(File.Exists(tempFilePath));
  29:     var fileInfo = new FileInfo(tempFilePath);
  30:     Assert.IsTrue(fileInfo.Length > 0);
  31:   }
  32: }

Last but not least, for completeness I threw in a test for working with any of the SharePoint *.ASMX or *.SVC Web services. You don’t need any special helpers here as they include a CookieContainer class already:

   1: [TestMethod]
   2: public void WebServiceTest() {
   3:   XmlNode results;
   4:  
   5:   var claimsHelper = new MsOnlineClaimsHelper(
   6:                             MSO_USERNAME, 
   7:                             MSO_PASSWORD, 
   8:                             MSO_ROOT_SPSITE_URL);
   9:   using (var client = new Lists() {
  10:     Url = string.Format("{0}_vti_bin/Lists.asmx", MSO_SPSITE_URL),
  11:     UseDefaultCredentials=true,
  12:     CookieContainer = claimsHelper.CookieContainer
  13:   }) {
  14:     results = client.GetList("Shared Documents");
  15:   }
  16:  
  17:   Assert.IsNotNull(results);
  18: }

This post is part of a series on Office365/SharePoint Online, Windows Azure and Authentication. The other posts in this series are as follows:

Now let's see how we can address the authentication fixes for each of the different ways you can access SharePoint remotely. In this post I'll cover each of the specific tools (REST or OData / CSOM / Web Services / WebClient) and how to address each of the tricks. Each one has it's pros & cons, hence why I had to use all four tools in my demo in my breakout session Out of the Sandbox and into the cloud: Build your next SharePoint app on Azure at the Microsoft SharePoint Conference 2011 (see that link for where you can download the sample).

Any code samples I show in this post were taken from my session Out of the Sandbox and into the cloud: Build your next SharePoint app on Azure at the Microsoft SharePoint Conference 2011… you can get the demo code from the Critical Path Training site’s Members section… look for the AC's SharePoint Conference 2011 Sessions download in the Presentations section.

For all the samples below, I created a private property in my class called MsftOnlineClaimsHelper that creates a local instance of the MSO helper and automatically authenticates.

   1: private MsOnlineClaimsHelper _claimsHelper;
   2: /// <summary>
   3: /// Microsoft Online claims helper used to authenticate to SharePoint Online.
   4: /// </summary>
   5: private MsOnlineClaimsHelper MsftOnlineClaimsHelper {
   6:   get {
   7:     if (_claimsHelper == null) {
   8:       _claimsHelper = new MsOnlineClaimsHelper(
   9:                 RoleEnvironment.GetConfigurationSettingValue("SharePointUsername"),
  10:                 RoleEnvironment.GetConfigurationSettingValue("SharePointPassword"),
  11:                 RoleEnvironment.GetConfigurationSettingValue("SharePointRootSiteUrl"));
  12:     }
  13:     return _claimsHelper;
  14:   }
  15: }

CSOM Client Context & CBA Challenges

One of the most common ways to work with SharePoint 2010 from off the SharePoint server is using the CSOM. Authentication with the CSOM is pretty straight forward using the ClientContext object. The trick comes into play with claims based authentication (CBA).

When you want to switch to FBA it's a simple property switch on the ClientContext, but as I previously stated there is no such way to do this for CBA. What you need to do is rewire the ClientContext so that every request it makes to a site collection includes a SAML token to authenticate the request. You do this by trapping the ExecutingWebRequest event of the ClientContext and injecting the cookie container generated by the MSO helper into all requests:

   1: private ClientContext _clientContext;
   2: /// <summary>
   3: /// CSOM client context.
   4: /// </summary>
   5: private ClientContext CsomClientContext {
   6:   get {
   7:     if (_clientContext == null) {
   8:       _clientContext = new ClientContext(
   9:         RoleEnvironment.GetConfigurationSettingValue("SharePointSiteUrl")
  10:       );
  11:  
  12:       // wire up claim helper to include SAML tokens (cookies) in all requests
  13:       _clientContext.ExecutingWebRequest += 
  14:             (webRequestSender, args) => {
  15:                 args.WebRequestExecutor.WebRequest.CookieContainer 
  16:                     = MsftOnlineClaimsHelper.CookieContainer;
  17:             };
  18:     }
  19:     return _clientContext;
  20:   }
  21: }
Now, almost all requests the ClientContext make will include the SAML token! I say "almost" because there is a bit of an issue with the ClientContext. There is a method called File.OpenBinaryDirect() that you can use to download a file from SharePoint. For some reason this method doesn't raise the same ExecutingWebRequest event so your token isn't handled! Ouch… oversight in the API me thinks… regardless, you can get around this using a stock Web Client...

Web Request & CBA Challenges

The way you can address the lack of passing along the SAML token to SPO when you try to open and download a file using the File.OpenBinaryDirect() method is to simply create a simple Web request that will download the file. However this process also needs a little bit of work to pass along the SAML token. What I did was create a custom version of the WebClient class that did this for you as follows:

   1: namespace AndrewConnell.ACsCichlids.StoreFront {
   2:   public class ClaimsFriendlyWebClient : WebClient {
   3:     private CookieContainer _cookieContainer;
   4:     public CookieContainer CookieContainer {
   5:       get { return _cookieContainer; }
   6:       set { _cookieContainer = value; }
   7:     }
   8:  
   9:     protected override WebRequest GetWebRequest(Uri address) {
  10:       var request = base.GetWebRequest(address);
  11:       if (request is HttpWebRequest)
  12:         ((HttpWebRequest)request).CookieContainer = _cookieContainer;
  13:  
  14:       return request;
  15:     }
  16:   }
  17: }

This method is handy when you want to download a file from a site collection. To use it you simply pass in the MSO helper's cookies and they will be included on all requests:

   1: using (var webclient = new ClaimsFriendlyWebClient() 
   2:     { CookieContainer = MsftOnlineClaimsHelper.CookieContainer }
   3:   ) {
   4:       // download file into a memory stream
   5:       var fileStream = ((ClaimsFriendlyWebClient)webclient).OpenRead(cichlidPicture.OriginalUri);
   6:  
   7:       // create & save blob
   8:       var blob = AzureStorageContainer.GetBlobReference(cichlidPicture.ImportedFilename);
   9:       blob.UploadFromStream(fileStream);
  10: }

REST / OData / Web Services & CBA Challenges

My preferred way to read/write data to SharePoint lists is using the RESTful OData service ListData.svc. This service, like all the other Web services that are included with SharePoint 2010 (*.ASMX & *.SVC), don't understand claims by default. When you want to authenticate for a Windows or FBA site you have to create a network credential object and set it as a property on the service proxy.

However this isn't available when it comes to authenticating with CBA. Like the ClientContext, you need to rewire the calls to make sure they include the SAML token in each request. This is pretty simple as most services, like the Lists.asmx service, includes a CookieContainer property:

   1: XmlNode results;
   2:  
   3: // call lists.asmx web service to get attachments for each item 
   4: //    use same "cookie container" technique to authenticate
   5: using (var client = new Lists() {
   6:   Url = siteUrl + "/_vti_bin/Lists.asmx",
   7:   UseDefaultCredentials = true,
   8:   CookieContainer = MsftOnlineClaimsHelper.CookieContainer
   9: }) {
  10:   results = client.GetAttachmentCollection(
  11:     RoleEnvironment.GetConfigurationSettingValue("SharePointManagementListName")
  12:     , listItemId.ToString()
  13:   );
  14: }

There you have it... hopefully this series & code samples will help you authenticate into your site collections in SharePoint Online!


This post is part of a series on Office365/SharePoint Online, Windows Azure and Authentication. The other posts in this series are as follows:

In my previous post in this series I talked about how authentication works with Office365 (O365) & SharePoint Online (SPO). You first authenticate with Microsoft Online (MSO) and then your browser passes along the SAML token with each request to site collections in SPO.

The first thing we need to do when accessing SPO programmatically is to understand how we authenticate with MSO and get the SAML token. Thankfully Microsoft provides a sample to help with the authentication piece. What you need is to make sure you have the Windows Identity Framework Runtime installed first. The main class that Wictor Wilen provides for authenticating to MSO is MsOnlineClaimsHelper. This class takes three parameters in the constructor:

  • MSO Username of the account to authenticate with
  • MSO Password of the account to authenticate with
  • URL of the root site collection for your SPO account - note this is the root site collection which may not necessarily be the site collection you are interested in, but it is needed to authenticate and get the key

The MsOnlineClaimsHelper class has a public property, CookieContainer (which is also of type System.Net.CookieContainer), that you'll use to get the SAML token from MSO to attach to future requests. When the CookieContainer property is accessed, if null, the MsOnlineClaimsHelper will go authenticate and parse the results to extract the SAML token and store it in the cookie container.

Check out the code sample I mentioned previously in this post. What you're looking for are two files in the MSDN folder of the ACsCichlids.StoreFront project & the CSOM redistributable. These two files are the aforementioned MsOnlineClaimsHelper.cs & a file it depends on, WcfClientContracts.cs. Copy them into your project and that's all you need in order to authenticate.

Here's just a little code from a simple console app that shows me authenticating into my SharePoint Online site collection:

   1: static void Main(string[] args) {
   2:  
   3:   string msoUsername = "jack.sparrow@aconn.onmicrosoft.com";
   4:   string msoPassword = "[password]";
   5:   string msoRootSpSite = "https://aconn.sharepoint.com/";
   6:  
   7:   var msoHelper = new MsOnlineClaimsHelper(
   8:                           msoUsername,
   9:                           msoPassword,
  10:                           msoRootSpSite);
  11:  
  12:   CookieContainer cookieJar = msoHelper.CookieContainer;
  13:  
  14: }
Tuesday, January 10, 2012

This post is part of a series on Office365/SharePoint Online, Windows Azure and Authentication. The other posts in this series are as follows:

Creating applications in SharePoint 2010, specifically the hosted environment Office365/SharePoint Online, when you want to communicate with applications and services in Windows Azure you are limited to client-side communications or using Business Connectivity Services (BCS). This is because all server-side code must run in the sandbox and that blocks all external web service calls.

What's the Problem?

However when you create an application in Windows Azure, or anything that wants to talk to SharePoint Online, regardless of the tools you use to connect to SharePoint Online (REST or OData / CSOM / Web Services / WebClient) you'll quickly find you are having authentication issues. The reason for this is because SharePoint Online is configured for claims authentication and use Microsoft Online (MSO) for authentication. That's why when you go to your SharePoint Online site, you are sent to what looks similar to a LiveID login page but it is branded for Office 365:

 

So…. what's the problem? The root problem is that none of the tools I mentioned above (REST or OData / CSOM / Web Services / WebClient) understand how to authenticate to a system that's configured for claims based authentication (CBA). Thankfully each one can be rewired a bit as to make it work but it takes a bit of work.

Quick Primer on Claims Based Authentication

If you aren't versed in CBA, let me point to two great links where you can get more information. Check out  MSDN's Patterns & Practices group: An Introduction to Claims but my favorite explanation one is by Donovan Follette's real world  example here. Basically here's how it works, borrowing a picture from the P&P article:

 

The user tries to hit a resource that's protected by CBA such as a site collection in SharePoint Online (SPO). Because the user isn't authenticated, the user is sent to a login page which in this case is Microsoft Online (MSO). The user then logs (#1) in and is given a  token (#2) that's in the format of Security Assertion Markup Language (SAML). The user puts this token in a cookie in the HTTP header and goes back to the SPO site collection (#3). Because the site collection gets the authentication token and the hosting SharePoint installation trusts the source that created the token (using some digital signatures & trust configurations that were setup ahead of time by administrators), the user is allowed in.

OK… So… How Do you Fix the Tools for CBA?

Here's the root of the problem: none of the four tools know how to authenticate with MSO and how to pass the SAML token along to the target system. The light bulb might have just gone off for a few of you… you may have been trying to programmatically authenticate with MSO and then make your request only to get a 401 - Access Denied. The reason you get that is because you aren't passing along your token in the header of your requests… you're being denied because you're trying to hit the resource as an unauthenticated (aka: anonymous) user.

The big picture fix for this is very simple:
  1. Programmatically authenticate with MSO
  2. Parse the response to get the SAML token
  3. Attach the token to all requests to site collections in SharePoint Online
Thankfully a lot of other folks have done a lot of the heavy lifting for us. I don't want to duplicate the work they've done and you should read these. Each of them have various code downloads for things you need (like the Windows Identity Foundation runtime & SDK & the CSOM redistributable (& don't forget about the CSOM redistributable service pack 1 x86 or x64). Almost all of them have associated code downloads filled with helpful stuff.

Up Next…

Now that you understand how the authentication works & the challenges each of our client tools & techniques has with it, we need to take a moment to get past the first step: authenticate with MSO to get a SAML token!


In the previous series I talked about how you can leverage both SharePoint 2010 and Windows Azure to create compelling & powerful integrated solutions. In the introduction post I talked about how I did a session at Microsoft's SharePoint Conference 2011 on integrating the two. In building out the demo for that I ran into some challenges when I tried to have my custom Windows Azure solution talk to and interact with a hosted SharePoint 2010 site collection living in my Office 365 SharePoint Online space. I ran into a few challenging spots when I tried using any of the four remote data access techniques available to developers:

  • REST / OData / WCF Data Services (ListData.svc)
  • Client Side Object Model (CSOM)
  • Web Services (ASP.NET ASMX / WCF SVC)
  • Web Requests (WebClient)

This blog series will talk about some of these challenges and how to overcome them. The last post will introduce a little helper library you can get for free from the Critical Path Training Member's only download site. Let's get to it! 

Friday, January 06, 2012

This post is part of a series of on integrating SharePoint 2010 & Windows Azure. The other posts in this series are as follows:

Now it's time to talk about integration options with SharePoint 2010 & Windows Azure. First let's look at what type of options are available to developers, then we'll talk about the technologies you can use to facilitate this integration.

Integration Options

You can look at the different integration scenarios as three pillars ranging from the most simple to the more complex scenarios. Here's how I presented them in a breakout session I presented at the SharePoint Conference in October 2011:

Let's look at each of these in more detail:

Simple

I admit, this is so easy to do… in fact I referred to it as the "stupid easy simple approach." This approach falls in the category of when a solution can be implemented 100% outside of SharePoint, in other words, it doesn't need to communicate with SharePoint for anything. Maybe this is something as simple as a stock ticker, news feed or weather control.

Consider the following scenario: you need to build something to satisfy a requirement for a project but you're limited to working within the sandbox. The thing you need to build must communicate with some external Web services. This isn't possible in the sandbox because the CAS policy that enforces rules on the sandbox doesn't grant code the WebPermission which is required for making calls to Web services. Sure, you could do the whole thing client side, but that's not the point of this post! This is a perfect case for moving the solution to Azure. So you create a little Windows Azure Web Role that contains a single ASPX page. This page makes the calls out to the Web service and renders what it needs to on the page. To get this page to show up on SharePoint you can create a sandbox friendly Web Part that simply renders an IFRAME pointing to the page in Azure. When the SharePoint page renders, the user won't notice the page is actually showing content from an external Windows Azure site especially if you set the IFRAME to be borderless.

   1: [WebBrowsable(true),
   2:   WebDisplayName("Windows Azure App Url"),
   3:   Personalizable(PersonalizationScope.Shared)]
   4: public string AzureAppUrl { get; set; }
   5:  
   6: protected override void CreateChildControls() {
   7:   string targetUrl = string.IsNullOrEmpty(AzureAppUrl) 
? string.Empty : AzureAppUrl;
   8:  
   9:   HtmlGenericControl iFrame = new HtmlGenericControl("iframe");
  10:   iFrame.Attributes.Add("width", "700");
  11:   iFrame.Attributes.Add("height", "500");
  12:   iFrame.Attributes.Add("frameborder", "0");
  13:   iFrame.Attributes.Add("src", targetUrl);
  14:  
  15:   this.Controls.Add(iFrame);
  16: }

Yes, this is a VERY simple solution and one that's quite obvious, but the simplest ones can also be the best ones to implement especially if they get the job done. Best part is that this will work in an on-premise or hosted SharePoint site such as Office 365 & SharePoint Online.

Moderate:

The next step is to look at a moderate solution for integrating Windows Azure and SharePoint 2010. One option would be to use BCS to expose data from some external source to SharePoint via Azure. This comes in handy when you need to have BCS point to a custom Web service. You can deploy the custom WCF Web service in Windows Azure & use BCS to create an external content type that exposes the data to SharePoint. Best of all, with the November 2011 updates to Office 365, SharePoint Online can now leverage BCS in hosted sites that point to a WCF service using SharePoint Designer 2010 to create the external content types! This is a great no-code or little-code (if you have to create the custom WCF service) solution.

Another option here is to get your hands dirty and crack open Visual Studio. There may be times where you need to write code to consume some data or control a service that is hosted in Windows Azure from SharePoint. Unfortunately when you're in the sandbox you can't make Web service calls from server side code. Thankfully you can accomplish this with some client side code! This option is also easy to implement as you simply need to get some JavaScript on the page. This can be accomplished using the Content Editor Web Part or with SharePoint Designer and creating/editing a site/content page and adding some client side script. This script can call across the wire to a remote service and parse the results. The following snippet shows how to make a call using jQuery to a remote RESTful OData service hosted in Windows Azure:

   1: <script type="text/javascript">
   2: //<![CDATA[
   3:   $(document).ready(function () {
   4:     // when page loads jQuery, get all the lakes...
   5:     $("#StatusMessage").html("<em>Loading lakes...</em>");
   6:     var url = "http://[hosted_service_prefix].cloudapp.net:81
/AfricanCichlids.svc/OriginLakes?$orderby=LakeName"
;
   7:     $.getJSON(url, function (result) {
   8:       for (var i = 0; i < result.d.length; i++) {
   9:         $("#LakeOrigin").append('<option value="' + result.d[i].Id 
+ '">' + result.d[i].LakeName + '</option>');
  10:       }
  11:     }
  12:   }
  13: //]]>
  14: </script>

You can even get pretty complex by passing queries along in the URL since it's a RESTful service like this (this is from my session SPC410 at the Microsoft SharePoint Conference 2011… you can get the demo code from the Critical Path Training site’s Members section… look for the AC's SharePoint Conference 2011 Sessions download in the Presentations section):

   1: // search for specified cichilds
   2: $("#StatusMessage").html("<em>Searching AC's Cichlids Inc. online database...</em>");
   3: var url = "http://[hosted_service_prefix].cloudapp.net:81/AfricanCichlids.svc/AfricanCichlids
?$orderby=CommonName&$filter=(CichlidType/Id eq "
+ $("#CichlidType option:selected").val()
+ ") and (OriginLake/Id eq " + $("#LakeOrigin option:selected").val() + ")";
   4: $.getJSON(url, function (result) {
   5:   if (result.d.length == 0)
   6:     $("#SearchResults").html("<em>No African Cichlids found for the 
specified criteria.</em>"
);
   7:   else {
   8:     // clear results
   9:     $("#SearchResults").html("");
  10:     // write all results
  11:     for (var i = 0; i < result.d.length; i++) {
  12:       $("#SearchResults").html("&raquo; <a target='_blank' 
href='http://acscichlids.cloudapp.net/ProfileFull.aspx
?id="
+ result.d[i].Id + "'>" + result.d[i].CommonName + "</a><br />");
  13:     }
  14:   }
  15:   $("#StatusMessage").html("");
  16: });

Complex

Now for the fun stuff where developers can go wild! In the more complex scenario you can do quite a few things. Now, if you're working within a hosted instance of SharePoint and thus, all your code is running in the sandbox, you're limited to what you can do. This basically comes down to client-side solutions since you can't reach across the wire from server side code in the sandbox. You could do a lot in the client though, either using the JavaScript techniques I showed in the moderate section previously or by doing something with Silverlight.

If you're on premise & can use farm solutions, there are quite a few more things you can do obviously. You're no longer limited to anything and can make calls across the wire to Windows Azure to your heart's content. Another thing you can do is have your custom solution in Windows Azure call back into SharePoint to do various things.

The diagram above shows a sample of different things you can leverage in Windows Azure and a SharePoint integrated solution. As with anything, with custom code you can do just about anything using all the features and capabilities of each platform.

How to talk TO Windows Azure FROM SharePoint

So you have some stuff you've deployed to Windows Azure and you want to access it from SharePoint… how can you do this? Most of the stuff in Windows Azure is exposed via HTTP/HTTPs and is mostly Web service based. You can directly access a SQL Azure database using a standard connection string, but I think it's a better practice to expose it via a standard OData service. Most of the capabilities of Windows Azure, like queues, table storage and blob storage, all have RESTful services you can use. This means you are generally going to connect and access those services using standard web service techniques. Of course you can also create a custom endpoint in Windows Azure and communicate with it, but that's up to you to do.

How to talk TO SharePoint FROM Windows Azure

Let's flip this around… let's say you have an existing SharePoint site, either an on premise deployment that you host or a hosted instance such as SharePoint Online in Office 365. There are a few things SharePoint 2010 offers in the way of remote communication and all have their advantages and disadvantages.

  • RESTful Service / OData / WCF Data Services
    The ListData.svc service enables people to read/write data to SharePoint lists using the standard OData protocal. Personally this is my favorite way to read & write data to SharePoint when I'm not on the server because it's such a standards based way of doing it. It is a bit slower than the next option… check out what the MSDN Patterns & Practices group has done WRT to this area in the SharePoint 2010 Guidance: Data Access for Client Applications.
  • Client Side Object Model (CSOM)
    This is a client abstraction of the server side SharePoint object model. Not only does it give you list read/write capabilities like the OData option, but you can do so much more like creating lists, sites, managing site columns, content types, users, permissions and so much more. Again, check out the P&P link above for performance implications.
  • Web Services
    At times the OData & CSOM options don't give you everything you need. In this case there are times when you can use the web services Microsoft includes with SharePoint 2010 which come in the form of stock *.ASMX style and WCF *.SVC style Web services.
  • Web Request
    And then there are those times when you just want to download a file… in these cases a stock WebClient class is all you need.

This post is part of a series of on integrating SharePoint 2010 & Windows Azure. The other posts in this series are as follows:

While the sandbox gives developers an option to creating custom solutions in the cloud, the fact remains that it can be somewhat limiting. Some tasks developers are used to doing on the server in a fully trusted environment are simply not permitted on the server. For instance developers cannot create custom web services or make calls to custom databases. While these limitations do exist, there are various ways to get around them when in a hosted scenario.

One option is to consider is leveraging Windows Azure, Microsoft's cloud solution. Both Office 365 and Windows Azure run in the same data centers so communication between the two environments is nice and quick. In addition, Windows Azure does not impose the same limitations on custom projects that the sandbox does. In many cases, Windows Azure even adds additional capabilities that are not offered in SharePoint, either on premise or in the cloud.

Microsoft's cloud offering is comprised of various components. The following list is a brief overview of some of the things you'll find in Windows Azure:

  • Windows Azure - The ability to run custom code in the cloud. For instance you can write web applications or services using the .NET Framework (even version 4) or non .NET languages such as php. It also includes storage such as blobs (for files), tables (for data) and message queues.
  • SQL Azure - The ability to create cloud hosted relational databases. Microsoft is adding business intelligence capabilities here as well, such as reporting and analytics.
  • Windows Azure AppFabric - AppFabric is a suite of middleware services you can use to extend your application. It enables your application to securely connect to backend services (ServiceBus), implement caching capabilities (CDN, caching) and authentication (Access Control Service).

The possibilities for how you can leverage SharePoint Online in conjunction with Windows Azure. For example, if you are working with large files, such as video or CAD files, it may be cheaper to store these files in Windows Azure Blob storage than using up your valuable site collection content database space. Depending on the application, you can leverage individual components in Azure or host the entire application within Windows Azure… it just depends on the project!

When exploring creating a project that will have a presence in SharePoint Online as well as one in Windows Azure, there are two things you need to figure out: how will SharePoint Online surface/communicate with Windows Azure and how will Windows Azure communicate with SharePoint Online.

If you haven't picked it up already, the majority of this series will discuss how you can break out of the sandbox limitations and leverage the unrestricted power of Windows Azure in your custom solutions.


This post is part of a series of on integrating SharePoint 2010 & Windows Azure. The other posts in this series are as follows:

Historically SharePoint has been very popular with large organizations because they can shoulder the financial and resource requirements required to deploy SharePoint. However one place where SharePoint hasn't grown as fast is in the small and medium side business (SMB) area. The reason for this is most likely the cost and resource requirements (not just hardware, but people as well) necessary to deploy and maintain it. With Office 365, many of these barriers have been removed and therefore it represents a new era and opportunity to grow the SharePoint customer base. Therefore this is an area you should be very aware of and should learn how you can best leverage and exploit this new and untapped market of possible SharePoint customers!

SharePoint 2010 included a new capability that enabled developers to create custom solutions and deploy them to site collections without involving administrators. These sandbox solutions are limited in some things they can do, as they should be considering they allow developers and site collection owners to deploy code directly to the server without the administrator check and balance. Sandbox solutions are the only way developers can create custom solutions and deploy them to a hosted environment. Microsoft does not allow fully trusted (aka: farm trust) solutions in Office 365 as they can't review every solution nor is it good for the stability of the farm.

As sandbox solutions are the only developer option for custom solutions in Office 365, developers should be aware of what things they can and cannot do in sandbox solutions. The following article on MSDN provide a good set of guidance to understanding what you can and cannot do within the sandbox: SharePoint Online for Office 365 Developer Guide

In addition to the limits imposed by the sandbox, Microsoft has incorporated some additional limits on Office 365. Unfortunately not all of these limitations are documented, but the following article on MSDN does provide good guidance to working in the hosted SharePoint Online environment: Developing SharePoint Online Solutions. In addition to the support Microsoft has provided, the community has developed an open source project on CodePlex that consists of documentation and FxCop rules you can use to check your code before deploying to the sandbox. It includes all the known limitations as well as those limitations found by customers but not yet documented clearly by Microsoft: Office365 Sandbox FxCop Rules.

This message speaks to existing SharePoint developers, but there's a bigger piece to this story. While SharePoint developers, those who understand how SharePoint works (the concepts of site collections, sites, lists, libraries and Web Parts to name a few things), there is another class of class of developers out there who are asked to build things to integrate with a company's SharePoint investments: non-SharePoint developers. This speaks to ASP.NET developers or even those who don't develop on the Microsoft platform! By integrating Windows Azure solutions with SharePoint as I'll discuss in the next few posts, developers are free to use non-Microsoft technologies in creating custom solutions that integrate with SharePoint, including Office 365. This is because Windows Azure not only supports the .NET Framework, but it also supports Java, php and even the popular node.js. By supporting these additional platforms, companies can leverage even larger pools of talent.


Copyright © 2003 - 2012 Andrew Connell
Creative Commons License 
This work is licensed under a Creative Commons License
Site design by Heather Solomon.

 
 
SharePoint 2010 Training
Looking for SharePoint 2010 training for developers, administrators, SharePoint Designer 2010 and end users? Look no further! My company, Critical Path Training offers the best SharePoint training around!