AWS - auto-join Windows clients to a managed AD
Notes:
- This post is part of a series: Part 1
- I still consider this post a “snippet”, cause it is just a bit of - hopefully - copy-pastable Terraform code, with a bit of explanation.)
All right. in the previous post we created a VPC including an AD, and configured route53 to forward requests regarding “AD stuff” to it. But, an AD is quite useless without Windows clients. And Windows clients are annoying, cause you have to join them to the AD. Luckily, there’s an automation for that.
AWS introduced the AWS Systems Manager (formerly SSM), which is a small software component pre-installed on most (all?) AWS AMIs and connects to an AWS-internal management instance to perform actions. Like “domain auto-join”.
Approach
There are two ways to do this:
- Configure each host to join the AD, or
- Tell AWS to perform the auto-join action on every host with a certain tag
I’m following option (2) here, using the tag key/value combination { "adjoin": "true" }
. Easy.
WARNING: AWS SSM was key in at least one massive data leak. SO BE VERY CAREFUL in how you use the Systems Manager and if in doubt refer to further documentation. I think this approach is safe, because we do not use any credentials in the following code.
Having said that, let’s go.
The code
resource "aws_ssm_document" "ad_join_domain" {
name = "ad-join-domain"
document_type = "Command"
content = jsonencode(
{
"schemaVersion" = "2.2"
"description" = "aws:domainJoin"
"mainSteps" = [
{
"action" = "aws:domainJoin",
"name" = "domainJoin",
"inputs" = {
"directoryId" = aws_directory_service_directory.ad.id,
"directoryName" = aws_directory_service_directory.ad.name,
"dnsIpAddresses" = aws_directory_service_directory.ad.dns_ip_addresses
}
}
]
}
)
}
resource "aws_ssm_association" "windows_server" {
name = aws_ssm_document.ad_join_domain.name
targets {
key = "tag:adjoin"
values = ["true"]
}
}
resource "aws_iam_role" "ad_autojoin" {
name = "ad-autojoin"
assume_role_policy = jsonencode({
"Version" = "2012-10-17",
"Statement" = [
{
"Effect" = "Allow",
"Principal" = {
"Service" = "ec2.amazonaws.com"
},
"Action" = "sts:AssumeRole"
}
]
})
}
# required it seems
resource "aws_iam_role_policy_attachment" "ssm-instance" {
role = aws_iam_role.ad_autojoin.id
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
# required it seems
resource "aws_iam_role_policy_attachment" "ssm-ad" {
role = aws_iam_role.ad_autojoin.id
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMDirectoryServiceAccess"
}
resource "aws_iam_instance_profile" "ad_autojoin" {
name = "ad-autojoin"
role = aws_iam_role.ad_autojoin.name
}
The Windows instance
Some notes:
- NOTE: I am using a terraform module here, but in my code I am using a modified version of it. So it might be that this code needs some adjustments.
- AWS does not yet provide Windows 10 images as AMIs. So we use Windows Server.
Let’s go.
data "aws_ami" "win2019server" {
owners = ["amazon"]
most_recent = true
filter {
name = "name"
values = ["Windows_Server-2019-German-Full-Base*"]
}
filter {
name = "platform"
values = ["windows"]
}
filter {
name = "architecture"
values = ["x86_64"]
}
}
module "ec2-instance" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "3.2.0"
ami = data.aws_ami.win2019server.id
instance_type = "t3.large"
cpu_credits = "unlimited"
subnet_id = element(module.vpc.private_subnets, 0)
# THIS IS WHAT WE NEED TO AUTO_JOIN!! :))
iam_instance_profile = aws_iam_instance_profile.ad_autojoin.name
tags = merge({ "adjoin" = "true" }, local.tags)
# yes, weird syntax
root_block_device = [{
volume_size = 50
tags = {
Name = "my-root-block"
}
}]
}
References
- Companies’ 5 Million Personal identifiable information records detected on an AWS service due to misconception of users
- What is AWS Systems Manager?
- Seamlessly join a Windows EC2 instance
- Checking SSM Agent status and starting the agent
- Viewing SSM Agent logs
- Join EC2 Instance to AD domain via terraform
- Resource: aws_ssm_association
- Terraform module “ec2-instance”