Skip to content

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

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 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 leverages 4 parameters stored in Systems Manager Parameter Store, to store the AD domain name, AD domain username, AD domain user's password, and a specific Organizational Unit (OU) in AD. The AD domain user's password is encrypted and decrypted with AWS Key Management Service (AWS KMS) as a secure string.

NOTE: 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 and parameters

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.

Parameter Store

The Automation runbook requires parameters stored in Systems Manager Parameter Store to complete the domain join and unjoining activities. If you chose to deploy the environment using the CloudFormation stack, these parameters are created automatically. Otherwise, the parameters must be created manually. This includes the AD domain name (FQDN), AD username, AD password, and a targetOU. To learn more about Parameter Store, visit the AWS documentation.

Below are details of the parameters that are created by the CloudFormation stack or manually if you choose not to use CloudFormation (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.example.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

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

Domain unjoin

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
}

With the exception of the parameters from the Systems Manager Parameter Store, 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.

NOTE: the PowerShell can be customized as needed. Also, AD credentials are currently stored as parameters in Systems Manager Parameter Store. However, customers can choose to store these credentials as secrets in AWS Secrets Manager. To learn more about Secrest Manager, visit the AWS documentation

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