A DNS audit can select the right hosts and still produce a misleading report.

The failure mode is usually a pipeline like this:

ansible "$GROUP" ... \
  | sed -n 's/.*CSV|//p' \
  | awk -F'|' '{print $1 "," $2 "," $3 "," $4}' \
  >> "$OUT"

That captures successful CSV|... markers, but it hides everything else. If half the hosts fail SSH or sudo, the CSV simply has fewer rows. The audit looks clean only because failed hosts disappeared.

Symptom

The host list is correct:

hosts (13):
  cluster-a-cp-1
  cluster-a-cp-2
  cluster-a-cp-3
  cluster-a-worker-1
  ...
  cluster-a-worker-10

But the audit CSV has fewer data rows:

targeted hosts: 13
csv rows:       6

That is not partial success. That is an incomplete audit.

Fix The Pipeline

Capture raw Ansible output first:

AUDIT_RAW="$(mktemp)"

set +e
ANSIBLE_NOCOLOR=1 \
ANSIBLE_STDOUT_CALLBACK=default \
ANSIBLE_HOST_KEY_CHECKING=False \
ansible "$GROUP" \
  -i "$INVENTORY" \
  -u "$ANSIBLE_USER" \
  -b -kK \
  -m shell -a '
ACTUAL_HOSTNAME=$(hostname -s)
RESOLV_SEARCH=$(awk "/^search/{\$1=\"\"; sub(/^ /,\"\"); print; exit} /^domain/{print \$2; exit}" /etc/resolv.conf)
[ -z "$RESOLV_SEARCH" ] && RESOLV_SEARCH="NONE"
NETPLAN_SEARCH=$(grep -R "search:" /etc/netplan/*.yaml /etc/netplan/*.yml 2>/dev/null | sed "s/.*search:[[:space:]]*//" | paste -sd ";" -)
[ -z "$NETPLAN_SEARCH" ] && NETPLAN_SEARCH="NONE"
if [ -L /etc/resolv.conf ]; then
  RESOLV_TYPE="symlink:$(readlink -f /etc/resolv.conf)"
else
  RESOLV_TYPE="static_file"
fi
printf "CSV|{{ inventory_hostname }}|%s|%s|%s|%s\n" "$ACTUAL_HOSTNAME" "$RESOLV_SEARCH" "$NETPLAN_SEARCH" "$RESOLV_TYPE"
' > "$AUDIT_RAW" 2>&1
AUDIT_RC=$?
set -e

Then parse CSV rows from the raw file:

sed -n 's/.*CSV|//p' "$AUDIT_RAW" \
  | awk -F'|' '{print $1 "," $2 "," $3 "," $4 "," $5}' \
  | sort -t, -k1,1 \
  >> "$OUT"

Do not discard the non-CSV output. Show it as audit evidence:

if grep -v 'CSV|' "$AUDIT_RAW" | grep -q '[^[:space:]]'; then
  echo
  echo "== Ansible warnings/failures =="
  grep -v 'CSV|' "$AUDIT_RAW"
fi

This exposes errors such as:

UNREACHABLE! => Permission denied (publickey,password,keyboard-interactive)
Missing sudo password
Failed to connect to the host via ssh

Compare Targeted Hosts To CSV Rows

Count targeted hosts before the audit:

TARGETED_HOSTS=$(ansible "$GROUP" -i "$INVENTORY" --list-hosts \
  | awk '/hosts \([0-9]+\):/ {gsub(/[():]/, "", $2); print $2}')

Count CSV data rows after the audit:

CSV_ROWS=$(awk 'NR > 1 {count++} END {print count + 0}' "$OUT")

Warn on mismatch:

if [ "$CSV_ROWS" -lt "$TARGETED_HOSTS" ]; then
  echo
  echo "WARNING: audit produced $CSV_ROWS CSV rows for $TARGETED_HOSTS targeted hosts"
  echo "Some hosts failed before returning DNS state. Treat this audit as incomplete."
fi

Interpret SSH And Become Flags

For Ansible audit scripts, auth flags matter:

-k  ask for SSH password
-K  ask for sudo/become password
-b  enable become/sudo

Useful combinations:

password SSH + password sudo:       -b -kK
SSH key + password sudo:            -b -K
SSH key + passwordless sudo:        -b

If -u ubuntu appears ignored, check inventory or group variables for ansible_user. A group var can force the remote user unless explicitly overridden with an extra var:

ansible "$GROUP" -i "$INVENTORY" -e ansible_user=ubuntu -m ping

Audit Finish Criteria

A DNS search-domain audit is complete only when:

selected host count matches expected inventory
CSV data row count equals selected host count
raw Ansible output has no unreachable or failed hosts
drift output is reviewed after row-count validation

If the row count is short, fix SSH/bootstrap/sudo access first. Do not treat the DNS state as clean just because missing hosts did not write CSV rows.

Related: DNS Search Remediation With Per-Cluster Ansible Inventory covers per-cluster inventory arguments, local credential overrides for Vault-backed Ansible vars, and final-audit handling after remediation.