Skip to content

Sample AWS CloudFormation templates to aide in improving Active Directory workload deployment and management in AWS.

License

Notifications You must be signed in to change notification settings

aws-samples/ssm-automation-custom-ad-domain-join-unjoin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 

Simplifying Active Directory domain join with AWS Systems Manager

Overview

Please refer to blog post for more details: https://aws.amazon.com/blogs/modernizing-with-aws/simplifying-active-directory-domain-join-with-aws-systems-manager/.

Deploy a custom AWS Systems Manager Automation runbook that automatically domain joins or unjoins from an Active Directory (AD) domain. This runbook can be used with on-premises AD, self-managed AD running on Amazon Elastic Compute Cloud (Amazon EC2) Windows instances, or AWS Managed Microsoft AD and can be executed manually or automatically with AWS services such as AWS Systems Manager, Amazon EventBridge, or AWS Lambda. The runbook create an AWS Secrets Manager secret with the AD credentials, AD domain name, and a specific Organizational Unit (OU) in AD.

This guide assumes DNS has been configured already for your Active Directory environment running in AWS. There are various methods to configure such an environment, either with Amazon Route 53 Resolver endpoints or DHCP option sets in Amazon VPC.

The Automation runbook workflow

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

  1. assertInstanceIsWindows - The runbook first checks if the EC2 instance is running Windows and will only continue if the platform is Windows.
  2. chooseDomainJoinActivity - This is the crucial activity, where a user selects which activity they want to execute automatically: join an AD domain or unjoin from an AD domain.
    • Determined from the DomainJoinActivity parameter.
  3. joinDomain & unjoinDomain - PowerShell to domain join or unjoin are on the EC2 instances locally, respectively. A successful domain join will restart the instance, where a successful unjoin will stop the instance.
    • The tagging steps are joinADEC2Tag and unjoinADEC2Tag.
    • The PowerShell is wrapped in try and catch blocks. To learn more, visit about_Try_Catch_Finally.
    • If either a domain join/unjoin fails, a Failed status is returned, the EC2 instance is tagged to reflect the failure (failADEC2Tag), and the EC2 instance is stopped.

    The failures from the PowerShell are outputted and displayed in the Systems Manager Automation executions console. Users can troubleshoot the domain join/unjoin failures based on these common errors. Further enhancements can be made by additionally outputting the failures to Amazon CloudWatch Logs in custom log groups created by Run Command. To learn more about log groups, visit the AWS documentation.

Deploy the Automation runbook

To deploy the runbook and parameters automatically, download and save the AWS CloudFormation template from Github, cfn-create-ssm-automation-parameters-adjoin.yml, and save it 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.

To create the Automation runbook manually, you can download the template separately from here and create a custom Automation runbook manually. Visit the AWS documentation to learn how to create runbooks with the Document Builder or how to create runbooks with the Editor.

Secret store AD domain configuration

The runbook uses a secret to store the AD credentials in Secrets Manager. Secrets Manager helps you manage and retrieve the AD credentials needed to perform AD join and unjoin activities. The secret is encrypted and decrypted with an AWS KMS customer managed key. The customer managed key is an AWS KMS key that is created, owned and managed by you and can be shared to other AWS accounts and AWS Regions if needed. The secret and customer managed key are referenced in the runbook, specifically in the PowerShell scripts, by using the secret’s value and customer managed key ID specified when the secret is created. These values are not hard coded in the PowerShell code, allowing AD admins to rotate the password and securely update the secret without modifying the code. In particular, this solution uses 4 values:

  • Fully qualified domain name (FQDN) of the AD domain.
  • AD username.
  • AD password.
  • Specific organizational unit (OU) in AD where the computer account for the domain-joined instance will be created.

Below is an example of the secret stored in JSON within Secrets Manager with the AD credentials, domain name, and OU, all of which are required to complete the domain join and unjoin activities. The CloudFormation template will create the secret automatically after filling in the input parameters.

The keys and values are case sensitive. The plaintext value of the domainJoinUserName key requires two backslashes to parse the down-level logon name format.

{
    "domainName": " corp.example.com",
    "domainJoinUserName": "CORP\\Admin",
    "domainJoinPassword": " YOURPASSWORD",
    "defaultTargetOU": " OU=Computers,OU=CORP,dc=corp,dc=example,dc=com"
} 

PowerShell

Within the Systems Manager Automation runbook there are two steps where either domain join or domain unjoin activities are executed. These steps call a Systems Manage Command document (SSM Document) to execute this code. Specifically, the SSM Command document that is executed is AWS-RunPowerShellScript, which simply executes any code that is passed as an input parameter. Below are the PowerShell code blocks used to perform domain join and domain unjoin activities, respectively.

Domain join

If ((Get-CimInstance -ClassName 'Win32_ComputerSystem' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'PartOfDomain') -eq $false) {
    Try {
        $jsonSecretValue = (Get-SECSecretValue -SecretId ${SecretKeyADPasswordResource}).SecretString | ConvertFrom-Json
        $targetOU = $jsonSecretValue.defaultTargetOU
        $domainName = $jsonSecretValue.domainName
        $domainJoinUserName = $jsonSecretValue.domainJoinUserName
        $domainJoinPassword = $jsonSecretValue.domainJoinPassword | ConvertTo-SecureString -AsPlainText -Force
    } Catch [System.Exception] {
        Write-Output "Failed to get secret $_"
    }
    $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
}

Domain unjoin

If ((Get-CimInstance -ClassName 'Win32_ComputerSystem' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'PartOfDomain') -eq $true) {
    Try {
        $jsonSecretValue = (Get-SECSecretValue -SecretId ${SecretKeyADPasswordResource}).SecretString | ConvertFrom-Json 
        $domainName = $jsonSecretValue.domainName
        $domainJoinUserName = $jsonSecretValue.domainJoinUserName
        $domainJoinPassword = $jsonSecretValue.domainJoinPassword | ConvertTo-SecureString -AsPlainText -Force
    } Catch [System.Exception] {
        Write-Output "Failed to get secret $_"
    }

    $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
}

With the exception of the secret, the PowerShell script should be familiar to any admin who leverages PowerShell AD cmdlets to execute domain join activities. There are exit codes specific to Systems Manager that allow the Automation runbook to identify failures during the domain join or unjoin process. Without the exit codes, a failed domain join, for example, may still be marked as Success despite not having been added to an AD domain. Learn more about exit codes by visiting AWS documentation.

The PowerShell can be customized as needed and provided as-is. Testing is required to confirm proper functionality within your AD environment!

Security

See CONTRIBUTING for more information.

License

This library is licensed under the MIT-0 License. See the LICENSE file.

About

Sample AWS CloudFormation templates to aide in improving Active Directory workload deployment and management in AWS.

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published