Tuesday, April 12, 2011
Scott Guthrie coming to Arizona
If you live in the Phoenix metro and have any interest in the .Net stack, you have to attend. Register now!
http://azgroups.org/2011scottguevent.aspx
Tuesday, November 17, 2009
TIP OF THE DAY: Creating nice looking animated gifs using Flash
So today I wanted to pretty up our “loading” animations. We’ve been using the AjaxLoad web app to create candy canes-type loader icons (nice site, btw), but wanted to brand it and stay consistent between our flash and HTML UIs.
I already had the animation in Flash. A simple, 40-frame thingy using parts of our logo. 5 plain colors. I thought exporting it to .gif would be a no-brainer. But of course, as with almost everything related to Flash, it’s not that simple. While the option is available in the export menu, Flash seems to always dither GIF colors. I even tried using only the web palette, all my colors looked dithered. Couldn’t find a way around it.
So here’s the trick I used.
- Create the animation in Flash
- Size the stage to the target gif dimensions
- Position your animation properly on the stage
- Export your movie as a PNG sequence with the following options:
- Resolution: 72 dpi
- Include: “Full document size”
- Colors: “24 bit with alpha channel”
- If you created a tween with identical starting and end frames, delete the last PNG file otherwise you’ll see a hiccup at the end of each loop
- Open Photoshop (I’m using CS4, not sure if the following option exists in earlier versions)
- Select the first PNG in the sequence
- Make sure the “Image Sequence” checkbox is selected (I had never noticed it before)
- Select “Export for Web”
- Tweak your gif settings (palette, transparency, matte etc…)
- Save
Voila! Your pretty, nicely optimized gif is ready to go.
Friday, October 30, 2009
GoGrid MyGSI vs Amazon AMI
For the past couple of days I've been working on setting up a GoGrid MyGSI image (the GoGrid equivalent of an Amazon Machine Image - AMI) for our app. The benefits are obvious - instead of manually preparing each new instance with the right apps, roles, configuration etc..., create a bootable image. This would speed up deployment time and would be invaluable whenever scaling up our grid horizontally or vertically.
I've been doing this in Amazon EC2 for the past 18 months. For those not familiar with that service, any EC2 instance can be bundled up and saved to S3, then registered as a private or public image for future use. On Linux, this requires a couple shell commands. For windows, you can use the EC2 console, a nice little web GUI. A couple clicks, that's it. Very convenient. Based on my experience, AMI bundling and saving usually takes about 10 minutes, less for a thin Linux image, more for a heavy Windows setup. So far, I haven’t noticed a difference between the original instance and the one started from the bundled image, other than public & private IP addresses. Admin passwords were restored, and everything seemed to be working properly.
GoGrid does things slightly differently. In order to create an image, you are required to start a GoGrid SandBox from an existing image. The sandbox is not intended to be used in production. It uses 2 GB RAM minimum, so leaving it on for an extended period will cost you. You’re supposed to start the sandbox, install whatever you need, windows updates, etc… then run a prep script, wait a few seconds and save the image using the GoGrid admin panel.
Once the image is saved, it shows up in the list of available images when starting a new GoGrid instance.
At first glance, this is not a big deal. But keep in mind that starting a sandbox takes about 10 minutes. Running prep and saving the image, another 15 to 20 minutes (I was using a 4 GB Win 2008 image). Then starting a new instance takes another 10 minutes (the virtual server looks ready after about 7 minutes, but it really takes another 2 to 5 minutes before you can connect to it). So updating your image and deploying it will take you roughly 30 to 45 minutes. You better be sure your image has everything you want on it, because before you know it, your whole day’s gone by the time your instances are really ready.
Now here’s my other gripe, and the reason why I had to update my image about 8 times already. Instances started from the saved MyGSI image are slightly different than the sandbox you were working on. The prep script does something to your image to make it bootable as a GoGrid virtual server. Here’s what I’ve noticed so far:
- The administrator password gets reset every time a new instance gets created. Retrieving the password is easy, it shows up in the GoGrid passwords panel. But if you’re relying on identical username / password combinations between your instances for network communication & security purposes, you’ll need to rely on a different account. The good news is you can create a new user account on your sandbox, and that one won’t get modified by the prep script. It gets properly restored, and the password doesn't change.
- Much more troublesome, GoGrid made the decision that by default, users are not interested in having their instances share a common private network. I don’t know why; it seems completely baffling to me. You get a private cloud storage space with your GoGrid account, but by default any new instance won’t be able to connect to it. To get this to work, you need to add a route, enable the second adapter, assign a static IP address, then mount the cloud storage account using the provided username & password, which can’t be modified. That’s about 3 manual steps required for every new virtual server that might need to connect to other virtual servers in your grid or to your cloud storage space. And you need to keep track of your static IP addresses in a separate spreadsheet, GoGrid won’t help you manage that. When you need to deploy a new instance quickly, that’s a real pain in the neck. You can create the route on your MyGSI sandbox, and it will be restored. However, configuring the private network adapter won’t help, the prep script will always disable it. So I strongly recommend the following:
- Start a cheap 512 MB instance and set it up as a DHCP server on your private network.
- Add the cloud storage route to your custom sandbox.
- Configure the MyGSI sandbox private network adapter to use DHCP
- Create a batch file on your MyGSI instance that will do the following:
- enable the network adapter:
netsh interface set interface name="Local Area Connection 2" admin=ENABLED - wait for a 30 seconds to let the adapter find your DHCP server (I found this trick to add a “wait” batch command on windows)
- optional – you might wan to force an IP address refresh by doing
ipconfig /release “Local Area Connection 2”
ipconfig /renew “Local Area Connection 2” - mount your cloud storage space
net use Z: \\[accountId].cloud.storage.gogrid.com\[accountId] [accountPassword] /USER:[accountId] /Persistent:Yes - Put that file on your MyGSI image, and set up windows scheduler or a RunOnce registry key to run the file on computer startup.
- After booting up my MyGSI image, SSL wasn’t working on my instance. Turns out that creating the instance broke my certificate. I don’t know if it’s a machine key problem or what, but the cert private key was gone. So I put a .pfx export of the certificate on the MyGSI image and added one line to my startup batch file:
- certutil –importpfx myFile.pfx
note: you don’t have to use a PFX file, look at the certutil help for more options
So now every new instance I create is automatically setup for private network and cloud storage access whenever started up or rebooted. Much, much better. Why GoGrid instances are not always set up this way is beyond me. I would expect GoGrid to provide private IP address assignment the same way Amazon does, and pre-configure the network adapter and cloud storage mapping. What’s the point of creating a grid if each node can’t communicate with the others? Cloud storage access, distributed caching & DB access are all basic requirements for any cloud app, and all require a private network. The GoGrid approach is way too cumbersome.
This is all well and good if your image doesn’t change very often. But we live in a beta world, and app deployments can be almost as frequent as nightly builds. Rebundling an image for each deployment and launching new instances from it every time you make a change to a web page is not exactly practical.
I’ve been using wowza media server for EC2 for a year, and I love their approach. You just add xml configuration data at launch time (looks like a build file), and the wowza instance goes through it the first time it’s instantiated. Installing a custom app just requires zipping up the jar & config files using the wowza folder structure, then defining the http location of that zip file in the startup script. No need to create custom image for your application. Upgrading to the latest version of the wowza AMI is a painless process.
Without going into as much depth & flexibility as wowza, we decided to follow a similar approach. Zip up our custom code & apps in a known folder structure, post it to our cloud storage app, and load it at startup using a startup batch file or command-line app. This way, updating a cloud with a bunch of nodes or scaling horizontally only requires uploading the archive, a few clicks in the GoGrid UI, and that’s it. Using the GoGrid API, we should even be able to put a web front end on our DHCP server (the one that’s always on) to upload the archive, put it on the cloud storage space, and remotely reboot all related instances.
We finally reached our holy grail: one-click, 10 minute deployment.
Tuesday, May 19, 2009
Best Of Mix ‘09 (MSDN event)
I went to this free MSDN event yesterday, a 3-hour recap of the most interesting technologies unveiled at Mix ‘09. The event focused on Silverlight 3, Windows Azure and ASP.NET MVC (we skipped that introductory lecture since we’ve been using the framework for quite a while). Here’s a quick recap.
Silverlight 3 / .Net RIA services
The MS demo guy really just focused on the RIA services extensions (a separate framework you can add on SliverLight 3 apps). The technology is built on top of SQL Server Data Services (Astoria), which provides quick REST scaffolding around your entities / db schema.
No WCF here, just an .axd resource handler with REST-like Uris that lets you access your CRUD services with no need to write code.
What’s cool about the Silverlight RIA dat aservice framework is it provides you with client-side proxy model and service classes that you don’t have to worite. Not only that, but it deals with entity states on the client, difgrams, and lets you save changes automatically or manually. It makes for a coll demo – build your entities, add a datagrid & form views to your sliverlight apps, a save button and you’re done. It uses optimistic concurrency by default, so you can even detect conflicts (mutliple users changing the same data concurrently).
You can also decorate your model classes with CLR attributes from System.ComponentModel.DataAnnotations to define validation rules (on the server), and bam – all validation is taken care of on the client as well.
I absolutely get the benefit of all this for quick and dirty intranet apps. You already have the data model, now you can build an entire RIA on top of it in a matter of hours. A few clicks in Visual Studio, some XAML, and you’re done. No need for an advanced C# programmer.
The demo I saw used ADO.NET Data services via an .ax resource handler. I’m not super familiar with that technology, but I read a blog post from Brad Abrams mentioning various data stores could be used, including REST service, nHibernate, WCF-server etc…
Personally I think MS can go a bit overboard with declarative markup with its declarative data sources etc… I think a little bit of C# never killed anybody, which is why I like the ASP.NET MVC model so much. But I digress.
Windows Azure
Azure is Microsoft’s cloud infrastructure response to Google App Engine, Mosso, Amazon Web Services etc… For those of you who’ve heard about cloudiness but don’t really know what it means, these vendors are trying to make it easy for you to deploy scalable clustered apps using a “pay for what you use” pricing model, without having to worry about load balancing, failover, redundancy etc... A typical cloud hosting offering usually consists of a combination of the following:
- Data store (SQL Server Data Services, Azure table storage, Amazon Simple DB, MYSQL cluster etc…)
- Cloud file storage (Amazon S3, virtual NAS devices, Azure simple data storage)
- Queuing service (Amazon SQS)
- Computing nodes for background processing
- Load-balanced web cluster to host your web app and/or web services
Their offerings range from pure infrastructure where you get a bunch of computing instances at the OS level (GoGrid, EC2) to a full framework (Google App Engine), with Azure and Mosso as in betweens – you don’t have access to the actual VMs, but your code is not completely dependant on your host.
What I like about Azure:
- Microsoft provides you with a full development stack / SDK you can install on your development machine. It’ll run your Azure app just like it would in the cloud, and deployment is a breeze. No other vendor out there offers anything like it. We use AWS, and we had to develop an abstraction layer for each service with implementations that allowed us to develop while not being connected to AWS. I still think it was the right thing to do since our app would now be easily portable to a different host, but it makes me nervous whenever we deploy. Unit-tests only take you so far, there are performance and scalability issues that can be impossible to detect when you’re simulating the app in a completely different environment (such as local disk access vs web service based storage).
- I registered for the beta a while a go and played with a bit. You get a staging and production environments out of the box, and deploying from staging to production is just one click.
- At first I was very excited about SQL Server in the cloud. I thought MS had found a way to scale a relational DB indefinitely. Yes, I also believe in magic. However, I was mistaken. It’s just like using any SQL server instance – sharding will be a pain, and the platform won’t work if you’re building a super-large app. For this you’ll need to use Azure table storage (closer to SimpleDB) - I’ll get to that in a minute. I’ll still put that feature as a pro since you don’t have to worry about DB maintenance and licensing – it’ll be price as you go. I have t say though, Mosso found a way to have a similar offering. Create as many DBs as you want, priced by amount of storage needed.
- Windows Server 2008 / IIS 7 support. I know this should be obvious, and Mosso & GoGrid do offer this as well, but not EC2. That’s the one thing preventing us from hosting our web app on the Amazon cloud.
What I don’t like:
- SQL Server Data Services don’t come with full-text indexing or CLR integration. So if your cloud app features a search box (I know, crazy concept, right?), you’ll need to use a 3rd party indexing app (Google?) or write your own indexer (who’s going to do that??).
- Table storage is so limited I don’t understand what I would do with it. You can basically store your entities in name/value property bags by specifying a partition key & row key. I’m fine with the inability to join, but you can’t even query data across partitions. Since each partition will always be stored on the same node, you’ll want to break your extra-large tables into multiple partitions by using multiple partition keys. So let’s say you build the next YouTuve and host – I don’t know – 500 million videos. You’ll segment your video data store into maybe 100 partitions. There’s no built-in way for you to query that huge data store at once. You’ll need to create your reports separately or use something like Hadoop (which Amazon offers) to query each partition and aggregate the results. In short: a lot of work.
- No distributed caching layer. This one baffles me. Any cloud app should be built with distributed caching in mind (like memcached). If you’re going to get 100 simultaneous requests to your load-balanced clustered, with each one potentially fetching 50 items from your data store, you could be in trouble. I’m sure SQL Server data services use replicated DBs, so that’ll help, but if MS charges for DB traffic, this could be very, very painful. Not only that, but it’ll be slower than fetching a binary serialized object from memory. IIS caching won’t do, you’ll get into big-time cache invalidation nightmares if your data changes often. The demo guy showed blob storage as a potential solution, but since operation writes the data 3 times, it’ll be slow – and potentially costly. It sounds like MS is aware of the problem, they’re working on a Velocity offering. No ETA though. BTW – Mosso has the same exact problem. Right now if you need distributed caching you’ll have to use an infrastructure offering like EC2 or GoGrid.
Conclusion
Azure looks promising. I really want pricing info though. And it sill looks about 12 to 18 months away from being exactly what I need.
Tuesday, April 28, 2009
WCF REST & ASP.NET MVC authorization
Last week I needed to implement an authorization scheme in our MVC and WCF apps. I found a bunch of resources on how to implement Role or Claims-based authorization in both frameworks, but they all required adding CLR attributes on controller actions and service operations - a bit of messy for my taste, and required hard-coding your authorization rules, which didn’t fit my requirements. We developed a system that allow us to define authorization rules at run-time, and I was looking for a way to write a single authorization front controller for all service / MVC calls. I could have gone the HTTP module way, but that would have mean writing code to parse the request & figure out what controller, contract, operation are being called with what parameters. Since all of this is already done for in the MVC and WCF frameworks, I wanted to have this authorization controller called after the request is parsed and before the operation is executed.
public class MyAuthorizedController {
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
string targetAction = filterContext.ActionDescriptor.ActionName;
string idParameter = filterContext.ActionParameters["id"];
string username = filterContext.HttpContext.User.Identity.Name;
bool isAllowed = IAuthorizationManager.Validate(username, idParameter , targetAction);
if(isAllowed)
base.OnActionExecuting(filterContext);
throw new UnauthorizedAccessException();
}
}Now just make sure all your secured controllers extend this class, and you’re done.
WCF looked just as easy at first. In the ServiceBehavior configuration, you can define a custom authorization class in the serviceAuthorization’s ServiceAuthorizationManagerType property. The custom class needs to extend ServiceAuthorizationManager, which exposes a protected virtual method called “CheckAccessCore”. Just override that method.
public class MyAuthorizationManager : System.ServiceModel.ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
// If using ASP.NET authentication, we can retrieve the user name from HttpContext
// Of course this won't work for anonymous requests, you;d need to handle those as well
var userName = HttpContext.Current.User.Identity;
// the target service is the actual service class being called.
var targetService = operationContext.Host.Description.ServiceType;
// not used in the specfic authorization manager, but could be useful depending on your scenario
var contractDescription = getOperationContractType(operationContext);
// The following key will help check whether or not this was a valid REST request
// If the key is found, we'll retrieve the UriTemplateMatch results which will give us info about operation name and
string key = WebHttpDispatchOperationSelector.HttpOperationSelectorUriMatchedPropertyName;
var props = operationContext.IncomingMessageProperties;
UriTemplateMatch match = null;
if (props.ContainsKey(key) && (bool)props[key])
match = operationContext.IncomingMessageProperties["UriTemplateMatchResults"] as UriTemplateMatch;
else
// in this case, we'll ignore all non-REST requests
return true;
// now that we've found the UriTemplateMatch instance, we can find information about our method parameters
// we'll use 'id' as an example
if(match.BoundVariables.AllKeys.Contains("id", StringComparer.InvariantCultureIgnoreCase))
idValue = match.BoundVariables["id"];
}
// For information about the data contract (the interface mapped to the endpoint being used),
// you'll need to loop through all EndPoints in operationContext.Host.Description.Endpoints and look for
// one that has the same namespace and contract name as your context's EndpointDispatcher
private static DictionarycontractMap = new Dictionary ();
private static ContractDescription getOperationContractType(OperationContext operationContext)
{
if(contractMap.Count == 0){
lock(contractMap){
ServiceEndpointCollection endpoints = operationContext.Host.Description.Endpoints;
foreach (ServiceEndpoint endpoint in endpoints) {
string contractKey = endpoint.Contract.Namespace + endpoint.Contract.Name;
contractMap[contractKey] = endpoint.Contract;
}
}
}
EndpointDispatcher dispatcher = operationContext.EndpointDispatcher;
string key = dispatcher.ContractNamespace + dispatcher.ContractName;
return contractMap[key];
}
}
So now we’ve found out the current user's username, the service’s type, the value of our id parameter, and the datacontract’s description. We’re getting close but – where’s the actual operation name (the name of the method being called)??
When using SOAP, you can easily find the action being called by using operationContext.IncomingMessageHeaders.Action. The action looks like this: http://MyService/IMyContract/MyAction1. This would be easy to parse.
Unfortunately, when using REST EndPoints, the Action message header is blank. So where is it the information stored? In an obscure untyped “data” property found in the UriTemplateMatch. When debugging, you’ll see that this property is actually an instance of type WebHttpDispatchOperationSelector+WCFLookupResult, which is a private class. It exposes 2 properties – Method (HTTP method) and OperationName (the method called on the service, which is what we’re looking for).
Why the WCF team would decide to hide the OperationName is beyond me. I kept thinking I was missing something, I went through every single property of the OperationContext class (a never-ending project), and I just couldn’t find it anywhere. How are you supposed to authorize a service call if you don’t know what action is called on the service??
My current workaround is to use reflection. Here’s the method:
protected override bool CheckAccessCore(OperationContext operationContext)
{
[...]
string operationName = getOperationName(match.Data);
bool isAuthorized = IAuthorizationManager.Validate(username, id, operationName);
}
private static object operationNameLock = new Object();
private static PropertyInfo operationNamePropertyInfo;
private static string getOperationName(object data){
if(operationNamePropertyInfo == null){
lock(operationNameLock){
operationNamePropertyInfo = data.GetType().GetProperty("OperationName");
}
}
try{
return operationNamePropertyInfo == null ? null : operationNamePropertyInfo.GetValue(data, null) as string;
}catch{
return null;
}
}
WCF, REST & POX – a few notes about serialization
I usually use 2 types of endpoints when using WCF: SOAP for server - server communication, and REST/JSON for browser – server service calls. But yesterday, while trying to debug some POST & PUT HTTP calls in Fiddler, I decided to use a REST/XML endpoint. After all, POX is a bit easier on the eyes than JSON, and this would give me an opportunity to test my XML endpoints, which I had created but never used.
Of course all I got were HTTP 400 responses – Method not allowed. The call would fail before ever reaching the service, and therefore was probably related to some DataContractSerializer exception.
My first thought was to use the .Net source code and inspect was going on inside WCF. I had been using the “Enable .Net Source Stepping” feature of VS 2008 for a while, and it had helped immensely for various System.Web & System.Net issues. Unfortunately, I found out WCF libraries (System.ServiceModel etc…) are not included. I sent a quick not to Scott Guthrie (who posted a year ago that those libraries would be coming soon) asking about for an ETA, and he was kind enough to forward my request to Bob Dimpsey, but I haven’t heard back yet (EDIT: Bob did get back to me last night, it looks like this will be coming out soon, but no ETA yet). So for now, no WCF source code.
Next I found some resources on how to configure WCF tracing. I configured the diagnostics to trace to the main output output window and finally figured out what was going on.
It turns out the DataContractSerializer was requiring a namespace in my message’s XML contents. Duh. I had forgotten to define the xml namespace in the DataContract. I fixed the problem, changed the message to added the xmlns attribute to the root of my xml data, and things started working.
Well, sort of. Some of my properties were missing in the deserialized object. After looking a bit deeper, it turns out DataContractSerializer forces specific ordering of your XML elements. When adding the DataMember attribute, there’s a way to specify an “Order” property. If that property is not defined, DataContractSerializer defaults to alphabetized names. This means if your class has 2 properties Name, FirstName defined, your XML data will need to list <FirstName /> <LastName /> – ordered alphabetically. Apparently it has something to do with performance and versioning.
Once I made sure my elements were alphabetized, I was able to text my REST services using XML without any problem.
Friday, October 24, 2008
Amazon EC2 for Windows is out
I was pretty excited when I received the email this morning (here's a link to the announcement). After looking at the details though - not so much.
The good news is their pricing is cheaper than I originally planned, with basic offering starting at $.125/hour (without authentication). The SQL Server instances are pretty pricey though, starting at $1.10/hr. That's roughly $750/mo. But for a large app you'll probably want to run a high-cpu instance, which will cost you twice as much.
Considering we own a license of SQL Server Standard, I'm guessing we should be able to create our own AMI on top of a base 64-bit AMI and run a high-cpu medium instance for $0.30/hr. That's not too bad.
There are still a few kinks to work out. The C: drive is still limited to 10GB, which can be problematic when installing large applications like Visual Studio. SQL Server itself requires 1.6GB available for temp files on the system drive. I don't know yet if that'll be a problem.
My only real grief is... right now Windows Server 2003 is the only supported OS. I had not planned for that, and it's a real bummer. We built our new app to run on IIS 7, and one of our new audio conversion features requires Windows Server 2008. Our ASP.NET mvc routes won't work on IIS 6, and a couple other custom http modules won't work either.
Windows Server 2008 requires minimum of 10GB available on the system drive (40GB recommended), which is the maximum available on EC2. Could this be the problem? Is this an AMI bundling limitation that will be difficult to circumvent? I really don't know. But considering EC2 is the front-runner in cloud computing platforms, limiting our options to a 5 year-old OS is pretty disappointing.
For now, I guess I'll need to give GoGrid another look.
----------------------------------------------------------
EDIT - this was posted yesterday (Oct 26th) on EC2 forums:
Thanks for all of your feedback. Our intention is to support the widest variety of options for our customers that we can. We are already working to support Windows 2008 in EC2, and anticipate being able to offer it publicly in the early part of next year.
Friday, October 03, 2008
Windows Cloud
Back in January I wrote about our serious need for EC2 to run Windows OSes, and since I couldn't get more info from Amazon (although Flexiscale's CEO was kind enough to post more info in a comment) I pretty much gave up and started looking elsewhere for a pay-as-you-go clustering platform to run our .Net code and SQL Server database. We built our architecture to run certain conversions on Linux using EC2, using S3 for file storage, and gluing it all with SQS. I looked closely at GoGrid to run our asynchronous processes running on .Net, IIS web servers and SQL Server database. I wasn't too excited about having to architect a solution that would run on 2 separate networks with a need to potentially transfer files back and forth between S3 and GoGrid, but our only other options were to either
- Re-write the whole app to run on Linux (php/java/RoR + mysql) - not gonna happen
- Use Mono. We seriously looked into it and wrote a bunch of code to run our .Net conversion service on Mono, but in the end we encountered weird errors in System.Drawing - it looks like some image formats are not properly supported. So we gave up on that - nice idea, just not stable enough yet.
GoGrid looks nice, pretty UI, easy setup, included load-balancer, and cheap SDL Server licensing. I don't like the fact that you can't create your own images - deploying more nodes in your web cluster would require installing your app on each node. They're working on it though.
Then lo and behold, on Oct 1st I received an email from Amazon announcing upcoming support for Windows and SQL Server on EC2. What a relief! This removes the need for a file transfer queue between both networks and will simplify our app quite a bit. We can still use GoGrid for failover database and web server, but the core app should be able to run 100% on EC2. Yay!
In the web 2.0 storm, where's Microsoft's cloud?
Amazon (AWS), SalesForce (Force.com), Google (app engine) and Yahoo (upcoming) are all big players in the cloud computing space. I have a hard time believe Microsoft is just focusing on upgrading their OS and doesn't see the value of hosting services. Windows Live, Office Live, SharePoint Live, Exchange Live and the new SQL Server Data Services show their commitment.
Back in March I referred to Microsoft "top-secret" cloud computing project. When I asked Steve Ballmer about an Amazon EC2 windows equivalent, he mentioned that something was in the works, and we'd know more over the next 3 months.
Well, it's been over 6 months, and I haven't heard much. Until Amazon made their announcement a couple days ago. Today it looks like Steve Ballmer decided to react to Amazon's new offering by telling the world about a new Windows OS, temporarily named "Windows Cloud". The official announcement should come in 4 weeks.
I expect the service to be pricier than Amazon's and targeted primarily to enterprise customers looking for reliability and a decent SLA, 2 things that AWS is currently sorely missing. We've see S3 and SQS outages, and the S3 SLA is nothing to write home about.
But it's good to know MS is keeping up. I love .Net 3.5, Linq, C#, ASP.Net MVC, IIS 7, SQL Server and Visual Studio 2008. These are great modern developer products and platforms, they make me very productive. I have nothing against LAMP and Linux in general, we do use those platforms for Java and C/C++ applications. But VS and .Net offer so much out of the box they pay for themselves after 2 months of work.