Wie erhalte ich das Home-Verzeichnis eines beliebigen Remote-Benutzers in Ansible?


77

Ich kann das mit Shell machen, indem ich eine Kombination aus getentund awkwie folgt verwende:

getent passwd $user | awk -F: '{ print $6 }'

Als Referenz kann ich in Puppet eine benutzerdefinierte Tatsache wie die folgende verwenden:

require 'etc'

Etc.passwd { |user|

   Facter.add("home_#{user.name}") do
      setcode do
         user.dir
      end
   end

}

home_<user name>Dadurch wird das Home-Verzeichnis des Benutzers als Tatsache verfügbar .

Wie erhalte ich das Home-Verzeichnis eines beliebigen Remote-Benutzers?

Antworten:


69

Ansible (ab 1.4) zeigt bereits Umgebungsvariablen für den Benutzer unter der ansible_envVariablen an.

- hosts: all
  tasks:
    - name: debug through ansible.env
      debug: var=ansible_env.HOME

Leider können Sie dies anscheinend nur verwenden, um Umgebungsvariablen für den verbundenen Benutzer abzurufen, wie dieses Playbook und die Ausgabe zeigen:

- hosts: all
  tasks:
    - name: debug specified user's home dir through ansible.env
      debug: var=ansible_env.HOME
      become: true
      become_user: "{{ user }}"

    - name: debug specified user's home dir through lookup on env
      debug: var=lookup('env','HOME')
      become: true
      become_user: "{{ user }}"

AUSGABE :

vagrant@Test-01:~$ ansible-playbook -i "inventory/vagrant" env_vars.yml -e "user=testuser"

PLAY [all] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [192.168.0.30]

TASK: [debug specified user's home dir through ansible.env] *******************
ok: [192.168.0.30] => {
    "var": {
        "/home/vagrant": "/home/vagrant"
    }
}

TASK: [debug specified user's home dir through lookup on env] *****************
ok: [192.168.0.30] => {
    "var": {
        "/home/vagrant": "/home/vagrant"
    }
}

PLAY RECAP ********************************************************************
192.168.0.30               : ok=3    changed=0    unreachable=0    failed=0

Wie bei allem in ansible können , wenn Sie ein Modul , das Sie nicht bekommen, was Sie wollen , dann sind Sie immer frei Shell aus (obwohl dies sparsam verwendet werden sollte , da es zerbrechlich sein kann und wird weniger beschreibend) so etwas wie dies mit ::

- hosts: all
  tasks:
    - name: get user home directory
      shell: >
             getent passwd {{ user }}  | awk -F: '{ print $6 }'
      changed_when: false
      register: user_home

    - name: debug output
      debug:
        var: user_home.stdout

Möglicherweise gibt es einen saubereren Weg, dies zu tun, und ich bin ein wenig überrascht, dass die Verwendung become_userzum Wechseln zu dem angegebenen Benutzer die envSuche nicht zu beeinflussen scheint, aber dies sollte Ihnen das geben, was Sie wollen.


Ich weiß das, aber es funktioniert nur für den aktuellen Benutzer. Ich möchte das Home-Verzeichnis eines anderen Benutzers erhalten (z. B. Konfiguration von gemeinsam genutzten Workstations)
Adam Ryczkowski

Sieht so aus, als würde become_userdas nicht aktualisiert, envalso bin ich mir nicht sicher, ob es einen saubereren Ansatz gibt, als nur zu
beschießen

Dies funktioniert nicht überall für Hosts, die mit LDAP oder einem anderen Verzeichnisserver verbunden sind. Dafür müssen Sie getentstattdessen verwenden.
TrinitronX

sudo_userund become_usersind nicht so portabel über verschiedene Versionen von Ansible auch. getentscheint mir hier immer noch die beste Lösung zu sein.
TrinitronX

3
Versuchte dies heute (ansible 2.5). ansible_env.HOMEGibt das HOME des Remote-Benutzers zurück (ist jedoch nicht betroffen von become). Allerdings lookup('env','HOME')gibt die HOME des Benutzers , das Textbuch am Controller ausgeführt wird .
jsantander

32

Ich denke, hier gibt es mehrere Antworten, die funktionieren würden, aber ich dachte, ich würde zeigen, dass Sie dies vom ansible-Benutzermodul erhalten können , indem Sie es als Variable registrieren.

- user:
    name: www-data
    state: present
  register: webserver_user_registered

Hinweis: Der Benutzer wird erstellt, wenn er nicht vorhanden ist ...

Wir können also Debug verwenden, um die Werte dieser Variablen anzuzeigen, einschließlich des Pfads ...

- debug:
    var: webserver_user_registered

TASK [wordpress : debug] ******************
ok: [wordpresssite.org] => {
    "webserver_user_registered": {
        "append": false,
        "changed": false,
        "comment": "www-data",
        "failed": false,
        "group": 33,
        "home": "/var/www",      <<------ this is the user home dir
        "move_home": false,
        "name": "www-data",
        "shell": "/usr/sbin/nologin",
        "state": "present",
        "uid": 33
    }
}

Und Sie können diese Eigenschaften in anderen Modulen wie diesem verwenden.

- file:
    name: "{{ webserver_user_registered.home }}/.wp-cli"
    state: directory

Dies sollte eine akzeptierte richtige Antwort sein, da dies die einzige plattformübergreifende Lösung ist, die auch recht elegant ist. getentDie Lösung funktioniert auch mit dem ansible getent-Modul unter MacOS (zumindest High Sierra) nicht, da die Informationen des Nicht-Systembenutzers aus der passwd-Datenbank entfernt wurden. Und die Lösung in der akzeptierten Antwort funktioniert nur für Linux-verwaltete Hosts.
Drew

Ich meinte, es ist "ziemlich elegant", weil es kurz ist und Sie das Objekt oder sogar eine Liste von Objekten mit gut benannten Eigenschaften wie .nameund erhalten .home.
Drew

Ich mag das, aber ich usermache mir Sorgen, dass das Modul den Benutzer erstellt, wenn es nicht existiert, anstatt zu versagen. Das wäre ein ernsthaft seltsamer Fall, und ich denke, Sie könnten so etwas wie eine Fehleraufgabe mit a when: user.changedausführen, aber die Erstellung des Benutzers wird dadurch nicht rückgängig gemacht. Ich denke, Sie könnten es in einen Block einwickeln und den Fehler mit einem Befehl beheben, der den Benutzer entfernt und dann einen weiteren Fehler im Rettungsblock hinzufügt.
Etwas

@ydaetskcoR Ja, die Standardeinstellung für stateist present, so wie geschrieben, wird der Benutzer erstellt, wenn er nicht vorhanden ist. Es macht jedoch keinen Sinn, das Home-Verzeichnis eines nicht vorhandenen Benutzers abzurufen. Wenn das Erstellen also Probleme verursachen würde, müssten Sie sich darum kümmern.
Tom H

Dies ist die eleganteste Methode zum Nachschlagen des Home-Verzeichnisses für einen bereits vorhandenen Linux-Benutzer.
Aidan Melen

25

Ansible 1.8 stellte das getentModul vor . Es registriert das getent-Ergebnis als Host-Tatsache - in diesem Fall ist es das getent_passwd.

Beispiele:

Drucken Sie den Home-Ordner für eine bestimmte user:

---

- getent:
    database: passwd
    key: "{{ user }}"
    split: ":"

- debug:
    msg: "{{ getent_passwd[user][4] }}"

Sammeln Sie eine Nachschlagetabelle ( user_homes), die Hebelwirkung set_factund den Jinja2- combine()Filter :

---

- assert:
    that:
      - user_name is defined

- when: user_homes is undefined or user_name not in user_homes
  block:
    - name: getent
      become: yes
      getent:
        database: passwd
        key: "{{ user_name }}"
        split: ":"

    - name: set fact
      set_fact:
        "user_homes": "{{ user_homes | d({}) | combine({user_name: getent_passwd[user_name][4]}) }}"

Wäre aber besser mit einem benutzerdefinierten Faktenmodul.


getentwurde in Ansible 1.8
myrdd

15

Das Problem

Die lookup()oder ENV var-Methoden zum Auffinden des Hauses eines beliebigen Benutzers funktionieren mit Ansible leider nicht zuverlässig, da sie wie vom Benutzer angegeben --user=REMOTE_USERund optional mit sudo(wenn sudo: yesim Playbook oder --sudobestanden) ausgeführt werden. Diese beiden Ausführungsmodi (sudo oder kein sudo) ändern die Shell-Umgebung, in der Ansible ausgeführt wird, und selbst dann sind Sie auf den als -u REMOTE_USERoder angegebenen Benutzer beschränkt root.

Sie könnten versuchen, es zu verwenden sudo: yes, und sudo_user: myarbitraryuserzusammen ... Aufgrund eines Fehlers in bestimmten Versionen von Ansible können Sie jedoch feststellen , dass es sich nicht so verhält, wie es sollte. Wenn Sie auf ansible sind> = 1.9, können Sie verwenden become: true, und become_user: myarbitraryuserstattdessen. Dies bedeutet jedoch, dass die von Ihnen geschriebenen Playbooks und Rollen in früheren Versionen von Ansible nicht funktionieren.

Wenn Sie nach einer tragbaren Methode suchen, um das Home-Verzeichnis eines Benutzers abzurufen, das auch mit LDAP oder einem anderen Verzeichnisdienst funktioniert, verwenden Sie getent.

Ansible getent Beispiel

Erstellen Sie ein einfaches Spielbuch mit dem Namen: playbooks/ad-hoc/get-user-homedir.yml

- hosts: all
  tasks:
    - name:
      shell: >
        getent passwd {{ user }} | cut -d: -f6
      changed_when: false
      register: user_home

    - name: debug output
      debug: var=user_home.stdout

Führen Sie es aus mit:

ansible-playbook -i inventory/racktables.py playbooks/ad-hoc/get-user-homedir.yml -e "user=someuser"

getent ist nicht auf mac osx 10.12.6
AnneTheAgile

1
@AnneTheAgile: Sie haben Recht, es sieht so aus, als ob OSX 10.12.6dieses Dienstprogramm nicht hat. Alternativen sind `dscacheutil -q Benutzer -a Name {{Benutzer}} , sudo dscl. -ls / Users`, um Benutzer aufzulisten und dscl . -read /Users/{{ user }}Informationen über einen Benutzer zu sichern . Für Benutzer und Gruppen im Einzelbenutzermodus gibt es auch immer /etc/passwdund /etc/groupweil OSX zumindest Unix-kompatibel mit diesen Dateien ist, wenn es im Einzelbenutzermodus ausgeführt wird. Andere Benutzer befinden sich im opendirectorydKommentar oben in diesen Dateien.
TrinitronX

1
@AnneTheAgile: Alternativ sieht es so aus, als hätte jemand ein getentBefehlszeilenprogramm erstellt, das Sie hier unter OSX kompilieren und verwenden können . git clone https://github.com/petere/getent-osx.git && cd getent-osx make ; make install
TrinitronX

Es geht weniger um Software-Zuverlässigkeit als vielmehr um vorhersehbares Verhalten. Wenn eine bestimmte Funktion so funktioniert, wie sie entworfen wurde, ist sie in Ordnung. Wenn Sie jedoch etwas anderes erwarten, besteht eine Diskrepanz zwischen Ihnen und dem Design. Vielleicht ist das Design nicht konsistent und daher ist es wahrscheinlich, dass die Codierer in solche nicht so offensichtlichen Fallstricke geraten, die sich aus dieser mangelnden Konsistenz ergeben.
Alexander Stohr

3

Sie können verwenden expanduser.

Zum Beispiel beim Durchlaufen einer Benutzerliste:

- name: Deploys .bashrc
  template:
    src: bashrc.j2
    dest: "{{ '~' + item | expanduser }}/.bashrc"
    mode: 0640
    owner: "{{ item }}"
    group: "{{ item }}"
  with_items: user_list

7
Beachten Sie, dass dadurch Benutzer auf dem Controller-Host und nicht auf dem Zielhost erweitert werden.
Marius Gedminas

3

Ich weiß, dass dies ein ziemlich alter Thread ist, aber ich denke, dies ist ein etwas einfacherer Weg, um das Home-Verzeichnis des Benutzers zu erhalten

- name: Get users homedir
  local_action: command echo ~
  register: homedir

Auf Linux- (oder Unix-) Systemen zeigt das Tilde-Zeichen auf das Home-Verzeichnis des Benutzers.


Die Verwendung von echo ~ username ist wahrscheinlich eine der portableren Methoden, um das zu erreichen, was in der ursprünglichen Frage gestellt wurde. Ein großer Vorteil ist, dass Sie mit dieser Methode nicht herumspielen müssen.
Brian Aker

"~ Benutzername" schlägt nicht fehl, wenn dieses Unterverzeichnis nicht aufgelöst werden kann, sondern gibt nur eine Zeichenfolge zurück, die mit der Eingabeabfrage identisch ist
Alexander Stohr

3

In jeder Antwort wird erwähnt, wie die Details des Basisverzeichnisses gedruckt werden, während das Playbook ausgeführt und mit Debug und Var auf dem Bildschirm angezeigt wird .

Anpassung an die Antwort von @TrinitronX

Zusätzliche Informationen zur Verwendung dieser Informationen für eine neue Aufgabe.

Ich habe eine Liste von Benutzern, deren Ausgangsverzeichnis extrahiert werden muss. Also habe ich die Benutzerdetails zu einer Liste hinzugefügt

- name: Get home directory
  shell: >
         getent passwd {{ item.user }} | cut -d: -f6
  changed_when: false
  with_items:
   - "{{java}}"
  register: user_home

Hier durchläuft dieser Schritt alle Benutzerlisten und registriert diese Details bei user_home. Und dies wird in Form eines Arrays sein.

Der nächste Schritt besteht darin, diese Informationen für eine neue Aufgabe zu verwenden, z. B. für die Beschaffung einer Datei in ein Bash-Profil. Dies ist nur ein Beispiel und kann ein beliebiges Szenario sein, die Methode bleibt jedoch gleich.

- name: Set Java home in .bash_profile
  lineinfile: path="{{ item.stdout }}/.bash_profile" regexp='^source "{{ java_dir }}/.bash_profile_java"' line='source "{{ java_dir }}/.bash_profile_java"' state=present
  with_items:
   - "{{ user_home.results }}"
  loop_control:
    label: "{{ item.stdout }}"

Ich habe eine Tatsache für java_dir auf / usr / java / latest im selben Playbook gesetzt.

Das Array user_home.results enthält die Details der Aufgabe "Home-Verzeichnis abrufen". Jetzt durchlaufen wir dieses Array und nehmen den Standardwert heraus, der den Pfad des Ausgangsverzeichnisses enthält.

Ich habe loop_control nur zum Drucken des Home-Verzeichnisses gesetzt, sonst wird das gesamte Array gedruckt.

Durch diesen Prozess können wir sicherstellen, dass wir, wenn n Benutzer vorhanden sind, diese Methode befolgen können und alle erledigt werden.

Hinweis: Ich habe begonnen, Ansible zu lernen. Falls eine von mir verwendete Terminologie falsch ist, entschuldigen Sie dies bitte. Ich habe einige Zeit damit verbracht, herauszufinden, wie das geht, und darüber nachgedacht, dasselbe zu teilen.


2
getent ist nicht tragbar. OSX ist das offensichtlichste Beispiel dafür, wo Sie getentt nicht finden.
Brian Aker

Die Verwendung von "cut" anstelle von "awk" ist hilfreich, wenn awk nicht installiert ist. Schnitt ist in den meisten Fällen viel wahrscheinlicher vorhanden, daher würde ich ihn bevorzugen.
Alexander Stohr

2

Ich bin zu diesem Thread gekommen, weil ich die PGDATA env-Variable vom postgres-Benutzer drucken musste. Ich habe nicht gefunden, wie ich es "ansuell" in ansible machen kann, aber ich habe damit aufgehört, dass dies funktioniert:

    - name: Find postgresql data directory
        shell: 'echo $PGDATA'
        become: yes
        become_user: postgres
        become_flags: "-i "
        register: pgdata_dir

Dann kann ich das in einem anderen Job mit "{{pgdata_dir.stdout}}" referenzieren.


Guter Punkt mit der Option "-i". Es kann einige zusätzliche Auswirkungen auf Shell-Optionen wie das Abbruchverhalten oder die Eingabeaufforderung haben. Solange sich jedoch nicht herausstellt, dass es destruktiv stört, verwenden Sie es nur, um Ihren speziellen Job zu erledigen.
Alexander Stohr

1

In Ansible gibt es derzeit keine einfache Möglichkeit, dies zu tun. Deshalb sollten Sie Ihre Stimmen zu diesem Thema hinzufügen

https://github.com/ansible/ansible/issues/15901

Während Sie diese Problemumgehung verwenden können: https://stackoverflow.com/a/33343455/99834 , sollten Sie nicht vergessen, das Feedback zu senden, dass dies einfach zu verwenden sein soll.


Das Problem ist immer noch nicht gelöst und Ansible hat das Problem geschlossen und für Kommentare und Abstimmungen gesperrt ...: /
Hubro
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.