【问题标题】:How to get an arbitrary remote user's home directory in Ansible?如何在 Ansible 中获取任意远程用户的主目录?
【发布时间】:2016-01-25 09:40:04
【问题描述】:

我可以使用 getentawk 的组合使用 shell 来做到这一点,如下所示:

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> 事实可用。

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

【问题讨论】:

    标签: ansible ansible-facts


    【解决方案1】:

    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 中的任何内容一样,如果您无法获得一个模块来满足您的需求,那么您可以随时使用shell out(尽管这应该谨慎使用,因为它可能很脆弱并且描述性较差) 使用这样的东西:

    - 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 查找,但这应该会给你你想要的.

    【讨论】:

    • 我知道,但它只适用于当前用户。我想获取任何其他用户的主目录(例如共享工作站的配置)
    • 看起来become_user 没有更新env 所以我不确定是否有比炮击更清洁的方法,但应该可以
    • 这不适用于连接到 LDAP 或其他目录服务器的主机。为此,您需要改用getent
    • sudo_userbecome_user 在不同版本的 Ansible 之间也不能移植。 getent 在我看来仍然是最好的解决方案。
    • 今天试过了(ansible 2.5)。 ansible_env.HOME 确实返回远程用户的 HOME(但不受 become 影响)。但是 lookup('env','HOME') 返回在控制器上运行 playbook 的用户的 HOME。
    【解决方案2】:

    我认为这里给出了几个可行的答案,但我想我会证明你可以通过将 ansible user 模块注册为变量来获得它。

    - 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 模块会在用户不存在时创建用户而不是失败。这将是一个非常奇怪的情况,我想你可以做一些事情,比如使用带有when: user.changed 的失败任务,但它不会撤消用户的创建。我想您可以将它包装在一个块中并使用删除用户的命令来挽救故障,然后在救援块中添加另一个故障?有点尴尬:/
    • @ydaetskcoR 是的,state 的默认值是present,所以写的时候,如果用户不存在,它将创建用户。但是,获取不存在用户的主目录没有任何意义,因此如果创建它会导致问题,那么您将不得不对此进行防范。
    • 这是对现有 linux 用户进行主目录查找的最优雅方式。
    【解决方案3】:

    Ansible 1.8 引入了getent module。它将 getent 结果注册为主机事实——在本例中,它是 getent_passwd

    例子:

    打印给定user 的主文件夹:

    ---
    
    - getent:
        database: passwd
        key: "{{ user }}"
        split: ":"
    
    - debug:
        msg: "{{ getent_passwd[user][4] }}"
    

    利用 set_fact 和 Jinja2 combine() filter 累积查找表 (user_homes):

    ---
    
    - 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 中引入
    【解决方案4】:

    问题

    遗憾的是,用于查找任意用户家的 lookup() 或 ENV var 方法无法可靠地与 Ansible 一起使用,因为它以 --user=REMOTE_USER 指定的用户身份运行,并且可选地与 sudo 一起运行(如果在 playbook 中使用 sudo: yes--sudo 通过)。这两种运行模式(sudo 或无 sudo)会改变 Ansible 运行的 shell 环境,即便如此,您也将被限制为指定为 -u REMOTE_USERroot 的用户。

    您可以尝试将sudo: yessudo_user: myarbitraryuser 一起使用...但是由于certain versions of 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 osx 10.12.6 上
    • @AnneTheAgile:你说得对,看起来 OSX 10.12.6 没有这个实用程序。替代方案是 ` dscacheutil -q user -a name {{ user }}, sudo dscl 。 -ls /Users` 列出用户,dscl . -read /Users/{{ user }} 转储有关用户的信息。对于单用户模式的用户和组,也总是有/etc/passwd/etc/group,因为在单用户模式下运行时,OSX 至少与 Unix 兼容。其他用户位于opendirectoryd,正如这些文件顶部的评论所述。
    • @AnneTheAgile:或者,看起来有人创建了一个getent 命令行实用程序,您可以使用compile and use on OSX heregit clone https://github.com/petere/getent-osx.git &amp;&amp; cd getent-osxmake ; make install
    • 与软件可靠性无关,更多与可预测行为有关。当某个功能按设计工作时,它就可以了。但如果你期待一些不同的东西,那么你和设计之间就会存在差异。也许设计不一致,因此可能会让编码人员陷入由于缺乏一致性而引起的不那么明显的陷阱。
    【解决方案5】:

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

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

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

    【讨论】:

    • 使用 echo ~username 可能是完成原始问题所要求的更便携的方法之一。一个很大的优点是,使用这种方法不需要乱七八糟。
    • "~username" 如果这些子目录无法解析,则不会失败,但只返回与输入查询相同的字符串
    【解决方案6】:

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

    适应@TrinitronX answer

    有关将此信息用于新任务的附加信息。

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

    - 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 将包含获取主目录任务的详细信息。 现在我们遍历这个数组并取出包含主目录路径的标准输出值。

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

    通过这个过程,我们可以确保如果有n个用户,我们可以按照这个方法,一切都会得到照顾。

    注意:我已经开始学习 Ansible,如果我使用的任何术语有错误,请原谅。我花了一些时间来弄清楚如何做到这一点,并想分享一下。

    【讨论】:

    • getent 不可移植。 OSX 是您找不到 getentt 的最明显示例。
    • 在未安装 awk 的情况下使用“cut”而不是“awk”很有帮助。 cut 更有可能出现在大部分情况下,所以我会优先考虑它。
    【解决方案7】:

    我来到这个线程是因为我需要从 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 选项产生一些额外的影响,例如中止行为或提示 - 但只要它不会以破坏性方式干扰,您就可以使用它来完成您的特定工作。
    【解决方案8】:

    目前在 Ansible 中没有简单的方法可以做到这一点,这就是为什么你 应该为这个问题添加你的投票

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

    虽然您可以使用此解决方法:https://stackoverflow.com/a/33343455/99834,但您不应该忘记发送您希望它易于使用的反馈。

    【讨论】:

    • 问题仍未解决,Ansible 已关闭问题并将其锁定在 cmets 和投票... :/
    【解决方案9】:

    您可以使用expanduser

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

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

    【讨论】:

    • 请注意,这会扩展控制器主机上的用户,而不是目标主机上的用户。
    猜你喜欢
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-16
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 2016-05-18
    相关资源
    最近更新 更多