11

I'm currently writting an Ansible script which should update openssl on every host running Debian or CentOS. On the hosts our SSH-Keys are deposited for my own user or root. I want to check if my user is existing on the host, if not I want to authenticate with the root user. Is there a possibility to do this? I tried it with a bash command but I want to check if my user exists before I'm running the tasks. Maybe there are other solutions to my problem but I don't know them. Running this playbook throws a syntax error. My Script looks like this right now:

---
- hosts: "{{ host_group }}" 
  remote_user: "{{ username }}"
  tasks:

# Check whether there's a existinig user or whether you have to use root
    - name: Check whether there's your user on the machine
      action: shell /usr/bin/getent passwd $username | /usr/bin/wc -l | tr -d ''
      register: user_exist
      remote_user: root
      when: user_exist.stdout == 0
      tags:
         - users

# Install openssl on Ubuntu or Debian
    - name: Install openssl on Ubuntu or Debian
      become: True
      become_user: root
      apt: name=openssl state=latest
      when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'

# Install openssl on CentOS or RHEL
    - name: Install openssl on CentOS or RHEL
      become: True
      become_user: root
      yum: name=openssl state=latest
      when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'
princexzijvd
  • 113
  • 1
  • 1
  • 4

2 Answers2

10

You can test the connection with local_action first.
Ansible need to know how to connect to the host for sure, otherwise it will trigger host unreachable error and skip remaining tasks for that host.
Something like this:

- hosts: myservers
  gather_facts: no  # or it will fail on the setup step
  tasks:
    - name: Test user
      local_action: "command ssh -q -o BatchMode=yes -o ConnectTimeout=3 {{ inventory_hostname }} 'echo ok'"
      register: test_user
      ignore_errors: true
      changed_when: false

    - name: Do useful stuff
      remote_user: "{{ test_user | success | ternary(omit, 'root') }}"
      command: echo ok
Konstantin Suvorov
  • 65,183
  • 9
  • 162
  • 193
  • 4
    Can you explain what this code does? I want to understand the sense of it and don't want to copy and paste this without knowledge – princexzijvd Sep 26 '16 at 11:03
  • 2
    Hmm... What part? At first, we execute `ssh` command on localhost to test if it is possible to connect to host in question with current user. Register results to `test_user` variable. Ignore errors if any (because command will fail if we can't connect with current user). Then we set `remote_user` for subsequent task to `root` based on the success/failure of our test: if `success` (current user exists), don't set `remote_user` (use `omit` keyword); if `failure` (current user doesn't exists), set `remote_user` to `root`. – Konstantin Suvorov Sep 26 '16 at 11:13
  • Cool, this works! The gather_facts: no above disables the gathering of facts about the OS on the remote hosts, but I need to know which os is running on the remote host for the package managers. How can I gather the remote OS without gathering every fact? EDIT: Just for me to know, can you explain the 'ternary' thing that you wrote? – princexzijvd Sep 26 '16 at 12:42
  • Run `setup` module as the first task after user check – this will force fact gathering. – Konstantin Suvorov Sep 26 '16 at 12:45
8

A more native and elegant way to test your SSH connection is with the Ansible ping module (which verifies the end-to-end SSH connection, not ICMP), and to use the playbook keyword ignore_unreachable, which was added in Ansible 2.7.

The technique below puts SSH testing into its own play where facts are not gathered; subsequent plays will gather facts as normal:

---
###
## First play: Dynamically configure SSH user
##
- hosts: "{{ host_group }}"
  gather_facts: false  # don't try to ssh yet!!
  vars:
    ansible_ssh_user: "{{ username }}"

  tasks:
    - name: "Test SSH connection"
      ping:  # <-- no args needed
      ignore_unreachable: true
      ignore_errors: true
      changed_when: false
      register: ssh_test

    - name: "Fall back to root user?"
      when: ssh_test.unreachable is defined
      connection: local
      set_fact:
        ansible_ssh_user: root


###
## Next play: Install Stuff
###
- hosts: "{{ host_group }}"
  tasks:
    - name: Install openssl on Ubuntu or Debian
      # ...
invsblduck
  • 81
  • 1
  • 3
  • Is there a way to get this to work without having the unreachable=1 in the play recap the first time running the playbook? – Tanuki May 07 '22 at 20:33