AWS - auto-join Windows clients to a managed AD
Posted on October 18, 2021 (Last modified on October 23, 2024) • 3 min read • 570 wordsNotes:
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”.
There are two ways to do this:
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.
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
}
Some notes:
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"
}
}]
}