Tuesday, November 27, 2012

Windows 8 Hyper-V Advanced Tips For ESATA

Here's a couple tips for a perhaps not so unique situation when using Hyper-V hosted in Windows 8 Pro with an ESATA spindle involved. 

1)  Moving a snapshot file to a different drive for performance reasons.

Hyper-V does not support keeping shapshot difference disks in folders separate from the base disk.  If you're like me, and store the main VHD on an E-SATA disk, performance isn't bad, but could be much better if you could move the AVHDx file to a different physical drive, preferably a full speed drive.  This issue is minimized with solid state drives, but if you don't have one, the best bet is to create your snapshot, remove the disk from your guest VM, move the AVHD file to an accessible location, and then re-attach it.  (Don't take my word for it:  back stuff up first!)  Once you re-attach, it should run your read/write activities in the avhd, and pull data from the vhd file as needed.

[Edit:  The trick to this part is figuring out where the config files are so you can modify them.  They live at C:\ProgramData\Microsoft\Windows\Hyper-V\Virtual Machines\, but the files themselves are XML files with GUID names. 

I typically find the correct one by simply looking at the modified date... it's probably the one you most recently used. 

Before you can edit these files, you must shutdown the Hyper-V Virtual Machine Management service, and before you start it back up, you'll have to make sure that the account this service runs with has read/write access to the .avhd, wherever you put it.]

2)  If your E-SATA drive is hardware encrypted, shut off power management.

One issue I was running into that was hosing the host and beating on the guest OS...  My ESATA disk has a keypad that you have to tap out a PIN on in order for the disk to function.  Power management was shutting down that drive at about 20 minutes by default, causing the disk to reset and disconnect, even with a VM running off of it...  this is the computer equivalent of crossing the streams... suffice it to say it's Bad™.  So using advanced power management, I was able to tell the system to stop spinning down the drives while the power cord was plugged in.  (If I'm on battery, I typically won't be running my VMs on it.)

Saturday, November 10, 2012

My Windows 8 Adventure So Far

I had different reasons for doing each of the upgrades I've done so far... Here's a list of the upgrades I've done so far, and why... maybe something I talk about will resonate:

My home PC (desktop):
I upgraded my computer initially to see what it was like. For $40, you don't even have to go to the store to buy it.  That's the Pro version, you can buy that to upgrade XP, Vista, and Windows 7 computers...  and it's a way better price than the $200 upgrade from Windows Vista to Windows 7 for example.   It's a relatively easy web-based upgrade. (I encountered some quirks with Symantec/Norton anti-virus, but Windows 8 includes security apps which make a good replacement.)  I've found it to be as solid as Windows 7, and once you get used to the mouse gestures and the way "Modern UI" works, navigation is easier than it was in Chicago-era UI's (The UI we've basically had since Windows 95).

I also intend to explore app development with it. 

I'm also deciding my hardware upgrade path.  It's an inexpensive way to get a grip on what devices I might want to invest in, going forward... for example, do I want to upgrade my current PC, which is great but has no touch screen support, lacking virtualization support, or do I want to bite the bullet and get something more current. 

I'm still deciding on a tablet, and it'll either be "Surface Pro" or "Surface RT"...  I don't want to invest in iPad or iPhone because they aren't going to provide the level of integration I'm seeing and liking with the Windows 8 generation of devices.   Right now, I'm actually leaning toward maybe getting a Surface RT tablet, which are already available at Microsoft Stores at the mall...  the Surface Pro will be more fully featured, but cost more.  I'm thinking for what I want to do with a tablet, the RT will suffice, and if I need more horsepower from my tablet, I'll just remote into a regular computer.
 
My wife's PC (laptop):
I upgraded my wife's computer because she & the kids loved the free games they saw me get from the app store... which works a lot like app stores on iPhone, Android and Windows Phone... they all mastered the "Modern UI" the first day, and found it to be an improvement, as well... so she got the "shiny new" experience on her older laptop.

That experience also enabled me to check out how Windows 8 devices (this includes computers, laptops, tablets, and phones) all communicate through the cloud...  I was easily able to transfer my profile from my desktop to my own login on my wife's laptop.  This is something that Microsoft has been trying to make better for years, and used to only be available to Enterprise users, but now, thanks to the cloud, it's something anyone can take advantage of.

My sister-in-law's PC (laptop)
I upgraded my sister in law's machine to get a less-involved, not quite so technical perspective on it. She picked up the new features right away, and is enjoying things like the free apps... Fresh Paint is one she mentioned as being a favorite, which is one my daughter is particularly fond of, as well.  She's had an odd behavior with it that I have to fix next time I get the chance, but it's just that startup takes longer than it should. 

My work machine (laptop)
I upgraded my work machine because I use Virtual Machines a lot, and Windows 8 has an updated version of Hyper-V in it... I was a bit nervous about this at first... my work depends on not screwing up my VMs, but after getting my first VM migrated from VirtualBox, I'm really glad I did it... Results so far are that performance seems better, and it's just so much more flexible than VirtualBox was.  I'm hoping to see some bugs I encountered in my VirtualBox hosted machines go away too, haven't tried that just yet...  (Stepping through code was a bit flaky in VirtualBox VMs)

To come...  My Mom's machine (desktop)
I'm upgrading my mom's machine for more practical reasons.   I think the UI will be easier for her to get around.  That said, it's mostly the fact that Windows 8 has better apps included than what she's using. (For example instant messaging, email, news... the web-based apps she's using are limited in comparison.)  She'll be able to toy with the apps from the app store, as well, which will be relatively a new technical freedom for her altogether.

Still...
If you want to see Windows 8 in action without risking a computer, I recommend stopping by a nearby Microsoft Store.  They have them set up so you can spend a bit of time playing with them, and plenty of people around to bounce questions off.

Monday, October 1, 2012

If the iPhone is "The Turn", It's Not "The Prestige".

A week ago this past Saturday, I presented at SharePoint Saturday New Hampshire on the topic of integrating Windows Phone with SharePoint in custom apps  I got sidetracked for a moment or two... chatting about why I see Windows Phone as being a viable platform.   So far, it's been rough.  As an anecdote, everyone I know who has a Windows Phone bought a copy of my charity-bound "Jimmy Sudoku" app.  Sadly, the contribution to the charity from it is... not what I hoped. 

Still, I think the cool-aid was worth sharing...   To be fair, all the people I know who have a Windows Phone are relatively outspoken fans of it... and that includes a number of folks you'd never suspect of being "Smart Phone" users.

Anyway, a few days before SPSNH, I ran across a relatively insightful article on TechCrunch (I’ll post the link at the end).  It opens by quoting the opening dialogue of Christopher Nolan’s 2006 film, "The Prestige":

“Every great magic trick consists of three parts or acts. The first part is called “The Pledge”. The magician shows you something ordinary: a deck of cards, a bird or a man. He shows you this object. Perhaps he asks you to inspect it to see if it is indeed real, unaltered, normal. But of course…it probably isn’t. The second act is called “The Turn”. The magician takes the ordinary something and makes it do something extraordinary. Now you’re looking for the secret… but you won’t find it, because of course you’re not really looking. You don’t really want to know. You want to be fooled. But you wouldn’t clap yet. Because making something disappear isn’t enough; you have to bring it back. That’s why every magic trick has a third act, the hardest part, the part we call “The Prestige”.

The TechCrunch’s MG Siegler makes a great point.:  Apple’s iPhone magic is in “The Turn”.   Apple has taken an “ordinary” item, the smart phone, and turned it into something “magic”. 
Indeed, I agree, it is magic, and everything that goes with it.

MG’s article was a commentary of the iPhone 5, and it captured the sentiment I’ve been hearing over & over again about it.   The Apple’s iPhone is starting to run a bit shy of manna.  (Update 9 Feb 2013:  Slashdot, Woz says iPhone Features are Behind. )

It shouldn't be a surprise though...  we've seen it all before, in fact...  we saw it first with the Mac.  It struck again to a lesser extent with the iMac, and dug in big for the iPhone.  There’s a lot to be said for the brand of magic that Apple has wielded over the past several decades, and many would argue that Steve Jobs was the one who brought focus to that magic.
Admiration aside…  If Apple’s past and present magic is in “The Turn”, (and we agree that the iPhone is a hard-won magic trick)  it follows that, within the market, there must have been “The Pledge” and “The Prestige” as well.  That's when I started to get excited... it seems pretty clear to me where "The Prestige" is, but I'll get back to that. 

So what of "The Pledge"?   MG’s article points to Samsung as a weak imitator.  Maybe it is (by it's association with Android), maybe it isn’t…  I guess the courts, and maybe even the public, are done deciding this.   In any case, Samsung never represented the promise of “The Pledge”; it only ever wanted to join in on Apple’s magic.  It’s not “The Prestige” either.
The role of “The Pledge” has been played before, as well, in popular technology of days gone by.  In the early PC wars, this role was played by a small number of makers.  The most memorable of them were the Commodores and TRS-80’s.   This cadre of early PC makers had one thing in common…  the average hobbyist (aka geek) could make them do magic in fits and starts, causing loyalty that ran deep (just ask the Amiga fans), but they didn’t have much, if any, magic for the popular user.  

I would argue, despite the fact that Google’s Android came at about the same time, late 2007, Android represents the promise of this magic…   “The Pledge”…  a Phone, integrated with a pocket computer, that anyone could have a satisfying user experience with.  Open, available, and accessible, it would be… it was everything a "Smart Phone" should be, and it appealed to exactly the market that Smart Phones were made for in 2007.  Despite its fits and starts of magic and a fierce geek following, it, like the Amiga (in its day), is still too immature to be the enduring solution.  Any time I mention the idea that the Android might fade into the realm of the Amiga, the geeks in the room threaten to get belligerent.    I remember getting the same way over my TRS-80 CoCo.

Between issues with platform versions & compatibility, components that don't integrate well, visually or functionally, and malware/spyware, Android is excellent if you're a technical person who's not intimidated by compilers and is savvy enough to avoid spyware & malware... but that's not what the popular user will go for in the long haul.  (Update 9 Feb 2013:  Slashdot, Fragmentation Leads to Android Insecurities)
So what does it take to become “The Prestige”?  It takes that maturity… the ability to allow the average user to make magic with it, affordably, easily… commonly, and, well, normally…  on some level, it restores normalcy, ushering in commoditization of the magic that once was so amazing.

If you take into account that Microsoft was the successor of the IBM compatible legacy, it starts to become clear that Microsoft holds the title to a long history of taking Apple’s “magic”, and refining it into maturity.
In some ways, it’s a bit sad:  iPhone’s manna is indeed running out…  there’s a little less magic in the world…  
…or is there?  This magic will soon be in the hands of friends and family who are just starting to get the itch for a mobile device that can play Fruit Ninja and Angry Birds on.  All those late adopters who wanted maturity, affordability, reliability, and ease of use over “magic”, but now they get both.  When they get their Windows Phone /  Surface RT / Windows 8 device, they’ll get to see magic that geeks and power users have been using for years now…  and that’s the hardest part  “The Prestige”, putting "smartphone"/tablet power and flexibility in the hands of every cell phone user.

As promised, MG Seigler’s article on TechCrunch:
http://techcrunch.com/2012/09/13/the-iphone-5-event/


(Update 1/9/2013 clarified PoV a bit on Android)
(Update 2/9/2013, linked back to Slashdot on various posts that supporting my position)

Wednesday, September 26, 2012

Getting at Office 365 SharePoint Lists In Windows Phone 7 Silverlight Apps

[Edit 7/8/2013:  I've tweaked this to note that this was for WP7.  WP8 has a whole new set of SDKs for SP integration, and it's a different, and much easier story to use them.]

As promised from my presentation at SharePoint Saturday New Hampshire, I owe a code listing on the meaty part of the chat...  the Office 365 authentication component, especially.  It allows a Windows Phone Silverlight app to access things the lists.asmx service behind the Windows Live ID authentication.  (Frankly, the technique is the same, no matter what kind of client you're using, but the demo I was doing was using Silverlight 4 for Windows Phone 7.

I also owe slides:


Here's the activity rundown:

1)  The client app ("Windows Phone App") makes a SAML SOAP request to https://login.microsoftonline.com/extSTS.srf
2)  The SAML response comes back, allowing the app to parse the SAML token.
3)  Make another call, this time to  {your Office365 team site}/_forms/default.aspx?wa=wsignin1, posting the token.
4) The response that comes back need only be checked for errors, the magic is in the cookie container.  It contains an HTTPOnly token (which the development tools do a terribly good job of hiding.)
5)  Assign your cookie container from your previous result to the ListSoapClient that you're using to make your service calls from.
6)  Profit!



I broke up the "Activation" line on the client side to point out that the calls are Async.

In any case, I have a very rough SPAuthenticationHelper class that I also promised to post.

Here's an example of how you can use it:

    class SPTasksList
    {
 
        SPAuthenticationHelper _authenticationHelper;
        ListsSoapClient _listsClient;
        bool isBusy = false;

        TaskItem currentUpdate = null;
        string _taskListUri = "http://spsnh.sharepoint.com/TeamSite/Lists/Tasks/AllItems.aspx";

        public SPTasksList()
        {
            _authenticationHelper = new SPAuthenticationHelper(_taskListUri);
            _listsClient = new ListsSoapClient();
            _listsClient.GetListItemsCompleted += new EventHandler<GetListItemsCompletedEventArgs>(_listsClient_GetTasksListCompleted);
            _listsClient.UpdateListItemsCompleted += new EventHandler<UpdateListItemsCompletedEventArgs>(_listsClient_UpdateListItemsCompleted);
        }
 

        public void BeginGetTasksList()
        {
            if (!_authenticationHelper.IsAuthenticated)
            {
                _authenticationHelper.OnAuthenticated += new EventHandler<EventArgs>(_authenticationHelper_OnAuthenticated_GetTasks);
                _authenticationHelper.SigninAsync(Configuration.UserName, Configuration.Password);
            }
            else if (!isBusy)
            {
                isBusy = true;
                XElement query = XElement.Parse("Completed");
                string ListName = "Tasks";
                string ViewId = "{f717e507-7c6e-4ece-abf2-8e38e0204e45}";
                _listsClient.GetListItemsAsync(ListName, ViewId, query, null, null, null, null);
            }
        }

        void _authenticationHelper_OnAuthenticated_UpdateTask(object sender, EventArgs e)
        {
            _listsClient.CookieContainer = _authenticationHelper.Cookies;
            BeginUpdateTask(currentUpdate);
        }

......
} 

I ported this from a few other examples I found online to Silverlight for Windows Phone.  I apologize,  I haven't had time to polish it, and I'm having a hard time with the embedded SOAP litteral, but here's the SPAuthenticationHelper class:





using System;
using System.Net;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
 
namespace SPSNH_SPConnector.Implementation
{
    public class SPAuthenticationHelper
    {
        public CookieContainer Cookies { get; set; }
        public bool IsAuthenticated { get; private set; }
        public event EventHandler<EventArgs> OnAuthenticated;
 
        private bool _isAuthenticationInProgress = false;
 
        const string _authUrl="https://login.microsoftonline.com/extSTS.srf";
        const string _login="/_forms/default.aspx?wa=wsignin1.0";
       
        //namespaces in the SAML response
        const string _nsS = "http://www.w3.org/2003/05/soap-envelope";
        const string _nswst = "http://schemas.xmlsoap.org/ws/2005/02/trust";
        const string _nswsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
        const string _nswsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
        const string _nswsa = "http://www.w3.org/2005/08/addressing";
        const string _nssaml = "urn:oasis:names:tc:SAML:1.0:assertion";
        const string _nswsp = "http://schemas.xmlsoap.org/ws/2004/09/policy";
        const string _nspsf = "http://schemas.microsoft.com/Passport/SoapServices/SOAPFault";
        const string _samlXml = @"<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope"" xmlns:a=""http://www.w3.org/2005/08/addressing"" xmlns:u=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd""> <s:Header> <a:Action s:mustUnderstand=""1"">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action> <a:ReplyTo> <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address> </a:ReplyTo> <a:To s:mustUnderstand=""1"">https://login.microsoftonline.com/extSTS.srf</a:To> <o:Security s:mustUnderstand=""1"" xmlns:o=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd""> <o:UsernameToken> <o:Username>{0}</o:Username> <o:Password>{1}</o:Password> </o:UsernameToken> </o:Security> </s:Header> <s:Body> <t:RequestSecurityToken xmlns:t=""http://schemas.xmlsoap.org/ws/2005/02/trust""> <wsp:AppliesTo xmlns:wsp=""http://schemas.xmlsoap.org/ws/2004/09/policy""> <a:EndpointReference> <a:Address>{2}</a:Address> </a:EndpointReference> </wsp:AppliesTo> <t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType> <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType> <t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType> </t:RequestSecurityToken> </s:Body> </s:Envelope>";

        Uri _uri;  
        HttpWebRequest _getTokenRequest = HttpWebRequest.CreateHttp(_authUrl);
        HttpWebRequest _submitTokenRequest = null;
        string _token;
 
        public SPAuthenticationHelper(string uri)
        {
            _uri = new Uri(uri);
            Cookies = new CookieContainer();
        }
 
        public void SigninAsync(string userName, string password)
        {
            if (!_isAuthenticationInProgress)
            {
                _isAuthenticationInProgress = true;
                getTokenAsync(userName, password);
            }
        }
 
       
        private  void getTokenAsync(string userName, string password)
        {
            string tokenRequestXml = string.Format(_samlXml, userName, password, _uri.Host);
 
            _getTokenRequest.Method = "POST";
            _getTokenRequest.BeginGetRequestStream(new AsyncCallback(Get_GetToken_RequestStreamCallback), tokenRequestXml);
        }
 
        private void Get_GetToken_RequestStreamCallback(IAsyncResult result)
        {
            string tokenRequestXml = (string)result.AsyncState;
            var reqstream = _getTokenRequest.EndGetRequestStream(result);
            using (StreamWriter w = new StreamWriter(reqstream))
            {
                w.Write(tokenRequestXml);
                w.Flush();
            }
            _getTokenRequest.BeginGetResponse(new AsyncCallback(Get_GetToken_ResponseStreamCallback), null);
        }
 
 
 
        private void Get_GetToken_ResponseStreamCallback(IAsyncResult result)
        {
            _token = null;
 
            var response = _getTokenRequest.EndGetResponse(result);
 
            var xDoc = XDocument.Load(response.GetResponseStream());
 
            var body=xDoc.Descendants(XName.Get("Body", _nsS)).FirstOrDefault();
            if (body != null)
            {
                var fault = body.Descendants(XName.Get("Fault", _nsS)).FirstOrDefault();
                if (fault != null)
                {
                    var error=fault.Descendants(XName.Get("text", _nspsf)).FirstOrDefault();
                    if (error != null)
                        throw new Exception(error.Value);
                }
                else
                {
                    var token = body.Descendants(XName.Get("BinarySecurityToken", _nswsse)).FirstOrDefault();
                    if (token != null)
                    {
                        _token = token.Value;
                        SubmitTokenAsync();
                    }
                }
            }           
        }
 

        private  void SubmitTokenAsync()
        {
 
            UriBuilder bldr = new UriBuilder(_uri.Scheme, _uri.Host, _uri.Port);
            _submitTokenRequest = HttpWebRequest.CreateHttp(bldr.Uri + _login);
            _submitTokenRequest.CookieContainer = Cookies;
            _submitTokenRequest.Method = "POST";
            _submitTokenRequest.BeginGetRequestStream(new AsyncCallback(Get_SubmitToken_RequestStreamCallback), null);
        }
 
        private void Get_SubmitToken_RequestStreamCallback(IAsyncResult result)
        {
            var requestStream = _submitTokenRequest.EndGetRequestStream(result);
            using (StreamWriter w = new StreamWriter(requestStream))
            {
                w.Write(_token);
                w.Flush();
            }
            _submitTokenRequest.BeginGetResponse(new AsyncCallback(Get_SubmitToken_ResponseCallback), null);
        }
 
        private void Get_SubmitToken_ResponseCallback(IAsyncResult result)
        {
            UriBuilder bldr = new UriBuilder(_uri.Scheme, _uri.Host, _uri.Port);
 
            var response = _submitTokenRequest.EndGetResponse(result);
            string responseString = (new StreamReader(response.GetResponseStream())).ReadToEnd();
           
            bldr.Path = null;
            Cookies = _submitTokenRequest.CookieContainer;//.GetCookies(bldr.Uri);
            _isAuthenticationInProgress = false;
            IsAuthenticated = true;
            if (OnAuthenticated != null)
            {
                EventArgs args = new EventArgs();
                OnAuthenticated(this, args);
            }
        }
    }
}
 
 


Monday, August 27, 2012

Reliving "Revolutionary" with Windows 8

"What do you think of Windows 8?"   I hear this question all the time... everywhere I go.   I hear people talking about it on the bus, in line at coffee shops, and even in odd places like hospital rooms.  It's the biggest change we've had in the PC in well more than a decade.  Everyone knows this is as big as broadband in everyone's home.

But... more than a decade?   Really? 

Definitely.  How old would a child be if it was born the last time there was a *true*, major version iteration of Windows?   3?  8...? 

How about...  18?   Yeah...  18... old enough to drive.  Old enough to be looking at colleges. The Daytona (Windows NT) / Chicago (Windows 95) user experience, were it a child, would now be looking at an opportunity to suffer the choice between Romney or Obama.  The experience unleashed on IT and the public introduced us to the Start menu, the Desktop, managed application installs, and several other major features that the enterprise and private user alike have now literally grown up on.

Some might argue that Windows XP was a hefty revision that almost qualifies, but I would say not so much.  Improvements abounded, but the core user experience hasn't changed by more than revision increments in Windows 98, ME, 2000, XP, 2003, 2008, 7... really...  since Windows 95. 

But, with Windows 8, this changes.  Windows 8 brings us a whole new user experience in the "Modern UI" formerly known as "Metro UI". 

If you recall, Windows 95 still essentially lived on top of DOS, and had enough of the Windows 3.x framework to run all the apps we'd already come to depend on (like Word 6, Excel 5, and Windows AOL 2.5).  While those programs ran in Chicago, there were compatibility issues, and the user interface really started to look crusty on legacy applications.  I was actually a relatively late adopter, waiting until Windows 98 before I finally succumbed to the dark side. (I had discovered IBM OS/2 Warp and become a fan... it actually took a 1-2 punch to Warp to get me to switch.  1:  When Warp was stable, it was unbeatable, but when it crashed it was unrecoverable, (and crash, it inevitably did).  2:   Command & Conquer / Red Alert, which had an improved video mode that was only available when installed in Windows... and it was even more awesome in that improved resolution mode. )

Just like Windows 95, Windows 8 is a transitional OS.

One of the big things I keep hearing about Windows 8 is... what a P.I.T.A. is is to figure out. "Microsoft is taking a huge risk with this... why are they breaking my Windows?", I hear.  Or...  "I'm open-minded.  I subjected myself to it until the pain became unbearable.  (I can't wait until Mac OS X takes over.)"

Transition, though?  Yes.  Transition.  Again, this is the first real full version increment of the Windows user experience that we've seen in years, and it all comes down to this Modern UI thing.  It does exactly what Windows 95 did to Windows 3.x on DOS.  It wipes the slate clean and re-imagines how we operate our computers from the ground up using modern human interface devices... (HIDs). 

Touch screen, movement, gestures, enhanced 3D graphics... these are things that started to accelerate development not long after the release of 95, but the world was still on the Windows 95 learning curve.  Hardware was too immature & expensive to develop an OS around them then... So, while you were getting comfortable with your desktop, (if you haven't noticed) your cell phone's user experience surpassed your desktop.

So on the surface (no pun intended) this is what Windows 8 is...  it's a full OS-deep refresh that catches home computing back up to what people have gotten used to in their cellphones.

"Common sense" says this all implies a true P.I.T.A. for people and companies that dig in on it. 

Let's look a little deeper, though, at what else this represents.  Again, this is a transitional OS.  It does everything the old user experience did... if you dig a bit.  It does this to support the old applications with their freshly encrusted-feeling user experience.  People can continue leveraging your old technology investments.  Indeed, you can continue making investments in the old user experience...  just know that the writing's on the wall. 

It's only a matter of time before people do what they inevitably did with Daytona/Chicago... adopt, extend, and embrace, or be extinguished.  

Why?  Because... when it comes down to it, the part that people really hate is not the "user experience" part.   It's the "NEW" part that hurts.  Once the "NEW" wears off, what you've got left is a really genuinely cleaner, better, more efficient UI that leverages new hardware in important ways, and puts it years ahead of desktop OS competition, both in terms of capability, and even in terms of price point...  and pushes that same advantage out seamlessly to a myriad of other devices.  So getting past the sharp learning curve on one device means you'll be rocking the new UI everywhere in no time.

Like the glory days of the Dot-Com boom, the days of Daytona & Chicago, these will be days of learning and technical renovation, even re-invention.  This is what I see coming with Windows 8 in the desktop, with an added benefit of being even more ubiquitous than it was back in the 90's.  With the coming of Surface, Windows Phone 8, your apps will have more opportunity to run in more places, on more machines, than ever before.... using more "Star Trek" functionality than we're yet used to. 

Those looking to remodel that kitchen... here's your wake up call.  Windows 8's user experience is representative of what made the Dot Com days so great... (and there were some plus sides.)  It was when leveraging any of the revolutionary new technology became a competitive advantage all by itself.  Early adopters will feel the pinch of the initial investment, but... with some planning, will reap the rewards by having that pain behind them by the time Windows 9 rolls around. 

I, for one, look forward to my new OS overlord.

Friday, August 17, 2012

Using Client Certs to Pull Data from WCF in SSIS Data Flow Transform Script

I've recently had the opportunity to brush off my SSIS skills and revisit this toolset.   In my most recent usage, I had a requirement to use SSIS to pull data from a WCF web service that was a) using the net.tcp protocol, and b) used transport security with a client X.509 certificate for authentication.

This was fun enough by itself.  Configuring WCF tend typcially to be non-trival even when you don't have to tweak app.config files for SQL SSIS services.  One of my goals, in fact, was to avoid having to update that, meaning I had to put code in my SSIS Script block in the data flow to configure my channel & security & such.

Luckily, I was able to find examples of doing this with wsHttpBinding's, so it wasn't a stretch to tweak it for netTcpBinding with the required changes to support certificate authenticated transport security.

Here's the code...
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.ServiceModel;
using SC_13defb16ae45414dbac17137434aeca0.csproj.PaymentSrv;


[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
    ChannelFactory<IProfile> channelFactory;
    IProfile client;
    public override void PreExecute()
    {
        base.PreExecute();

        bool fireAgain = false;
        this.ComponentMetaData.FireInformation(0, "Pull From Profile Service.PreExecute", "Service URI: '" + this.Variables.varProfileServiceUrl + "'", null, 0, ref fireAgain);
        this.ComponentMetaData.FireInformation(0, "Pull From Profile Service.PreExecute", "Cert Fingerprint: '" + this.Variables.varClientCertFingerprint + "'", null, 0, ref fireAgain);

        //create the binding
        NetTcpBinding binding = new NetTcpBinding();
        binding.Security.Mode = SecurityMode.Transport;
        binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
        binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;

       
        EndpointAddress endpointAddress = new EndpointAddress(this.Variables.varPaymentServiceUrl);
        channelFactory = new ChannelFactory<IProfile>(binding, endpointAddress);

        channelFactory.Credentials.ClientCertificate.SetCertificate(
            System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
            System.Security.Cryptography.X509Certificates.StoreName.My,
            System.Security.Cryptography.X509Certificates.X509FindType.FindByThumbprint,
            this.Variables.varClientCertFingerprint);
            //" x8 60 66 09 t6 10 60 2d 99 d6 51 f7 5c 3b 25 bt 2e 62 32 79");

        channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode =
            System.ServiceModel.Security.X509CertificateValidationMode.PeerTrust;
       
        //create the channel
        client = channelFactory.CreateChannel();

       
        IClientChannel channel = (IClientChannel)client;

       
        channel.Open();
        this.ComponentMetaData.FireInformation(0, "Pull From Profile Service.PreExecute", "Open Succeeded.", null, 0, ref fireAgain);


    }

    public override void PostExecute()
    {
        base.PostExecute();

        //close the channel
        IClientChannel channel = (IClientChannel)client;
        channel.Close();

        //close the ChannelFactory
        channelFactory.Close();

    }

    public override void Input0_ProcessInputRow(Input0Buffer Row)
    {
        Guid txGuid = Guid.NewGuid();
        Profile profile = null;
        try
        {
            profile = client.getProfile(txGuid, Row.ProfileId);
            Row.PSProfileType = GetProfileType(profile);
           
        }
        catch (Exception ex)
        {
            string message = ex.Message();
            Log(message, 0, null);
        }
       
       
    }
    private string GetProfileType(Profile profile)
    {
        return "x";
    }
}
So one of the challenges I encountered while using this method had to do with the client certificate.  This error drove me nuts:

The credentials supplied to the package were not recognized.
Server stack trace:
   at System.Net.SSPIWrapper.AcquireCredentialsHandle(SSPIInterface SecModule, String package, CredentialUse intent, SecureCredential scc)
   at System.Net.Security.SecureChannel.AcquireCredentialsHandle(CredentialUse credUsage, SecureCredential& secureCredential)
   at System.Net.Security.SecureChannel.AcquireClientCredentials(Byte[]& thumbPrint)
   at System.Net.Security.SecureChannel.GenerateToken(Byte[] input, Int32 offset, Int32 count, Byte[]& output)
   at System.Net.Security.SecureChannel.NextMessage(Byte[] incoming, Int32 offset, Int32 count)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at System.ServiceModel.Channels.SslStreamSecurityUpgradeInitiator.OnInitiateUpgrade(Stream stream, SecurityMessageProperty& remoteSecurity)
   at System.ServiceModel.Channels.StreamSecurityUpgradeInitiatorBase.InitiateUpgrade(Stream stream)
   at System.ServiceModel.Channels.ConnectionUpgradeHelper.InitiateUpgrade(StreamUpgradeInitiator upgradeInitiator, IConnection& connection, ClientFramingDecoder decoder, IDefaultCommunicationTimeouts defaultTimeouts, TimeoutHelper& timeoutHelper)
   at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.SendPreamble(IConnection connection, ArraySegment`1 preamble, TimeoutHelper& timeoutHelper)
   at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.DuplexConnectionPoolHelper.AcceptPooledConnection(IConnection connection, TimeoutHelper& timeoutHelper)
   at System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(TimeSpan timeout)
   at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open()
Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at System.ServiceModel.ICommunicationObject.Open()
   at ScriptMain.PreExecute()
   at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.PreExecute()
If you look at it, this is an authentication error.  Tracing the code, it happens AFTER the code successfully retrieves the client certificate from the certificate store.  The call to SetServerCertificate succeeds without incident.

The error hits  when the code opens the channel, and tries to use the private key attached to the client certificate to prove to the server that "I'm a valid client."

I went nuts because I was an administrator on the machine, and had installed the client certificate to the certificate store myself.  It initially worked, and there was no indication that there was a problem getting the certificate from the cert store.

It turns out that when you use the machine store under these circumstances, I needed to give myself explicit permission to the client certificate in order for the SetServerCertificate to get the private key along with the client certificate.  This was counter-intuitive for two *additional* reasons:  1)  I was an administrator on the box, and already should have had this permission by the fact that my login account belonged to the administrators group (which you can see from the pic below, also had access.)  2)   It worked the day before.  When I imported the private key originally to the key store, it appears somewhere in the depths of Windows 7 (and this applied on Server 2008 R2 as well) I still had permission in my active session context.  When I logged out, that login context died, and, coming back the next day, I logged in again, not realizing I wouldn't be able to access the key.  Giving myself explicit permission as shown below allowed me to run my SSIS package within Visual Studio and from SSMS.
(Sorry, Blogger's not letting me include this bigger... click it for full size view.)