How to Join and Unjoin AWS EC2 in ASG with AWS Directory Service
Purpose
The purpose of this blog is to demonstrate the following:
- How to create a Windows Custom AMI for successfully launching in an ASG.
- How to join a EC2 launched in an Autoscaling group(ASG) automatically into an existing AD(Active Directory).
- Further to above, if an EC2 within ASG gets terminated, then the EC2 hostname added as AD (Active Directory) object should be removed from Active Directory.
On successful execution of the following steps, following will be achieved:
- New EC2 instance in ASG becomes part of the AD domain.
- A new hostname is added as AD Object.
- AWS SSM document retrieves the new EC2 association successfully.
- New instance in EC2 ASG gets a new hostname.
- New instance can be accessed using the specified password as well as AD credentials.
- If the instance in ASG is terminated, the hostname entry also gets removes from AD.
Note: The joining can be done via Ec2 ASG lifecycle hooks. In this blog, instead of lifecycle hooks, user data script on launch is used.
Let us first create a custom Windows AMI.
We will then see how to achieve this in two parts — Join EC2 with AD and Remove EC2 from AD.
Custom Windows AMI Approach:
We will use 2 EC2 Instances outside Auto scaling Groups.
- First EC2 instance will be created from a standard AWS or you can create from a custom golden image. This will be further custom configured, which we will see in the steps later, for launching EC2 in an AD domain. Let us tag this EC2 instance as inst-golden. The inst-golden will be also used to test the goal of the PoC i.e. joining with AD and removing from the AD.
- Second EC2 instance, tagged as inst-custom will be created from the image of the inst-golden. This is essentially a clone of inst-golden but will be sysprepped. After sysprep, it will lose some of the configurations done in inst-golden, for example after sysprep, this instance will no more be in the AD domain.
Steps:
a) Create an AWS directory service.
Note the AD account details. An example of created directory for this blog is as follows.
b) Launch an EC2 from an AWS/Base Custom Image and tag as inst-golden.
- While creating the instance, provide the above directory information in EC2 launch wizard.
The first time you specify a domain in the EC2 launch wizard, the wizard generates the domain’s default SSM document.
- The role to be applied needs to include the policy AmazonEC2RoleforSSM-ASGDomainJoin and is created as per the details in this document.
For more details, refer part 2 / Step 1: Create a new IAM policy, copying the AmazonEC2RoleforSSM policy.
c) Remote Login into the launched Ec2 instance inst-golden using the RDP credentials.
d) Using ‘Server Manager’, select add Roles and Features, select Role-based or feature-based installation, and add role AD Domain Services. This will enable to access Active Directory Users and Computers.
e) Change the Administrator password under User Accounts.
f) Just Disconnect and verify if you can log into the Ec2 using below accounts
— Administrator and the new changed password.
— AD account.
g) When logged in with AD login, you will be able to access Active Directory Users and Computers and see OU=glad and under it Users and Computers.
Under Computers, you will be able to see the hostname of inst-golden.
f) Login back with Administrator account.
g) Ensure SSM is installed in the instance.
h) Ensure the policy: AmazonSSMDirectoryServiceAccess for SSM to communicate with AD, is added to the EC2 IAM role. Complete steps can be seen here.
i) Run the following command in Windows Powershell to schedule a Windows Task that will run the User data on next boot:
C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 –Schedule
j)Install (if not installed) and Open EC2Launch v2.
— Ensure Set Computer Name is unchecked.
This will enable to launch EC2 instances with unique host names.
— Administrator Password.Choose Specify and provide a password.
— Do a Shutdown with Sysprep.
More details are here.
After shutting down with sysprep, the EC2 instance as expected cannot be accessed using AD credentials.
g) Create Image of the above Ec2 instance, say ami-custom.
h) We will use this image ami-custom in AWS Launch Configuration.
Optionally, an EC2 instance can be launched from this image, and after launch you will notice:
— Able to login with Administrator and the specified password.
— Not able to login using AD login.
Join Approach
We will mainly follow the steps in the document here. However, while following this document, below are more details that will help achieve the results fast.
Steps:
- Under launch Configurations, use ami-custom. Fill in the obvious details.
- Under Advanced, add User data script as below with ‘persist’ tag. With true added, the issue of EC2 custom AMI not running bootstrap (user-data) can be avoided.
User data script
<powershell>
Set-DefaultAWSRegion -Region us-east-2
Set-Variable -name instance_id -value (Invoke-Restmethod -uri http://169.254.169.254/latest/meta-data/instance-id)
New-SSMAssociation -InstanceId $instance_id -Name “awsconfig_Domain_<ds-id>_glad.test.com”
</powershell>
<persist>true</persist>
- Set Metadata Accessible=Enabled.
- Create new Auto scaling Group from the already created Launch Configuration.
Tag the instances according to your policies or as simple as autoscale. The ASG will launch the desired instances. - Verify that the launched instances are joined to the AD.
- Log into the instance using
— Administrator and the password (specified earlier).
— AD account.
- Use following SSM command to see that EC2 is successfully associated.
aws ssm list-associations — association-filter-list key=Name,value=awsconfig_Domain_<ds-id>_glad.test.com
Remove Approach
The approach to remove the EC2 from the domain directory is to identify which EC2 has been terminated. To identify the same, configure the Auto Scaling group to use the autoscaling: EC2_INSTANCE_TERMINATE notification type, so that after the Auto Scaling group terminates an instance, it sends a notification. The recipient / subscriber of the notifications, in our case, will be a SQS topic. The SQS queue will then be polled for the messages, the EC2 instance id will be retrieved and the associated hostname will be removed from AD.
Let us get started.
- Create the SQS queue.
- Create the SNS topic
Create a new SNS topic and add a subscription to the SNS topic selecting ‘Amazon SQS’ as the endpoint, i.e.: arn:aws:sqs:us-east-2:{aws-account-id}:poc-removead - Configure providing permission to SNS, to be allowed to send the EC2 Termination message to SQS queue. In the SQS queue created in the prior step, select the ‘Access Policy’ tab.
4. Add below policy replacing the placeholders <>.
{
“Version”: “2012–10–17”,
“Id”: “SQSSendMessagePolicy”,
“Statement”: [
{
“Sid”: “SQS-Access”,
“Effect”: “Allow”,
“Principal”: “*”,
“Action”: “SQS:SendMessage”,
“Resource”: “arn:aws:sqs:us-east-2:<account-id>:poc-removead”,
“Condition”: {
“ArnEquals”: {
“aws:SourceArn”: “arn:aws:sns:us-east-2: :<account- id>::poc-removead” }
}
}
]
}
5. Configure the notification for the Auto Scaling Group.
Select the Auto Scaling Group and choose the ‘Notifications’ tab and then ‘Create notification’.
For the notification choose the option ‘Terminate’ and select the SNS topic created earlier.
6. Configure the IAM role
The EC2 instance that will be running a Powershell script to remove the instance, requires permissions to access the SQS queue. To allow this, add below policy.
{
“Version”: “2012–10–17”,
“Statement”: [
{
“Sid”: “VisualEditor0”,
“Effect”: “Allow”,
“Action”: [
“sqs:DeleteMessage”,
“sqs:GetQueueUrl”,
“sqs:ReceiveMessage”,
“sqs:GetQueueAttributes”
],
“Resource”: “arn:aws:sqs:us-east-2:<account-id>:poc-removead”
}
]
}
7. Prepare the Ec2 to run Powershell script.
— Open Windows Powershell ISE for AWS.
— Set-AWSCredential -AccessKey {access-key} -SecretKey {secret-key} `
-StoreAs default.
Note: Adding CLI credentials is just for prototype purpose. Better strategy to apply temporary tokens should be used in production enviornment .
8. Save and Run below Powershell script
#Function that logs a message to a text file
function LogMessage
{
param([string]$Message)
((Get-Date).ToString() + “ — “ + $Message) >> $LogFile;
}$LogFile = “C:PSLog.txt”#Full location of the log file#Delete log file if it exists
if(Test-Path $LogFile)
{
Remove-Item $LogFile
}$Message >> $LogFile;#Write the variable to the log file#Get the SQS queue URL
$queueurl = Get-SQSQueueUrl -queuename poc-removead -QueueOwnerAWSAccountId <account-id> -region us-east-2#Get the number of SQS messages in the queue
$messages = Get-SQSQueueAttribute -QueueUrl $queueurl -AttributeName ApproximateNumberOfMessages -Region us-east-2LogMessage -Message “messages:”;
$messages >> $LogFile;LogMessage -Message “messageCount:”;
$messageCount = $messages.ApproximateNumberOfMessages$messageCount >> $LogFile;#Loop through each message to remove the terminated server from #Active Directory
While ($messageCount -gt 0)
{
$messageCount-=1
$message = Receive-SQSMessage -QueueUrl $queueurl -Region us-east-2
LogMessage -Message “message:”;
$message >> $LogFile;
$jsonObj = $($message.Body) | ConvertFrom-Json
LogMessage -Message “jsonObj after ConvertFromJson:”;
$jsonObj >> $LogFile;
$id=$jsonObj.EC2InstanceId
LogMessage -Message “id after ConvertFromJson:”;
$id >> $LogFile;$consoleOutput = Get-EC2ConsoleOutput -InstanceId $id -Region us-east-2$bytes = [System.Convert]::FromBase64String($consoleOutput.Output)
$instanceIds = (Get-EC2Instance -Filter @(@{name=”platform”;value=”windows”})).Instances.InstanceId#Convert from Base 64 string
$bytes = [System.Convert]::FromBase64String($consoleOutput.Output)
$string = [System.Text.Encoding]::UTF8.GetString($bytes)#If the string contains RDPCERTIFICATE-SUBJECTNAME, we can extract the hostnameif($string -match ‘RDPCERTIFICATE-SUBJECTNAME: .*’) {
$windowsHostName = $matches[0] -replace ‘RDPCERTIFICATE-SUBJECTNAME: ‘}Get-ADComputer -Identity $windowsHostName | Remove-ADObject -Recursive -Confirm:$FalseRemove-SQSMessage -QueueUrl $queueurl -ReceiptHandle $message.ReceiptHandle -region us-east-2 -ForceLogMessage -Message “Done”;}
- Use following SSM commands to verify that EC2 hostname is successfully dis-associated.
Commands
aws ssm list-associations — association-filter-list key=Name, value=awsconfig_Domain_<ds-id>_glad.test.com
9. Schedule Task: The above powershell script is recommended to be scheduled as a task that runs at a business suitable interval
Further References
1. Approach and script to remove the terminated ADs is here. In this article, the powershell script does not work as is today and need some modifications. The modified script is as posted above.
2. Troubleshooting articles that may help in executing the mentioned steps of AD joining task.
· Access Hidden files in Windows.
· If the Ec2 association is not seen in SSM document, validate all the required configuration in this article.
· Unable to get Password of Instance launched from AMI issue
· Create Custom Windows AMI as here.
· Useful SSM Commands
- aws ssm delete-document --name awsconfig_Domain_<ds-id>_glad.test.com- aws ssm create-document --content file://<filename>.json --name awsconfig_Domain_<ds-id>_glad.test.com- aws ssm get-document --name awsconfig_Domain_<ds-id>_glad.test.com
· Sample SSM document
{
“schemaVersion”: “1.0”,
“description”: “Automatic Domain Join Configuration created by EC2 Console.”,
“runtimeConfig”: {
“aws:domainJoin”: {
“properties”: {
“directoryId”: “<ds-id>”,
“directoryName”: “glad.test.com”,
“dnsIpAddresses”: [
“10.1.1.13”,
“10.1.2.12”
]
}
}
}
}
Thats all!