Auto scale groups with domain join

Ned Bellavance
3 min read


When it comes to AWS, it often feels like Windows is a second class citizen.  I have been doing work in the Azure public cloud for a long time, where the situation is somewhat reversed.  In AWS, the commands, assumptions, and example use cases are almost always in Linux with Windows as a bit of an after thought.  I’ve been doing a lot of AWS work recently, and one of the things that came up was the ability to deploy AutoScale groups and Launch Configurations using CloudFormation.  By itself, that process is relatively straightforward, especially if you are working in a Linux context.  But what if you are deploying Windows boxes in a domain and want them to have a specific hostname structure?  That’s a bit more tricky, but I got it figured out.

The first thing to know is that I am deploying this configuration in an existing domain, and I am using CloudFormation to do it.  The AutoScale group is part of a larger environment that includes various SQL servers, web servers, and application servers.  The requirement to have domain-joined servers stems from a need to centrally apply policies and deploy software.  The members of the AutoScale group fall under that umbrella, so they need to be part of the domain.  There are two parts to the deployment, the first is the CloudFormation resources that deploy the Launch Configuration and AutoScale group, and then there is the PowerShell script that helps perform the renaming of the servers.  The full stack template and PowerShell script are included at the end of the post.

Here is the AutoScalingGroup resource:

[code] “MyServerAutoScalingGroup”: { “Type”: “AWS::AutoScaling::AutoScalingGroup”, “Properties”: { “LaunchConfigurationName”: { “Ref”: “MyServerLaunchConfiguration” }, “DesiredCapacity” : “2”, “MaxSize” : “5”, “MinSize” : “1”, “VPCZoneIdentifier” : [{“Ref” : “AutoScaleSubnetId”}] } } [/code]

The VPCZoneIdentifier refers to a subnet Id that I grab from the parameters in the template.

The LaunchConfiguration resource has a few things worth noting.  In the Metadata section I have added an AWS::CloudFormation::Init section, which is how each instance gets configured.  Most of it is straightforward, like creating the configuration file for the cfn-auto-reloader.  There is a PowerShell script that gets loaded to handle the renaming of the server.  The source is an S3 bucket that is defined in the template parameters.

[code] “c:\\cfn\\scripts\\Rename-LCComputer.ps1” : { “source” : { “Fn::Join” : ["", [ “https://”, { “Ref” : “BucketName” }, “” ]]} } [/code]

Within the rename configset the script is called.

[code] “a-execute-powershell-script-RenameComputer”: { “command”: { “Fn::Join” : ["", [ “powershell.exe -executionpolicy unrestricted -command “, “c:\\cfn\\scripts\\Rename-LCComputer.ps1 -ServerPrefix “, {“Ref” : “ServerPrefix”}, " -DCIPAddress \””, { “Ref” : “ADServer1PrivateIP” }, “\” -credentials “, “(New-Object System.Management.Automation.PSCredential(’”, { “Ref”: “DomainNetBIOSName” }, “\\”, { “Ref” : “DomainAdminUserName” }, “’,”, “(ConvertTo-SecureString ‘”, { “Ref”: “DomainAdminPassword” }, “’ -AsPlainText -Force)))” ]] }, “waitAfterCompletion”: “forever” } [/code]

The script takes a ServerPrefix value that is used to figure out the server name. It queries Active Directory for all servers that begin with the prefix and sorts them in descending order.  If no servers are found, it tacks on an 01 to the prefix and renames the server.  If existing servers are found, it takes the last one and adds one to it, e.g. is myservername03 is found, server is renamed myservername04.  The script will work up to 99 servers.  The rest of the Init changes the DNS servers for the network interface and joins the instance to the domain.  In order to access the S3 bucket, I have included an AWS::CloudFormation::Authentication section.

[code] “AWS::CloudFormation::Authentication” : { “S3AccessCreds” : { “type” : “S3”, “accessKeyId” : { “Ref” : “CfnKeys” }, “secretKey” : {“Fn::GetAtt” : [“CfnKeys”, “SecretAccessKey”]}, “buckets” : [ { “Ref” : “BucketName” } ] } } [/code]

Here is the full stack template:

And here is the PowerShell script:

Share and enjoy!