Skeddly Blog

Skeddly news and announcements...

Joining Elastic Beanstalk to AWS Directory Service

AWS Directory Service is a managed Microsoft Active Directory solution. Using this service, you can manage the identities for your organization, and by joining EC2 instances to your directory domain, authorize access to your AWS resources.

AWS Elastic Beanstalk is a deployment service for your web applications. By configuring your Elastic Beanstalk environment, AWS can manage the scaling and high-availability for your web application. Supported environments include NodeJS, Ruby, and .NET, among others.

If you have an existing .NET application, or are building one that utilizes “Windows Authentication”, you can deploy your application using Elastic Beanstalk, and use Directory Service as your Windows authentication directory.

Ideally, we would implement this combination using off-the-shelf Elastic Beanstalk AMI images. However, due to an issue with Elastic Beanstalk hostnames being re-used, we’ll need to create a custom AMI image to be used by our Elastic Beanstalk applications.

The custom AMI will only address the hostname issue. This way, once AWS fixes it on their end, the custom AMI can be done away with.

We have employed these techniques in our own internal applications and we wanted to share them with anyone else who wanted to do the same.

This article will walk you through these steps:

  1. Setting up the domain.
  2. Creating the SSM document used to join EC2 instances to the domain.
  3. Configuring your ASP.NET application for Windows Authentication, and installing Windows Authentication on your EC2 instances.
  4. Configuring your ASP.NET application to join your domain.
  5. Creating an IAM Role for your EC2 instances.
  6. Creating a custom AMI.
  7. Creating your Elastic Beanstalk application.

Setting Up the Domain

The first requirement for this setup is to have a directory created using AWS Directory Service. In this example, I have setup a “small” Simple AD directory.

Unfortunately, AWS does not include a built-in method to manage users in your directory. So once you have your directory, you will need to connect an EC2 instance to your directory to manage users using the traditional Active Directory management tools.

For more information about this, please see the following articles in the AWS documentation:

Preparing the SSM Document

In order to join an EC2 instance to an AWS directory, you need to have an SSM document that’s designed to do that.

If you join an EC2 instance to an AWS directory using the AWS Management Console, then the Launch Instance wizard will create one for you automatically. That document may be good enough for your purposes.

You can look in the AWS Management Console to see if you already have one defined. Go to the EC2 Management Console, and go to “Documents” page under the “Commands” section in the left navigation bar. You should see a list of SSM documents with names such as “AWS-RunPowerShellScript”.

If you already have a document with a name such as “awsconfig_Domain_d-906723588a_corp.example.com”, then you can use that. Otherwise, you will need to create a new document with the following contents.

{
    "schemaVersion": "1.0",
    "description": "Sample configuration to join an instance to a domain",
    "runtimeConfig": {
        "aws:domainJoin": {
            "properties": {
                "directoryId": "d-906723588a",
                "directoryName": "corp.example.com",
                "dnsIpAddresses": [
                    "172.31.12.42",
                    "172.31.59.123"
                ]
            }
        }
    }
}

You will need to populate the directoryId, directoryName, and dnsIpAddresses values from those from your AWS directory.

Once you have your JSON file, you can create the document using the AWS Management Console, or you can use the AWS CLI:

$ aws ssm create-document --content file://path/to/file.json --name "My_Custom_Config_File"

References:

Configuring The ASP.NET / MVC Application

We will use mechanisms built-in to Elastic Beanstalk to customize the EC2 instance as part of our deployment process. Your ASP.NET / MVC application will need to have some preparations done to perform these customizations.

Use Windows Authentication In Your Web Application

First off, make sure that your web application has Windows authentication enabled. For many ASP.NET or MVC application, this would be done in the web.config file.

<system.web>
  <authentication mode="Windows" />
</system.web>

Install and Enable Windows Authentication

To setup Windows Authentication, we are going to use a file in the .ebextensions folder. Elastic Beanstalk will detect this file and execute the commands inside.

This file will instruct Elastic Beanstalk to execute PowerShell commands to do the following:

  1. Install the Windows Authentication role.
  2. Enable Windows Authentication in our IIS application.

.ebxtensions/01-WindowsAuth.config:

commands:
  01-install-windows-auth:
    command: powershell.exe -ExecutionPolicy RemoteSigned "Add-WindowsFeature Web-Security,Web-Windows-Auth"
    ignoreErrors: true
    waitAfterCompletion: 5
  02-enable-iis-windows-auth:
    command: powershell.exe -ExecutionPolicy RemoteSigned "Set-WebConfigurationProperty -Filter '/system.webServer/security/authentication/windowsAuthentication' -Name Enabled -Value True -PSPath 'IIS:\' -Location 'Default Web Site'"
    ignoreErrors: true
    waitAfterCompletion: 5

If your web application is not being deployed directly to the “Default Web Site” site under IIS, then you will need to adjust the -Location value.

Join the Domain

Next, we’ll use another .ebextensions file to join the EC2 instance to our AWS Directory Server domain.

To join the instance to our domain, we create an SSM association between the EC2 instance and our SSM document that was created above. You’ll want to use the SSM document name in the placeholder below.

.ebxtensions/02-JoinDomain.config:

files:
  "C:\\Scripts\\JoinDomain.ps1":
    content: |
      Import-Module AWSPowerShell
      Start-transcript -Path C:\\JoinDomain-Transcript.txt -Force
      $instanceId = Invoke-RestMethod -uri http://169.254.169.254/latest/meta-data/instance-id
      $availabilityZone = Invoke-RestMethod -uri http://169.254.169.254/latest/meta-data/placement/availability-zone
      $region = $AvailabilityZone.Substring(0, $availabilityZone.Length - 1)
      Set-DefaultAWSRegion $region

      Try
      {
        New-SSMAssociation -InstanceId $instanceId -Name "your ssm document name"
      }
      Catch
      {
        $errorMessage = $_.Exception.Message
        "Exception: $errorMessage"
      }
      Stop-transcript

container_commands:
  01-join-domain:
    command: powershell.exe -ExecutionPolicy Bypass -File C:\\Scripts\\JoinDomain.ps1
    ignoreErrors: true
    waitAfterCompletion: 5

Package all of the above together into a deployment package and upload it to Elastic Beanstalk.

Creating the IAM Role For EC2

When you create your Elastic Beanstalk application, it will need to have an IAM Role for EC2 associated with the EC2 instances.

You will want to attach the AmazonEC2RoleforSSM Managed Policy to your role. This will allow the EC2 instances to look for and execute the SSM documents.

You’ll also need to give the IAM role permission to execute ssm:CreateAssociation. This is so that the EC2 instance can join the domain.

IAM Policy:

{
  "Version" : "2012-10-17",
  "Statement": [ {
    "Effect": "Allow",
    "Action": [
      "ssm:CreateAssociation"
    ],
    "Resource": "*"
  } ]
}

Creating the Custom AMI

AMI images provided by AWS for Elastic Beanstalk have a “quirk” in that all EC2 instances launched will have the same hostname. This prevents all but the first EC2 instance from connecting to the directory correctly.

Until AWS addresses this with fixed AMI images, we’ll need to address it ourself. As a workaround, we’ll create a custom AMI image that will create EC2 instances with unique hostnames.

  1. Pick your favourite Elastic Beanstalk AMI image.
  2. Launch an EC2 instance using this AMI image. Just launch it as a standard EC2 instance, not as part of an Elastic Beanstalk application.
  3. Once ready, RDP into the EC2 instance.
  4. Run the “Ec2ConfigService Settings” application from the Start menu.
  5. On the “General” tab, turn on “Set Computer Name”. This will deal with the hostname issue.
  6. On the “Image” tab, select “Random” under “Administrator Password”.
  7. Click “Shutdown with Sysprep”. Click “Yes” when prompted for confirmation.

Sysprep will do some work for a few minutes, then shutdown the EC2 instance automatically.

Once the instance is shutdown, create an AMI image from the EC2 instance.

Creating the Elastic Beanstalk Application

Finally, you’ll need to create your Elastic Beanstalk application. You can use the Elastic Beanstalk wizard to create it, paying attention to these points:

  • Create a .NET Web Server Environment Application. At the time of this writing, the current platform version was “IIS 8.5 on 64bit Windows Server 2012 R2 v1.2.0”.
  • Upload your deployment package that has Windows Authentication installing and configured to use.
  • Create the environment inside a VPC. Select the same VPC that contains your AWS Directory.
  • For the instance profile, select the instance profile/role that you created above with the SSM permissions.

Once the environment is up and running, there are some post-launch configuration changes that you’ll need to make.

  • In the “Instances” section, set “Custom AMI ID” to your custom AMI image ID.
  • Your health check will need to access a page on your web application that is not protected by Windows Authentication. Anonymous access must work for the health check.
  • Due to the way ELB interacts with Windows Authentication data, you’ll need to change your application’s configuration to use a TCP listener on port 80 instead of an HTTP listener. This is done in the “Load Balancing” section.
  • This also means that HTTPS termination on ELB will not work. You may be able to install an SSL certificate on your EC2 instances (or make a custom AMI), then use an SSL listener on your ELB.

Once your environment is up-and-running, you can check the EC2 instance’s console logs to verify that they have joined the domain correctly. See Getting the Domain Join Status for more information about that. In our experience, it may take a while for the instances to join. In some cases, it took 15 to 30 minutes.

At this point, you should be able to point a browser at your ELB’s endpoint. When required, provide your username/password from your AWS directory. You may need to include your domain’s short name with the username, such as “CORP\myusername”.

Additional Notes

As your application scales in/out, new EC2 instances will associate with your domain. There are two places you will want to periodically clean them up:

  • In your Active Directory computer list. Using the Active Directory tools, you will want to clean up old computers.
  • Old EC2 instances will have stale SSM associations. Use an AWS CLI command like the following to list all existing association for a particular SSM document:
$  aws ssm list-associations --region us-east-1 --association-filter-list key=Name,value="awsconfig_Domain_d-906723588a_corp.example.com"

Also, since HTTP is being used, rather than HTTPS, you should connect to your web application over a secured VPN rather than over the open internet.

Sample Code

We have put a sample ASP.NET MVC application on GitHub for your reading pleasure. It includes a CloudFormation template you can use to get started. It will create:

  1. A small Simple AD directory,
  2. The SSM document to join the domain,
  3. IAM Role to be used by your Elastic Beanstalk EC2 instances.

The web application:

  • Includes the .ebextensions file used to setup Windows Authentication.
  • Includes the .ebextensions file used to join the domain. You will need to edit this file.
  • Includes a deployment profile (Publish…) which will create a ZIP file you can upload to Elastic Beanstalk.

The project can be found at https://github.com/eleven41/JoinElasticBeanstalkToAWSDirectoryService.

Credits

A big thanks to Shyam K. in the Sydney AWS Support Centre. Shyam was very helpful in working out some troublesome “kinks” in this setup.

About Skeddly

Skeddly is the leading scheduling service for your AWS account. Using Skeddly, you can:

  • Reduce your AWS costs,
  • Schedule snapshots and images, and
  • Automate many DevOps and IT tasks.

Sign-up for our 30 day free trial or sign-in to your Skeddly account to get started.

<