Monthly Archives: February 2012

Helping Your Teams To Success

I was going to write a big post about this but every time I come to work on it I find I spend more time that I intend to capture what is actually a fairly easy to understand set of principles that I’ve been lucky enough to pickup during my time working on software delivery teams.  So here they are:

Flexibility: a lot of people join teams expecting to play only one role.  While specialists can probably expect this to be the case, the rest of us should really be prepared to play whatever role is required depending on circumstances.  I’ve seen a lot of people who have been unprepared to show role flexibility where the result is that some aspect of the project delivery is negatively impacted.  Software delivery is not all glamour – there’s a lot of grinding leg work needs doing on any project in order for it to succeed.

Commitment: think about how many teams you’ve been on where you’ve had people who have contributed but ultimately their contribution is dwarfed by others on the team.  What commitment boils down to is doing the things you say you will do by the time you said you will do them (or otherwise early and clearly communicating why they won’t be done). 

Respect: pretty much all the best working relationships I’ve had (and continue to have) are based on respect.  If you approach all relationships you have with your team mates on this basis you will find that your team is far more successful than it could ever be otherwise.  Yes, if you are having challenges with other team mates around "101" concepts it can be a bit hard to proffer respect for them, however you should try your hardest to make it work and to find elements that you can respect. 

Communicate: be open and keep everyone across what you’re doing.  Be pro-active about communicating – don’t wait for someone to ask you for information.  The one proviso here is that you determine what is appropriate for others to be told.  I lean more towards over-communication – people will soon let you know if they don’t need to know the level of detail you are giving them.

Reliable: don’t be random.  Try to keep your behaviour the same as often as you can – people should come to expect you to behave in a certain way in a certain situation.  Some of being reliable goes back to commitment: do the things you say you are going to do when you said you will do them.  Delivery of any project is challenging enough without having team members going off-piste and doing ad-hoc work that was never on the cards.  Ultimately if people don’t find you reliable then you will struggle to be trusted and trust is certainly at the core of any great team.

Well I guess I could ramble about this topic ad nauseum and if you’re reading this far then I salute you!

As always, any discussion of teams and teamwork would not be complete without the obligatory Dilbert cartoon! 

Happy teaming, enjoy!

Tagged , , , ,

Getting Web Deploy Working For Non-Admin Logins

There’s a lot of good information around online about how to get Web Deploy (a.k.a. msdeploy) working.  What most of the information tends not to cover is how to get it functioning for non-admin users.

In this post I’m going to cover the steps to go through to get a non-Admin windows user working for deployments.

The Foundation

First of all, let’s get the basics out of the way.  This is the environment these instructions are applicable to:

  1. Windows Server 2008 R2 (with SP1).
  2. Web Role (IIS) Installed – make sure you have installed the Management Service (see below).
  3. Windows Firewall on but with an Inbound allow rule for TCP traffic on port 8172.
  4. You have downloaded Web Deploy.

Management Service Installed.

Now we have the main bits ready to go we need to setup Web Deploy.

Install and Configure Web Deploy

When you install Web Deploy you need to make sure all components are available.  Either select ‘Complete’ or ‘Custom’ when prompted for what to install.  You should find that the components to install looks like the following.

What items from Web Deploy you need to select.

Once you have finished the installation you can verify the state of your configuration by reviewing your server and you should find:

1. A new local user called WdeployAdmin.

2. Two new services – Web Deployment Agent Service and the Web Management Service.

New Services Installed.

Add Windows Login

We’re going to be using a non-Admin user for our deployments so lets go ahead and add a new Standard Windows login (i.e. one that is not an Administrator).

Note: Username and password should be chosen with care – in some deployment scenarios your password (particularly) may cause issues if it has characters that cannot be included in XML without being escaped. A simple rule of thumb is to avoid &, < and >.

Tip: If you have authentication issues test using a simple password that has no special characters.

Configure Management Service

We need configure the management service to allow remote connections and (in this instance) to only allow Windows credentials (the default).

Open up the IIS Manager on your server and ensure you have Features View on in the right pane.

Look for the Management Group (usually at the bottom) and then within that group select Management Service (see below).

Management Service Highlighted in Blue.

When this view opens you will most likely find the form is disabled – this is because the service is running – you can’t change the configuration.  If you look at the right pane you will see an option to Stop the service.

Make sure to check the ‘Enable remote connections’ option and to leave the ‘Windows credentials only’ selected (as below).  Now restart the service.

Management Service Configuratoin

Grant Windows Login IIS Manager Permissions

You can now grant the non-Admin user you created earlier the rights to manage sites on your IIS machine.

In the left pane of the IIS Manager select the site you wish to add your Windows login as a manager for (you will need to repeat for each site).

In the right pane you should see a Management group with two options (Configuration Editor and IIS Manager Permissions).  Open the IIS Manager Permissions view.

In the new view that opens on the right hand pane near the top you should see ‘Allow User…’ – click on it and a popup will appear.

From the popup you can select the Windows user you wish to add – click on the Select button and then search for the user you create previously.  Finally click OK on the two dialogs so you return to the initial screen where you will see a new entry for your user (sample below).

User View once granted access to deploy.

The Missing Link

I can almost guarantee you at this point that if you run the deployment it will fail.  This is something I spent a fair amount of time trying to troubleshoot and so I have this advice for you:

The non-Admin Windows login you granted IIS Manager Permissions to must be able to read / write to the root folder location that the IIS site is deployed to.

Using this approach I’ve been able to get non-Admin users publishing successfully so the approach should work for you too.

May 2012 – Updated!

One important addition to add to all of the above.

When you setup Web Deploy it will create a two local users on the host that have priveleges to setup IIS sites and modify configuration files.  The logins are WDeployAdmin and WDeployConfigWriter.

If you find that after a period of time Web Deploy starts giving errors and not deploying it is most likely due to the passwords for these users expiring and Windows setting the “user must change password on next logon” flag (assuming you left the default password policy in place on your Windows server).  Either set the password not to expire or update it and clear the next logon flag.

Tagged , , , , ,

Amazon AWS Elastic Load Balancers and MSBuild – BFF

Our jobs take us to some interesting places sometimes – me, well, recently I’ve spent a fair amount of time stuck in the land of Amazon’s cloud offering AWS.

Right now I’m working on a release process based around MSBuild that can deploy to a farm of web servers at AWS.  As with any large online offering ours makes use of load balancing to provide a reliable and responsive service across a set of web servers.

Anyone who has managed deployment of a solution in this scenario is most likely familiar with this approach:

  1. Remove target web host from load balancing pool.
  2. Update content on the web host and test.
  3. Return web host to load balancing pool.
  4. Repeat for all web hosts.
  5. (Profit! No?!)

Fantastic Elastic and the SDK

In AWS-land load balancing is provided by the Elastic Load Balancing (ELB) service which, like many of the components that make up AWS, provides a nice programmatic API in a range of languages.

Being focused primarily on .Net we are we are happy to see good support for it in the form of the AWS SDK for .NET.  The AWS SDK provides a series of client proxies and strongly-typed objects that can be used to programmatically interface with pretty much anything your AWS environment is running.

Rather than dive into the detail on the SDK I’d recommend downloading a copy and taking a look through the samples they have – note that you will need an AWS environment in order to actually test out code but this doesn’t stop you from reviewing the code samples.

Build and Depoy

As mentioned above we are looking to do minimal manual intervention deployments and are leveraging MSBuild to build, package and deploy our solution.  One item that is missing in this process is a way to take a target machine out of the load balancer pool so we can deploy to it.

I spent some time reviewing existing custom MSBuild task libraries that provide AWS support but it looks like many of them are out-of-date and haven’t been touched since early 2011.  AWS is constantly changing so being able to keep up with all it has to offer would probably require some effort!

The result is that I decided to create a few custom tasks so that I could use for registration / deregistration of EC2 Instances from one or more ELBs.

I’ve included a sample of a basic RegisterInstances custom task below to show you how you go about utilising the AWS SDK to register an Instance with an ELB.   Note that the code below works but that it’s not overly robust.

The things you need to know for this to work are:

  1. Your AWS Security Credentials (Access and Secret Keys).
  2. The names of the ELBs you want to register / deregister instances with.
  3. The names of the EC2 Instances to register / deregister.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using Amazon.ElasticLoadBalancing;
using Amazon.ElasticLoadBalancing.Model;

namespace TheFarm.MsBuild.CustomTasks.Aws.Elb
{
    /// <summary>
    /// Register one or more EC2 Instance with one or more AWS Elastic Load Balancer.
    /// </summary>
    /// <remarks>Requires the AWS .Net SDK.</remarks>
    public class RegisterInstances : Task
    {
         /// <summary>
         /// Gets or sets the load balancer names.
         /// </summary>
         /// <value>
         /// The load balancer names.
         /// </value>
         /// <remarks>The account associated with the AWS Access Key must have created the load balancer(s).</remarks>
         [Required]
         public ITaskItem[] LoadBalancerNames { get; set; }

         /// <summary>
         /// Gets or sets the instance identifiers.
         /// </summary>
         /// <value>
         /// The instance identifiers.
         /// </value>
         [Required]
         public ITaskItem[] InstanceIdentifiers { get; set; }

         /// <summary>
         /// Gets or sets the AWS access key.
         /// </summary>
         /// <value>
         /// The aws access key.
         /// </value>
         [Required]
         public ITaskItem AwsAccessKey { get; set; }

         /// <summary>
         /// Gets or sets the AWS secret key.
         /// </summary>
         /// <value>
         /// The aws secret key.
         /// </value>
         [Required]
         public ITaskItem AwsSecretKey { get; set; }

         /// <summary>
         /// Gets or sets the Elastic Load Balancing service URL.
         /// </summary>
         /// <value>
         /// The ELB service URL.
         /// </value>
         /// <remarks>Will typically take the form: https://elasticloadbalancing.region.amazonaws.com</remarks>
         [Required]
         public ITaskItem ElbServiceUrl { get; set; }

         /// <summary>
         /// When overridden in a derived class, executes the task.
         /// </summary>
         /// <returns>
         /// true if the task successfully executed; otherwise, false.
         /// </returns>
         public override bool Execute()
         {
             try
             {
                  // throw away - to test for valid URI.
                  new Uri(ElbServiceUrl.ItemSpec);

                  var config = new AmazonElasticLoadBalancingConfig { ServiceURL = ElbServiceUrl.ItemSpec };

                  using (var elbClient = new AmazonElasticLoadBalancingClient(AwsAccessKey.ItemSpec, AwsSecretKey.ItemSpec, config))
                  {
                        foreach (var loadBalancer in LoadBalancerNames)
                        {
                              Log.LogMessage(MessageImportance.Normal, "Preparing to add Instances to Load Balancer with name '{0}'.", loadBalancer.ItemSpec);

                              var initialInstanceCount = DetermineInstanceCount(elbClient, loadBalancer);

                              var instances = PrepareInstances();

                              var registerResponse = RegisterInstancesWithLoadBalancer(elbClient, loadBalancer, instances);

                              ValidateInstanceRegistration(initialInstanceCount, instances, registerResponse);

                              DetermineInstanceCount(elbClient, loadBalancer);
                        }
                  }
             }
             catch (InvalidInstanceException iie)
             {
                   Log.LogError("One or more supplied instances was invalid.", iie);
             }
             catch (LoadBalancerNotFoundException lbe)
             {
                   Log.LogError("The supplied Load Balancer could not be found.", lbe);
             }
             catch (UriFormatException)
             {
                   Log.LogError("The supplied ELB service URL is not a valid URI. Please confirm that it is in the format 'scheme://aws.host.name'");
             }

             return !Log.HasLoggedErrors;
        }

        /// <summary>
        /// Prepares the instances.
        /// </summary>
        /// <returns>List of Instance objects.</returns>
        private List<Instance> PrepareInstances()
        {
            var instances = new List<Instance>();

            foreach (var instance in InstanceIdentifiers)
            {
                Log.LogMessage(MessageImportance.Normal, "Adding Instance '{0}' to list.", instance.ItemSpec);

                instances.Add(new Instance { InstanceId = instance.ItemSpec });
            }
            return instances;
        }

        /// <summary>
        /// Registers the instances with load balancer.
        /// </summary>
        /// <param name="elbClient">The elb client.</param>
        /// <param name="loadBalancer">The load balancer.</param>
        /// <param name="instances">The instances.</param>
        /// <returns>RegisterInstancesWithLoadBalancerResponse containing response from AWS ELB.</returns>
        private RegisterInstancesWithLoadBalancerResponse RegisterInstancesWithLoadBalancer(AmazonElasticLoadBalancingClient elbClient, ITaskItem loadBalancer, List<Instance> instances)
        {
            var registerRequest = new RegisterInstancesWithLoadBalancerRequest { Instances = instances, LoadBalancerName = loadBalancer.ItemSpec };

            Log.LogMessage(MessageImportance.Normal, "Executing call to add {0} Instances to Load Balancer '{1}'.", instances.Count, loadBalancer.ItemSpec);

            return elbClient.RegisterInstancesWithLoadBalancer(registerRequest);
        }

        /// <summary>
        /// Validates the instance registration.
        /// </summary>
        /// <param name="initialInstanceCount">The initial instance count.</param>
        /// <param name="instances">The instances.</param>
        /// <param name="registerResponse">The register response.</param>
        private void ValidateInstanceRegistration(int initialInstanceCount, List<Instance> instances, RegisterInstancesWithLoadBalancerResponse registerResponse)
        {
            var postInstanceCount = registerResponse.RegisterInstancesWithLoadBalancerResult.Instances.Count();

            if (postInstanceCount != initialInstanceCount + instances.Count)
            {
                 Log.LogWarning("At least one Instance failed to register with the Load Balancer.");
            }
        }

        /// <summary>
        /// Determines the instance count.
        /// </summary>
        /// <param name="elbClient">The elb client.</param>
        /// <param name="loadBalancer">The load balancer.</param>
        /// <returns>integer containing the instance count.</returns>
        private int DetermineInstanceCount(AmazonElasticLoadBalancingClient elbClient, ITaskItem loadBalancer)
        {
             var response = elbClient.DescribeLoadBalancers(new DescribeLoadBalancersRequest { LoadBalancerNames = new List<string> { loadBalancer.ItemSpec } });

             var initialInstanceCount = response.DescribeLoadBalancersResult.LoadBalancerDescriptions[0].Instances.Count();

             Log.LogMessage(MessageImportance.Normal, "Load Balancer with name '{0}' reports {1} registered Instances.", loadBalancer.ItemSpec, initialInstanceCount);

             return initialInstanceCount;
        }
    }
}

So now we have a task compiled into an assembly we can reference that assembly in our build script and invoke the task using the following syntax:


    <RegisterInstances LoadBalancerNames="LoadBalancerName1" InstanceIdentifiers="i-SomeInstance1" AwsAccessKey="YourAccessKey" AwsSecretKey="YourSecretKey" ElbServiceUrl="https://elasticloadbalancing.your-region.amazonaws.com" />

That’s pretty much it… there are some vagaries to be aware of – the service call for deregistration of an Instance returns prior to the instance being fully de-registered with the load balancer so don’t key any deployment action directly off of that return – you should perform other checks first to make sure that the instance *is* no longer registered prior to deploying.

I hope you’ve found this post useful in showing you what is possible when combining the AWS SDK with the extensibility of MSBuild.

Tagged , , ,