如何在Ansible中获取任意远程用户的主目录?


77

我可以使用shell结合使用getentawk例如:

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

作为参考,我可以在Puppet中使用自定义事实,如下所示:

require 'etc'

Etc.passwd { |user|

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

}

这实际上使用户的主目录可用home_<user name>

如何获得任意远程用户的主目录?

Answers:


69

Ansible(从1.4开始)已经在ansible_env变量下为用户显示了环境变量。

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

不幸的是,您显然只能使用它来获取所连接用户的环境变量,如此剧本和输出所示:

- 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 }}"

输出

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

与Ansible中的任何内容一样,如果您无法获得一个模块来提供所需的内容,那么您总是可以使用以下方式随意掏空(尽管应谨慎使用,因为它可能很脆弱并且描述性较低) :

- 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

可能有一种更清洁的方法,使我感到有些惊讶的是,使用become_user切换到指定的用户似乎并不会影响env查找,但这应该可以为您提供所需的信息。


我知道,但是它仅适用于当前用户。我想获取任何其他用户的主目录(例如,配置共享工作站)
Adam Ryczkowski

看起来好像become_user没有更新,env所以我不确定是否有一种更简单的方法而不仅仅是炮击,但这应该可行
ydaetskcoR 2015年

对于连接到LDAP或某些其他目录服务器的主机,这将无法正常解决。为此,您需要使用getent代替。
TrinitronX

sudo_user并且become_user在Ansible的不同版本中也无法移植。 getent在我看来,这仍然是最好的解决方案。
TrinitronX

3
今天对此进行了尝试(可调整的2.5)。ansible_env.HOME确实会返回远程用户的HOME(但不受的影响become)。但是,lookup('env','HOME')返回在控制器上运行剧本的用户的HOME。
jsantander '18

32

我认为这里给出了一些可行的答案,但我想表明,您可以通过将ansible用户模块注册为变量来获取此答案。

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

注意:如果不存在,它将创建用户...

因此,我们可以使用debug来显示该var的值,包括路径...

- 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
    }
}

您可以在其他模块中使用这些属性,如下所示:

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

这应该是一个可以接受的正确答案,因为这是唯一的跨平台解决方案,它也很优雅。getent即使具有ansible getent模块的解决方案也无法在MacOS(至少是High Sierra)上运行,因为非系统用户的信息已从passwd数据库中删除。并且接受的答案中的解决方案仅适用于linux托管主机。
提请

我的意思是说它“非常优雅”,因为它很短,并且您获得的对象甚至对象列表都具有诸如.name和的精美命名的属性.home
提请

我喜欢这个,但是我担心user如果不存在该模块就会创建用户,而不是失败。那将是一个非常奇怪的情况,我想您可以执行将失败任务与a结合使用的操作,when: user.changed但不会撤消用户的创建。我想您可以将其包装在一个块中,然后使用删除用户的命令来挽救故障,然后在救援块中添加另一个故障?Kinda尴尬:/
ydaetskcoR

@ydaetskcoR是的,默认state值为present,因此按照书面形式,如果不存在,它将创建用户。但是,获取不存在的用户的主目录没有任何意义,因此,如果创建它会引起问题,则必须对此加以防范。
汤姆·H

这是对预先存在的linux用户执行主目录查找的最优雅的方法。
艾丹·梅伦

25

Ansible 1.8引入了该getent模块。它将获取的结果记录为宿主事实-在这种情况下为getent_passwd

例子:

打印给定的主文件夹user

---

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

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

累积查找表(user_homes),set_fact并利用Jinja2combine()过滤器

---

- 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]}) }}"

不过,使用自定义事实模块会更好。


getent是在Ansible 1.8中引入的
myrdd '18

15

问题

lookup()遗憾的是,用于查找任意用户房屋的or或ENV var方法无法在Ansible上可靠地工作,因为它按--user=REMOTE_USER,和(可选)sudo(如果sudo: yes在剧本中或--sudo通过)指定的用户身份运行。这两种运行模式(sudo或no sudo)将更改Ansible在其中运行的Shell环境,即使如此,您仍将限于指定为-u REMOTE_USER或的用户root

您可以尝试同时使用sudo: yessudo_user: myarbitraryuser,但是...由于某些版本的Ansible中存在错误,您可能会发现它的行为不正常。如果您使用Ansible> = 1.9,则可以使用become: truebecome_user: myarbitraryuser。但是,这意味着您编写的剧本和角色将无法在早期版本的Ansible上使用。

如果您正在寻找一种可移植的方式来获取用户的主目录,该目录也可用于LDAP或其他目录服务,请使用getent

Ansible getent示例

创建一个简单的剧本,名为: 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

使用以下命令运行它:

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

getent不在Mac OS X 10.12.6上
AnneTheAgile

1
@AnneTheAgile:是的,看起来OSX10.12.6没有此实用程序。替代方法是`dscacheutil -q user -a name {{user}} , sudo dscl。-ls / Users`列出用户,并dscl . -read /Users/{{ user }}转储有关用户的信息。对于单用户模式的用户和组,也总是存在/etc/passwd/etc/group因为以单用户模式运行时,至少OSX与这些文件的Unix兼容。其他用户位于opendirectoryd这些文件顶部的注释中。
TrinitronX

1
@AnneTheAgile:或者,好像有人创建了一个getent命令行实用程序,您可以在此在OSX上进行编译和使用git clone https://github.com/petere/getent-osx.git && cd getent-osx make ; make install
TrinitronX

它对软件可靠性的关注较少,而对可预测行为的关注则更多。当某个功能按其设计工作时,那就很好了。但是如果您期望有所不同,那么您和设计之间会有差异。可能是设计不一致,因此可能会邀请编码人员陷入由于缺乏一致性而导致的不太明显的陷阱。
亚历山大·斯托尔

3

您可以使用expanduser

例如,在遍历用户列表时:

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

7
请注意,这将在控制器主机而不是目标主机上扩展用户。
Marius Gedminas

3

我知道这是一个非常老的线程,但是我认为这是获取用户主目录的一种简单方法

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

在Linux(或Unix)系统上,波浪号指向用户的主目录。


使用echo〜username可能是完成原始问题所要求的更便捷的方法之一。一个很大的优点是,无需使用这种方法就可以解决问题。
Brian Aker

如果这些子目录无法解析,则“〜username”将不会失败,而只会返回与输入查询相同的字符串
Alexander Stohr

3

每个答案都提到在运行剧本并使用debug和var在屏幕上显示它时如何打印主目录详细信息

适应@TrinitronX答案

有关将此信息用于新任务的其他信息。

我有一个需要提取其主目录的用户列表。因此,我已将用户详细信息添加到列表中

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

在这里,此步骤将遍历所有用户列表,并将该详细信息注册到user_home。这将是数组的形式。

然后,下一步是将这些信息用于新任务,例如,将文件源到bash配置文件中。这仅是示例,可以是任何情况,但是方法将保持不变。

- 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 }}"

我已经在同一本书中将java_dir设置为/ usr / java / latest。

数组user_home.results将包含“获取主目录”任务的详细信息。现在,我们遍历此数组并取出包含主目录路径的stdout值。

我放了loop_control仅用于打印主目录,否则它将打印整个数组。

通过此过程,我们可以确保如果存在n个用户,则可以遵循此方法,所有用户都将得到照顾。

注意:我已经开始学习Ansible,如果我使用的任何术语错误,请原谅。我花了一些时间弄清楚如何执行此操作,并考虑了共享它们。


2
getent不可移植。OSX是您找不到getentt的最明显示例。
Brian Aker

在未安装awk的情况下,使用“ cut”代替“ awk”会很有帮助。在大多数情况下,裁员的可能性更大,因此我会优先考虑裁员。
亚历山大·斯托尔

2

我进入这个线程是因为我需要从postgres用户打印PGDATA env变量,我还没有找到如何在ansible中更“本地”地进行操作,但是我结束了这个工作:

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

然后,我可以使用“ {{pgdata_dir.stdout}}”引用另一个作业


使用“ -i”选项的好处。它可能会对shell选项产生一些额外的影响,例如中止行为或提示-但只要它不会以破坏性的方式进行干扰,您就可以使用它来完成您的特定工作。
亚历山大·斯托尔

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.