Microsoft Workloads on AWS

Simplifying Active Directory domain join with AWS Systems Manager

In this blog post, I will present a solution for managing the Active Directory domain membership for a dynamic fleet of Amazon Elastic Compute Cloud (Amazon EC2) Windows instances using Automation, a capability of AWS Systems Manager.

Using Systems Manager Automation, you can dynamically automate domain join and unjoin activities with Microsoft Active Directory Domain Services (AD DS) for your Windows instances on Amazon EC2. With Automation, you can use runbooks to perform domain join and unjoin activities manually, automatically, or as event-driven. Thus, you no longer have to rely on legacy login scripts or third-party software to perform such operations, which cannot be easily scaled in the cloud.

With built-in multi-account and multi-Region support, Systems Manager Automation can ease the domain join and unjoin tasks for your Windows workloads at any size. Automation is flexible as it supports hybrid AD DS environments, self-managed AD DS running on Windows EC2 instances, and AWS Directory Service for Microsoft Active Directory (AWS Managed Microsoft AD). Finally, Systems Manager Automation runbooks can be the glue to the other event-driven AWS services. For example, Automation can be used together with Amazon EC2 Auto Scaling to support domain join or unjoin activities for your dynamic fleet of Windows EC2 instances.

Solution Overview

This blog post will demonstrate how to manually run Automation against a Windows EC2 instance to join an AWS Managed Microsoft AD domain. The steps outlined in this blog post are also applicable for AD environments in a hybrid cloud or a self-managed AD running on EC2 instances. To learn more about deploying and hosting AD on AWS, please visit our QuickStart guide or visit our documentation to learn more about AWS Managed Microsoft AD.

There are a couple of components to this solution:

To deploy the runbook and parameters automatically, download and save the AWS CloudFormation template from Github locally to your computer to create a new CloudFormation stack. Creating a new stack will simplify the deployment of the Automation runbook and create the appropriate parameters to perform the AD join/unjoin activities automatically. To learn more about CloudFormation stack creation, visit the AWS documentation.

A CloudFormation stack, presented In Figure 1, is created with the name SSM-Automation-Demo. The first input parameter in the stack requires you to enter a name for the Automation runbook. For this demo, I will use SSM-Automation-AD-Join, though you can enter any name you like as long as it meets the requirements referenced on the stack creation screen. The remaining four input parameters in the stack create Systems Manager parameters that are relevant to the AD domain join and unjoin activities. I will look at the Parameter Store in more detail later in the post.

Figure 1: Example screenshot of AWS CloudFormation stack creation.

Figure 1: Example screenshot of AWS CloudFormation stack creation.

Now, let’s take a look at the runbook.

The Automation runbook workflow

This custom runbook is the central piece of the AD domain join and unjoin workflow. Using this runbook, administrators can manually or automatically trigger domain join or unjoin for their Windows EC2 instance to and from an AD domain. This blog post will demonstrate the manual execution of the Automation.

First, let’s review the parameters that need to be specified.

Parameters

  • AutomationAssumeRole – The service role gives the automation permission to perform actions on your behalf, for example, when configuring automatic domain join/unjoin activities with AWS Systems Manager State Manager.
  • InstanceId – The Windows EC2 instance Id where your runbook will run the commands. The instance Id is persistent throughout the runbook.
  • DomainJoinActivity – A dropdown option allowing you to select to either Join or Unjoin from an AD domain. Depending on which option you choose, the runbook will perform the appropriate follow-up steps.

Runbook steps

There are 9 steps in the Automation workflow. Below are descriptions of the key steps and how they factor into the AD domain join/unjoin activities.

  • assertInstanceIsWindows– The first step checks if the EC2 instance is running Windows and will only continue if the platform is Windows. The aws:assertAwsResourceProperty action allows you to assert a specific resource state or event state for a specific Automation step. To learn more about aws:assertAwsResourceProperty, follow the official documentation.
  • chooseDomainJoinActivity– This step is associated with the aws:branch action, which allows Automation to conditionally follow the steps relevant to join an AD domain or unjoin from an AD domain. It is similar in concept to a basic if-else statement in PowerShell, for example. To learn more about aws:branch, follow the official documentation.

Below are the decision trees that explain the steps in sequence for both successful and failed domain joins (Figure 2) and unjoins (Figure 3).

Figure 2: Decision tree when users select Join in chooseDomainJoinActivity.

Figure 2: Decision tree when users select Join in chooseDomainJoinActivity.

Figure 3: Decision tree when users select Unjoin in chooseDomainJoinActivity.

Figure 3: Decision tree when users select Unjoin in chooseDomainJoinActivity.

Join and unjoin domain

There are two steps, joinDomain and unjoinDomain, that are associated with the aws:runCommand action. Either of these steps can be run directly on respective EC2 instances. Since I am working on Windows Server explicitly, I want to run domain join and unjoin related commands using PowerShell with the aid of the AWS-RunPowerShellScript command document. To learn more about aws:runCommand, follow the official documentation.

The PowerShell statements in both the joinDomain (Figure 4) and unjoinDomain (Figure 5) steps are wrapped in the try and catch blocks. To learn more, visit about_Try_Catch_Finally. If either step succeeds, the EC2 instance will be tagged to indicate the instance has been joined or unjoined to or from the AD domain, respectively. Reboots are only applied upon a successful domain join executing the rebootServer step upon the join activity. Otherwise, the runbook will stop the EC2 instance by running the stopServer step upon the unjoin activity. If either step fails, a Failed status is returned and outputted in the Systems Manager Automation console; the Automation also runs the failADEC2Tag step, which tags the EC2 instance to reflect the failure, and then runs the stopServer step to stop the EC2 instance. This allows you to troubleshoot the failure and try again. (For example, the domain controllers may not be responding or there is a typo with one of the parameters, etc.)

If ((Get-CimInstance -ClassName 'Win32_ComputerSystem' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'PartOfDomain') -eq $false) {
    Try {
        $targetOU = (Get-SSMParameterValue -Name 'defaultTargetOU' -ErrorAction Stop).Parameters[0].Value
        $domainName = (Get-SSMParameterValue -Name 'domainName' -ErrorAction Stop).Parameters[0].Value
        $domainJoinUserName = (Get-SSMParameterValue -Name 'domainJoinUserName' -ErrorAction Stop).Parameters[0].Value
        $domainJoinPassword = (Get-SSMParameterValue -Name 'domainJoinPassword' -WithDecryption:$true -ErrorAction Stop).Parameters[0].Value | ConvertTo-SecureString -AsPlainText -Force
    } Catch [System.Exception] {
        Write-Output " Failed to get SSM Parameter(s) $_"
    }
    $domainCredential = New-Object System.Management.Automation.PSCredential($domainJoinUserName, $domainJoinPassword)

    Try {
        Write-Output "Attempting to join $env:COMPUTERNAME to Active Directory domain: $domainName and moving $env:COMPUTERNAME to the following OU: $targetOU."
        Add-Computer -ComputerName $env:COMPUTERNAME -DomainName $domainName -Credential $domainCredential -OUPath $targetOU -Restart:$false -ErrorAction Stop 
    } Catch [System.Exception] {
        Write-Output "Failed to add computer to the domain $_"
        Exit 1
    }
} Else {
    Write-Output "$env:COMPUTERNAME is already part of the Active Directory domain $domainName."
    Exit 0
}

Figure 4: The PowerShell statement that implements the joinDomain step.

If ((Get-CimInstance -ClassName 'Win32_ComputerSystem' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'PartOfDomain') -eq $true) {
    Try {
        $domainName = (Get-SSMParameterValue -Name 'domainName' -ErrorAction Stop).Parameters[0].Value
        $domainJoinUserName = (Get-SSMParameterValue -Name 'domainJoinUserName' -ErrorAction Stop).Parameters[0].Value
        $domainJoinPassword = (Get-SSMParameterValue -Name 'domainJoinPassword' -WithDecryption:$true -ErrorAction Stop).Parameters[0].Value | ConvertTo-SecureString -AsPlainText -Force
    } Catch [System.Exception] {
        Write-Output "Failed to get SSM Parameter(s) $_"
    }

    $domainCredential = New-Object System.Management.Automation.PSCredential($domainJoinUserName, $domainJoinPassword)

    If (-not (Get-WindowsFeature -Name 'RSAT-AD-Tools' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'Installed')) {
        Write-Output 'Installing RSAT AD Tools to allow domain joining'
        Try {
            $Null = Add-WindowsFeature -Name 'RSAT-AD-Tools' -ErrorAction Stop
        } Catch [System.Exception] {
            Write-Output "Failed to install RSAT AD Tools $_"
            Exit 1
        }    
    }
    
    $getADComputer = (Get-ADComputer -Identity $env:COMPUTERNAME -Credential $domainCredential)
    $distinguishedName = $getADComputer.DistinguishedName

    Try {
        Remove-Computer -ComputerName $env:COMPUTERNAME -UnjoinDomainCredential $domainCredential -Verbose -Force -Restart:$false -ErrorAction Stop
        Remove-ADComputer -Credential $domainCredential -Identity $distinguishedName -Server $domainName -Confirm:$False -Verbose -ErrorAction Stop
    } Catch [System.Exception] {
        Write-Output "Failed to remove $env:COMPUTERNAME from the $domainName domain and in a Windows Workgroup. $_"
        Exit 1
    }  
} Else {
    Write-Output "$env:COMPUTERNAME is not part of the Active Directory domain $domainName and already part of a Windows Workgroup."
    Exit 0
}

Figure 5: The PowerShell statement that implements the unjoinDomain step.

Parameters to store AD domain configuration

The runbook uses parameters stored in Parameter Store. Parameter Store provides secure, hierarchical storage for managing configuration data. You can store configuration values as either plain text or as encrypted ciphertext. These values will be referenced as Systems Manager parameters in the runbook, specifically in the PowerShell script, by using the unique name that was specified when the parameters were created.

Pay close attention to the Get-SSMParameterValue cmdlet. This PowerShell cmdlet reads the parameter values and stores it in a variable. This is particularly useful for the AD credentials, specifically the AD domain user’s password, which is encrypted and decrypted with an AWS Key Management Service (AWS KMS) ID. Credentials are stored securely. They are not hard coded in the PowerShell code, and AD admins can rotate the password and update the parameters without modifying the code.

In particular, this solution uses 4 parameters:

  • Fully qualified domain name (FQDN) of the AD domain name
  • AD domain user’s username
  • AD domain user’s password
  • Specific Organizational Unit (OU) in AD where the computer account for the domain-joined instance will be created

Below are examples of the parameters and values required to complete the domain join and unjoin activities. The CloudFormation template will create the parameters automatically after filling in the input parameters as seen in Figure 1. To manually create these parameters, review our documentation to learn more.

(NOTE: the parameter names and values are cAsE-SeNsItIvE).

Name (do not change these values) Type Data type Value (example values) AWS KMS Key ID Required
domainName String text corp.exae.com No
domainJoinUserName String text CORP\Admin No
domainJoinPassword SecureString text YOURPASSWORD Yes
defaultTargetOU String text OU=Computers,OU=CORP,dc=corp,dc=example,dc=com No

Figure 6: Table reference of the parameter names and sample values.

Figure 7: The custom parameters stored in Parameter Store.

Figure 7: The custom parameters stored in Parameter Store.

Putting it all together

Now that the key steps in the runbook and the parameters needed are recognized, I will walk through an example of how to manually domain join an EC2 instance.

  1. Open the AWS Systems Manager console at https://console.aws.amazon.com/systems-manager/.
  2. In the navigation pane, choose Automation, and then choose Execute automation (Figure 8).
Figure 8: Execution automation in AWS Systems Manager.

Figure 8: Execution automation in AWS Systems Manager.

  1. In the Automation document list, choose the SSM-Automation-AD-Join Choose the Owned by me tab to view the custom runbook.
  2. In the Document details section, verify that Document version is set to Default version at runtime. While I will choose the default version in this example, the system includes the following version options:
    1. Default version at runtime: Choose this option if the Automation runbook is updated periodically and a new default version is assigned.
    2. Latest version at runtime: Choose this option if the Automation runbook is updated periodically, and you want to run the version that was most recently updated.
    3. 1 (Default): Choose this option to run the first version of the document, which is the default (Figure 9).
Figure 9: Choose automation runbook.

Figure 9: Choose automation runbook.

  1. Choose Next.
  2. In the Execution Mode section, choose Simple execution.
  3. In the Input parameters section, specify the required inputs.
    1. Select an Instance ID from the interactive instance picker.
    2. In the DomainJoinActivity dropdown, select Join.
  4. Choose Execute (Figure 10).
Figure 10: Select your instance and DomainJoinActivity. This example demonstrates the Join activity.

Figure 10: Select your instance and DomainJoinActivity. This example demonstrates the Join activity.

NOTE: Simple execution targets a single EC2 instance. To deploy this runbook against multiple EC2 instances, you can choose Rate control in the Execution Mode section. In this mode, you can define your targets based on different parameters. For example, you can select InstanceId as the Parameter and Parameter Values as the Targets. From there, you can multi-select all of the EC2 instances you want to join to an AD domain. Refer to Figure 11 to see what this looks like and our documentation for more details on how to deploy runbooks for multiple targets.

Figure 11: Selecting multiple instances and to configure DomainJoinActivity to perform the Join activity.

Figure 11: Selecting multiple instances and to configure DomainJoinActivity to perform the Join activity.

  1. The Execution detail screen will show a green banner stating Execution has been initiated. From here, each individual step is run in the Executed steps.
    1. The overall status is available in the Executions status.
    2. Each step can be viewed by clicking on the Step ID link along with start and end time stamps
    3. The Step ID is unique to every execution.
  2. Each step indicates individual Success or Failed in the Status column (Figure 12).
Figure 12: Execution detail screen showing Success and the executed steps.

Figure 12: Execution detail screen showing Success and the executed steps.

  1. For example, to see the details of Step #3, I can click on the 469c0afd-6ab4-4b51-9e4d-ac5a336e92bf Step ID
  2. Since this step runs PowerShell locally on the Windows EC2 instance, the Outputs displays PowerShell output relevant to the Add-Computer cmdlet I would expect.
  3. To go back to the Execution detail screen, click on Back to execution detail (Figure 13).
Figure 13: An example of a successful step details and outputs.

Figure 13: An example of a successful step details and outputs.

  1. After all the execution steps have successfully completed, the Windows EC2 instance has been joined to the AD domain.

The process to join a Windows EC2 instance to an AD domain is easy using Automation. With Automation, you can review the steps as they are processed or follow-up from within the Systems Manager console at any time. If a failure occurs on any step, the entire process will stop, and the failed process can be reviewed by clicking on the Step ID.

Below are example screenshots of a failed domain join attempt due to an improper parameter configuration. In Figure 14, I can see that Step #3 failed and is clearly indicated with a status of Failed. Figure 15 shows the specific output of the failure; in this case, it is a specific PowerShell error related to the Add-Computer cmdlet.

Figure 14: Execution detail screen with a failure in Step #3.

Figure 14: Execution detail screen with a failure in Step #3.

Figure 15: An example of a failed step details and outputs.

Figure 15: An example of a failed step details and outputs.

Finally, to remove the EC2 instance from an AD domain, simply rerun the Automation runbook and select Unjoin in the DomainJoinActivity dropdown as seen in Figure 16. Similar to the domain join process, the unjoin process will run the Remove-Computer and Remove-ADComputer cmdlets to remove the instance from AD and clean up AD immediately. (Please reference Figure 5 for more details for the code.)

Figure 16: An example runbook execution where the DomainJoinActivity is configured for the Unjoin activity.

Figure 16: An example runbook execution where the DomainJoinActivity is configured for the Unjoin activity.

Cleanup

To delete the resources created by the CloudFormation template, go to the AWS CloudFormation console in the management account. Choose the stack you created, and then choose Delete.

Summary

There you have it! Using Systems Manager Automation, you can simplify the domain join and unjoin processes for your Amazon EC2 instances running Windows Server.


AWS can help you assess how your company can get the most out of cloud. Join the millions of AWS customers that trust us to migrate and modernize their most important applications in the cloud. To learn more on modernizing Windows Server or SQL Server, visit Windows on AWSContact us to start your modernization journey today.

Syed Ahmad

Syed Ahmad

Syed Ahmad is a Principal Specialist Solutions Architect for Microsoft workloads in AWS. He specializes in Active Directory, AWS Identity services, and AWS Management & Governance. Outside of work, Syed likes to spend time with his family, enjoys playing on his Xbox, mimics great chef recipes at an amateur level, and waiting for another 49ers Super Bowl victory.