How to Check if a Linux Machine is Successfully Domain-Joined to Active Directory Using adcli


2 views

When automating domain joins with SaltStack, we frequently encounter situations where we need to verify whether a Linux machine (CentOS 6.6/7 in our case) has already been successfully joined to our Active Directory (2008R2 functional level) using adcli join. While repeated joins don't break functionality, they introduce unnecessary overhead during provisioning.

Many administrators suggest checking for /etc/krb5.keytab existence, but this approach has limitations:

# Not reliable as a standalone check
if [ -f "/etc/krb5.keytab" ]; then
    echo "Keytab exists, but doesn't guarantee active domain join"
fi

The keytab file persists even after machine account removal from AD, making it an unreliable indicator of current domain membership.

Here's a robust method that combines several checks:

#!/bin/bash

# 1. Check keytab existence (basic first check)
if [ ! -f "/etc/krb5.keytab" ]; then
    echo "Machine not domain-joined (missing keytab)"
    exit 1
fi

# 2. Verify we can get a TGT
if ! klist -k /etc/krb5.keytab | grep -q "HOST/$(hostname -s)"; then
    echo "Keytab doesn't contain host principal"
    exit 1
fi

# 3. Test actual AD communication
if ! adcli testjoin; then
    echo "adcli reports machine is not properly joined"
    exit 1
fi

echo "Machine is successfully domain-joined"
exit 0

For SaltStack deployments, we can create a custom state module:

# File: _states/adcheck.py

import os

def is_domain_joined():
    """
    Verify domain join status
    """
    if not os.path.exists('/etc/krb5.keytab'):
        return False
    
    # Additional verification steps
    if os.system('adcli testjoin >/dev/null 2>&1') != 0:
        return False
        
    return True

For systems using realmd, we can leverage:

realm list --name-only

This provides clear output about the current domain membership status.

Special considerations when:

  • DNS isn't properly configured
  • Time synchronization is off
  • AD connectivity is intermittent

We can enhance our verification script to handle these scenarios:

# Check time synchronization
if ! ntpdate -q $(grep ^server /etc/ntp.conf | head -1 | awk '{print $2}') >/dev/null; then
    echo "Time synchronization issue detected"
fi

For large-scale deployments, cache the verification results:

# Cache TTL of 1 hour
CACHE_FILE="/var/cache/ad_status"
CACHE_TTL=3600

if [ -f "$CACHE_FILE" ] && \
   [ $(($(date +%s) - $(stat -c %Y "$CACHE_FILE"))) -lt $CACHE_TTL ]; then
    cat "$CACHE_FILE"
    exit
fi

# Run actual checks and cache results
adcli testjoin > "$CACHE_FILE"

When automating Linux machine enrollment in Active Directory through SaltStack, a common pain point emerges: reliably detecting whether a machine is already domain-joined. The adcli join command happily reprocesses existing joins, wasting time and creating unnecessary AD operations.

Many administrators first check for /etc/krb5.keytab existence, but this only indicates historical joins. More robust verification requires checking both local configuration and AD connectivity:


# Basic but insufficient check
if [ -f "/etc/krb5.keytab" ]; then
    echo "Keytab exists - possible domain join"
else
    echo "Not domain joined"
fi

For CentOS 6.6/7 systems integrated with 2008R2 domains, implement these verification steps:

Method 1: Test Kerberos Authentication


# Verify we can get a Kerberos ticket
if kinit -k -t /etc/krb5.keytab $(hostname -s)${}; then
    echo "Successfully authenticated - domain joined"
    kdestroy
else
    echo "Failed to authenticate - not properly joined"
fi

Method 2: Query AD Directly


# Check computer object exists in AD
COMPUTER_NAME=$(hostname -s)
DOMAIN=$(hostname -d | tr '[a-z]' '[A-Z]')

if net ads search "(&(objectClass=computer)(name=$COMPUTER_NAME))" \
   -P | grep -q "dn: CN=$COMPUTER_NAME"; then
    echo "Computer object exists in AD"
else
    echo "No AD computer object found"
fi

For automated deployments, create a custom execution module:


# File: _modules/ad_utils.py

import subprocess

def is_domain_joined():
    """
    Returns True if machine is properly domain joined
    """
    try:
        # Test Kerberos auth
        hostname = subprocess.check_output(['hostname', '-s']).decode().strip()
        domain = subprocess.check_output(['hostname', '-d']).decode().strip()
        principal = f"{hostname}${domain.upper()}"
        
        subprocess.run(
            ['kinit', '-k', '-t', '/etc/krb5.keytab', principal],
            check=True,
            stderr=subprocess.PIPE,
            stdout=subprocess.PIPE
        )
        
        # Cleanup ticket
        subprocess.run(['kdestroy'], check=False)
        return True
        
    except subprocess.CalledProcessError:
        return False

Consider these scenarios in your verification logic:

  • Stale keytabs from previous joins
  • DNS resolution failures
  • Expired machine account passwords
  • Network connectivity issues

For production use, combine multiple verification methods with appropriate error handling:


def robust_domain_check():
    checks = [
        os.path.exists("/etc/krb5.keytab"),
        _test_kerberos_auth(),
        _check_ad_object_exists()
    ]
    return all(checks)