date: 2020-03-15title: ansible变量 #标题
tags: ansible变量 #标签
categories: ansible # 分类

该博文记录ansible变量相关总结笔记。

注册变量

ansible的模块在运行之后,其实都会返回一些”返回值”,只是默认情况下,这些”返回值”并不会显示而已,我们可以把这些返回值写入到某个变量中,这样我们就能够通过引用对应的变量从而获取到这些返回值了,这种将模块的返回值写入到变量中的方法被称为”注册变量”,我们可以在模块后通过register来注册变量,如下:

  1. [root@nginx ansible]# cat a.yml
  2. - name: test
  3. hosts: test1
  4. gather_facts: false
  5. tasks:
  6. - shell: echo 123 > /a.txt
  7. register: testvar
  8. - debug:
  9. msg: "{{ testvar }}"

执行结果如下:

  1. TASK [shell] *******************************************************************
  2. changed: [192.168.20.2]
  3. TASK [debug] *******************************************************************
  4. ok: [192.168.20.2] => { # 我们可以通过{{ testvar.xxxx }}来调用某个变量值,如:{{ testvar.cmd }}
  5. "msg": {
  6. "ansible_facts": {
  7. "discovered_interpreter_python": "/usr/bin/python"
  8. },
  9. "changed": true,
  10. "cmd": "echo 123 > /a.txt",
  11. "delta": "0:00:00.007632",
  12. "end": "2020-03-15 09:29:00.830874",
  13. "failed": false,
  14. "rc": 0,
  15. "start": "2020-03-15 09:29:00.823242",
  16. "stderr": "",
  17. "stderr_lines": [],
  18. "stdout": "",
  19. "stdout_lines": []
  20. }
  21. }

不同的模块,返回值也不尽相同。

交互式输入变量

在运行某些脚本时,有时候脚本会提示用户输入一些信息,脚本需要根据用户输入的信息决定下一步的动作,这种”交互”有时候是必须的,那么,在playbook中该怎样实现这种交互呢?我们可以这样做,提示用户输入信息,然后将用户输入的信息存入到指定的变量中,当我们需要使用这些”输入的信息”时,只要引用对应的变量即可。

例如:

  1. [root@nginx ansible]# cat a.yml
  2. - name: test
  3. hosts: test1
  4. gather_facts: false
  5. vars_prompt:
  6. - name: "your_name"
  7. prompt: "what is your name"
  8. - name: "your_age"
  9. prompt: "how old are you"
  10. tasks:
  11. - name: output vars
  12. debug:
  13. msg: Your name is {{your_name}},You are {{your_age}} years old

执行后如下:

  1. [root@nginx ansible]# ansible-playbook a.yml
  2. what is your name: # 输入的值将赋值到playbook文件中
  3. how old are you:
  4. # 输出如下:
  5. TASK [output vars] ************************
  6. ok: [192.168.20.2] => {
  7. "msg": "Your name is lvjianzhao,You are 18 years old."
  8. }

如你所见,输入的值并不会显示在屏幕中,这种方式比较适合用户输入密码的场景,如果想要显示用户输入的信息,你可以增加private选项,如下:

  1. [root@nginx ansible]# cat a.yml
  2. - name: test
  3. hosts: test1
  4. gather_facts: false
  5. vars_prompt:
  6. - name: "your_name"
  7. prompt: "what is your name"
  8. private: no # 增加此配置项即可显示用户输入的内容
  9. - name: "your_age"
  10. prompt: "how old are you"
  11. private: no
  12. tasks:
  13. - name: output vars
  14. debug:
  15. msg: Your name is {{your_name}},You are {{your_age}} years old.

在上面的注册变量和交互式写入变量可以发现,有时引入变量带了双引号,有时不带,总结来说,如果是以变量开头的,那么需要带双引号,如果变量前还有其他字符,则不需要双引号。千万不要想着不管引用变量前有没有字符都写上双引号,那就错了,比如上面的示例,如果加上双引号,则输出如下:

  1. TASK [output vars] **************************************************
  2. ok: [192.168.20.2] => {
  3. "msg": "Your name is \"dfdfd\",You are \"dfdfd\" years old."
  4. }

交互式输入创建用户并设置密码

  1. [root@nginx ansible]# cat a.yml
  2. - name: test
  3. hosts: test1
  4. gather_facts: false
  5. vars_prompt:
  6. - name: "user_name" # 准备接受用户名
  7. prompt: "Enter user name" # 提示语句
  8. private: no # 用户输入的信息可以显示在屏幕上
  9. - name: "user_password" # 准备接受用户密码
  10. prompt: "Enter user password" # 提示语句
  11. encrypt: "sha512_crypt" # encrypt关键字表示对用户输入的信息进行哈希, “sha512_crypt”表示使用sha512算法对用户输入的信息进行哈希
  12. confirm: yes # 提示第二次输入确认密码
  13. tasks:
  14. - name: create user
  15. user:
  16. name: "{{user_name}}"
  17. password: "{{user_password}}"

需要注意,当使用”encrypt”关键字对字符串进行哈希时,ansible需要依赖passlib库完成哈希操作,如果未安装passlib库(一个用于哈希明文密码的python库),执行playbook时会报如下错误ERROR! passlib must be installed to encrypt vars_prompt values

解决办法:

  1. # pip install passlib # 使用pip命令安装passlib库即可

上面的playbook文件中,任何配置都是存在既合理,如果不对用户输入的密码进行加密,那么保存在/etc/shadow文件中的密码就是一个未经过加密的字符,新用户也不能使用密码进行登录,所以才需要自行加密。
最后执行如下:

  1. [root@nginx ansible]# ansible-playbook a.yml
  2. Enter user name: test2 # 输入的用户名会显示在终端
  3. Enter user password: # 第一次输入密码
  4. confirm Enter user password: # 第二次确认密码
  5. PLAY [test] ********************************************************************
  6. TASK [create user] *************************************************************
  7. changed: [192.168.20.2]
  8. PLAY RECAP *********************************************************************
  9. 192.168.20.2 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
  10. # 如果两次输入的值不一致,则会提示重新输入,如下:
  11. [root@nginx ansible]# ansible-playbook a.yml
  12. Enter user name: test3
  13. Enter user password:
  14. confirm Enter user password:
  15. ***** VALUES ENTERED DO NOT MATCH ****
  16. Enter user password:
  17. confirm Enter user password:

通过命令行传入变量

  1. [root@nginx ansible]# cat a.yml # playbook文件如下
  2. - name: test
  3. hosts: test1
  4. gather_facts: false
  5. tasks:
  6. - debug:
  7. msg: "第一个变量为: {{pass_var1}},第二个变量为: {{pass_var2}}"

执行结果如下:

  1. # 通过命令行传入变量,多个变量名以空格相隔即可
  2. [root@nginx ansible]# ansible-playbook a.yml -e 'pass_var1="test1" pass_var2="test2"'
  3. PLAY [test] ********************************************************************
  4. TASK [debug] *******************************************************************
  5. ok: [192.168.20.2] => {
  6. "msg": "第一个变量为: test1,第二个变量为: test2"
  7. }

注:即使playbook文件中定义了相应的变量,命令行依然可以传入相同名称的变量对其进行覆盖,因为命令行传入变量比在playbook中的优先级要高。

还可以使用json格式传入变量,如下:

  1. [root@nginx ansible]# ansible-playbook a.yml -e '{"pass_var1":"test1","pass_var2":"test2"}'

对应的playbook文件正常引入变量即可。

通过json格式传入稍微复杂些的变量

  1. [root@nginx ansible]# ansible-playbook a.yml -e '{"pass_var1":["test1","test2"]}'
  2. # playbook中引用test1这个值的话,需要使用{{ pass_var1.0 }}
  3. # 如果要引用test2这个值,就是{{ pass_var1.1 }},以此类推。。。

在命令行中引入变量文件

命令行不仅能够传入变量,还能传入变量文件,变量文件中的变量都会一并被传入,变量文件可以是json格式的,也可以是YAML格式的,此处使用YAML格式的变量文件进行示例,示例文件内容如下:

  1. [root@nginx ansible]# cat variables # 变量文件如下
  2. pass_var1: test1
  3. pass_var2:
  4. - one
  5. - two
  6. - three
  7. [root@nginx ansible]# cat a.yml # yml文件如下
  8. - name: test
  9. hosts: test1
  10. gather_facts: false
  11. tasks:
  12. - debug:
  13. msg: "第一个变量为: {{pass_var1}},第二个变量为: {{pass_var2.1}}"
  14. [root@nginx ansible]# ansible-playbook a.yml -e "@variables" # @后面可以是绝对路径,也可以是相对路径。
  15. TASK [debug] *******************************************************************
  16. ok: [192.168.20.2] => {
  17. "msg": "第一个变量为: test1,第二个变量为: two"
  18. }

在清单中定义变量

  1. [root@nginx ansible]# tail -8 hosts
  2. [test1]
  3. 192.168.20.2 var1=host.test1 var2=host.test2 # 直接写在主机后面,优先级最高
  4. [test1:vars] # 通过主机组:vars来定义变量,优先级次于主机后面定义的变量
  5. var1=test1.test1
  6. var2=test1.test2
  7. [all:vars] # all的优先级生效范围是所有主机,优先级最小
  8. var1=all.test1
  9. var2=all.test2

执行结果如下:

  1. ok: [192.168.20.2] => {
  2. "msg": "变量为: host.test1,第二个变量为: host.test2"
  3. }

内置变量

inventory_hostname

  1. [root@nginx ansible]# cat a.yml # playbook如下
  2. - name: test
  3. hosts: test1
  4. gather_facts: false
  5. tasks:
  6. - debug:
  7. msg: "{{inventory_hostname}}"
  8. # 执行结果如下
  9. TASK [debug] *******************************************************************
  10. ok: [192.168.20.2] => {
  11. "msg": "192.168.20.2"
  12. }
  13. ok: [192.168.20.3] => {
  14. "msg": "192.168.20.3"
  15. }

返回的是hosts文件中定义的主机名称,如果定义的是IP,则返回IP。

play_hosts

  1. [root@nginx ansible]# cat a.yml # playbook如下
  2. - name: test
  3. hosts: test1
  4. gather_facts: false
  5. tasks:
  6. - debug:
  7. msg: "{{play_hosts}}"
  8. # 执行结果如下
  9. TASK [debug] *******************************************************************
  10. ok: [192.168.20.3] => {
  11. "msg": [
  12. "192.168.20.2",
  13. "192.168.20.3"
  14. ]
  15. }
  16. ok: [192.168.20.2] => {
  17. "msg": [
  18. "192.168.20.2",
  19. "192.168.20.3"
  20. ]
  21. }

细心如你,一定可以看出来play_hosts和inventory_hostname的区别是什么,没错,inventory_hostname返回的是当前正在执行的节点主机名,play_hosts每次返回的是主机组中所有的主机名。

group_names

用于返回主机所在的主机组名称,如下:

  1. [root@nginx ansible]# ansible 192.168.20.2 -m debug -a "msg={{group_names}}"
  2. 192.168.20.2 | SUCCESS => {
  3. "msg": [
  4. "test1" # 192.168.20.2属于test1主机组
  5. ]
  6. }

inventory_dir

用于返回主机清单所在的绝对路径,如下:

  1. [root@nginx ansible]# ansible 192.168.20.2 -m debug -a "msg={{inventory_dir}}"
  2. 192.168.20.2 | SUCCESS => {
  3. "msg": "/etc/ansible"
  4. }