Assigning permissions for Linux service account to add machines to AD

Create service account.

On the domain where the machines will be joined:
Open Active Directory Users and Computers. Enable Advanced Features on the “View” menu.

View the properties of the entire domain.

Select the Security tab, and select Advanced.

  • For this object and all descendant objects: Grant Create/Delete Computer objects
  • For descendant computer objects: Grant Reset password
  • For descendant computer objects: Read/write account restrictions
  • For descendant computer objects: Write all properties, Write all validated writes

Playbook that Converts Local to AD Users

If you like removing local users in favor of the domain users, check out how to do that in shell at my post Convert Local to AD Users.

If you want to do it at scale, you can wrap it with a bit of ansible. Check out the full thing with syntax highlighting on gitlab:

# Only use one thisuser at a time, for the fail/changed logic to work correctly!
# usage: ansible-playbook -l targethost1 /etc/ansible/books/stable/cladu.yml -v -e 'thisuser=joneill'

- name: book that runs cladu
  hosts: all
  become: yes
  become_user: root
  become_method: sudo

  - name: copy in rpm
      src: /etc/ansible/files/bgscripts-core-1.3-9.noarch.rpm
      dest: /tmp/
      mode: 0644

  - shell: rpm -U --nodeps /tmp/bgscripts-core-1.3-9.noarch.rpm
      warn: no
    register: this_rpm
    - 'not ("is already installed" in this_rpm.stdout or "is already installed" in this_rpm.stderr or this_rpm.rc == 0)'
    - 'not ("is already installed" in this_rpm.stdout or "is already installed" in this_rpm.stderr)'

  - shell: /usr/share/bgscripts/work/ -r -g '{{ thisuser }}'
      warn: no
    register: this_shell
    - 'not ("Skipped" in this_shell.stdout or "Failed" in this_shell.stdout)'
    - '"Failed" in this_shell.stdout'

Playbook that resets user password

As an admin, it’s my job to reset user passwords who still use local accounts. I’m working on converting users to domain accounts, but in the mean time, here’s my little book.
Check it out with context highlighting at

# Dependencies: from bgscripts-core, installed on ansible server
- name: book that resets password for thisuser
  hosts: all
  become: yes
  become_user: root
  become_method: sudo

  - name: generate pw hash
    shell: /usr/share/bgscripts/py/ "{{ thispw | default('TEMP_PASSWORD_HERE') }}"
    register: thispw
    delegate_to: localhost
    changed_when: false
    run_once: true

  - user:
      name: "{{ thisuser }}"
      password: "{{ thispw.stdout }}"

  - shell: passwd -e "{{ thisuser }}"
      warn: no

getent passwd -s sss LOCALUSER shows local user


I want to easily and quickly tell if a user is local or domain (don’t care which domain).


  • freeipa-client-4.6.1-3.fc27.x86_64
  • sssd-1.16.0-4.fc27.x86_64

Full story

I am writing a script that will show if a user is local, sssd, can ssh, and is permitted by sssd.

Currently I am doing the check for if the user is from the domain with the getent passwd -s sss $USERNAME command. But I ran into an issue where checking the sssd database returns a local user!

# getent passwd -s sss 'bgstack15-local'

Checking the contents of the database (cache) for sss shows sssd apparently caches all sorts of information about the local user.

# sudo su root -c 'strings /var/lib/sss/db/* | grep bgstack15-local' | sort | uniq
[...output truncated]

I tried clearing the sssd cache overall, and just for the user. Neither made a difference.

# sss_cache -U
# sss_cache -u bgstack15-local

The user does show up as a local user, and I promise it is only a local user!

getent passwd -s files 'bgstack15-local'

The man pages for getent(1) and getpwent(3) don’t help me understand what could be going on. sssd(8) shows me that sssd can cache local users, which actually goes against what I want! The nss section of sssd.conf(5) doesn’t help, but maybe I didn’t take enough time to read it. I’m a little stuck.

My sssd.conf

id_provider = ipa
ipa_server = _srv_,
ipa_domain =
ipa_hostname =
auth_provider = ipa
chpass_provider = ipa
access_provider = ipa
cache_credentials = True
ldap_tls_cacert = /etc/ipa/ca.crt
krb5_store_password_if_offline = True
services = nss, pam, ssh, sudo
domains =
homedir_substring = /home

Last resort

I can try doing my checks against ${USERNAME}@${DOMAIN} when doing the -s sss check, but that means I then have to iterate over all domains in sssd.conf and that would slow the process down.


The option that controls this behavior is buried in sssd.conf(5) on CentOS 7 and Fedora, but not in the online man page.


enable_files_domain = false

Reference 3 shows that sssd makes a “fast cache for local users.”

From man sssd.conf(5) on my Fedora system:

   enable_files_domain (boolean)
       When this option is enabled, SSSD prepends an implicit domain with
       “id_provider=files” before any explicitly configured domains.

       Default: true

Disabling this behavior lets me make a simple check to see if it is a local user or domain user.


  1. ddg: sssd disable caching local users
  4. Fedora 27 sssd.conf(5)

Ansible playbook for configuring access like another user

When you want User B to have the same groups, ssh access, and sssd permissions, use this playbook.

It depends on a few files from the bgscripts package for now. Maybe someday I’ll make some ansible modules for ssh_allowusers: allowed=yes name=”{{ item }}” and sssd_allow_users.

You’ll need these:



I needed to query certain information about a user on a Linux system. Specifically this output:

user: bgstack15
getent: YES
getent_type: sss
can_ssh: YES
can_sss: YES

I wanted to know if a user is defined (getent), and if so, in which database (local or in Active Directory). Also, is the user in the AllowUsers list of the sshd_config, or a member of a group in the AllowGroups list. And then the same question for the sssd config file.

The script

List outbound ssh sessions


sudo netstat -Watp | grep 'ESTABLISHED.*ssh' | awk '{print $5}' | sed 's/:ssh//;' | sort | uniq | while read line; do ps -ef | grep -o "ssh\s.*${line}"; done | sort | uniq | sed -r -e 's/ssh //g;' -e 's/-l (\w*) /\1@/;'


During other work, it came up that I was interested in seeing what outbound ssh sessions I was using. Now I don’t even know why it came up, because I was just writing a shell script to programmatically adjust my xfce settings using its xfconf-query API.

Walking through the command

sudo netstat -Watp | grep 'ESTABLISHED.*ssh' | awk '{print $5}' | sed 's/:ssh//;' | sort | uniq | while read line; do ps -ef | grep -o "ssh\s.*${line}"; done | sort | uniq | sed -r -e 's/ssh //g;' -e 's/-l (\w*) /\1@/;'

This whole statement lists the established ssh connections and then finds the running processes for those and tries to identify the usernames for them.
Step by step:
Everything before the while collects the list of established ssh connections.
sudo netstat -Watp | grep ‘ESTABLISHED.*ssh’ gets the list of ssh connections, and awk | sed | sort | uniq just gets the information we want from each row and removes duplicates.
The while read line; do :; done loop iterates over the list. So for each line in the list, search all running processes for that name on the same line as the expression ‘ssh.’
sort | uniq removes duplicates (apparently qemu+kvm in virt-manager uses a lot of separate ssh processes).
sed -r -e ‘s/ssh //g;’ -e ‘s/-l (\w*) /\1@/;’ trims extra characters and also converts compatible outputs into “username@hostname.”

Improvements to be made

This snippet as is only works if the ssh command issued matches exactly the description of the output of netstat. If dns reverse zones are not configured correctly, so that the netstat shows an IP address but the ssh command was a hostname, this snippet will not find it. I need to improve that, which will probably require a fancier script and not just a oneliner.