Documentation

变量

虽然存在自动化以使事情更容易重复,但您的所有系统可能都不完全相同。

在某些系统上,您可能希望设置一些与其他系统略有不同的行为或配置。

此外,某些观察到的远程系统行为或状态可能需要影响您配置这些系统的方式。 (例如,您可能需要找出系统的IP地址,甚至将其用作另一个系统上的配置值)。

您可能有一些配置文件的模板大致相同,但基于这些变量略有不同。

Ansible中的变量是我们如何处理系统之间的差异。

要了解变量,您还需要深入研究条件语句循环语句 有用的东西,如group_by模块和when条件也可用于变量,并帮助管理系统之间的差异。

强烈建议您查阅ansible-examples github存储库以查看大量使用的变量示例。

有关最佳实践建议,请参阅最佳实践一章中的变量​​和Vaults

是什么使变量名称有效的

在我们开始使用变量之前,了解哪些是有效的变量名称很重要。

变量名称应为字母,数字和下划线。 变量应始终以字母开头。

foo_port是一个很棒的变量。 foo5也很好。

foo-portfoo portfoo.port12 不是有效的变量名。

YAML还支持将键映射到值的字典。 例如:

foo:
  field1: one
  field2: two

然后,您可以使用括号表示法或点表示法引用字典中的特定字段:

foo['field1']
foo.field1

这些都将引用相同的值(“one”)。 但是,如果您选择使用点表示法,请注意某些键可能会导致问题,因为它们会与python词典的属性和方法发生冲突。 如果使用以两个下划线开头和结尾的键(那些是为python中的特殊含义保留的)或者是任何已知的公共属性,则应使用括号表示法而不是点表示法:

add, append, as_integer_ratio, bit_length, capitalize, center, clear, conjugate, copy, count, decode, denominator, difference, difference_update, discard, encode, endswith, expandtabs, extend, find, format, fromhex, fromkeys, get, has_key, hex, imag, index, insert, intersection, intersection_update, isalnum, isalpha, isdecimal, isdigit, isdisjoint, is_integer, islower, isnumeric, isspace, issubset, issuperset, istitle, isupper, items, iteritems, iterkeys, itervalues, join, keys, ljust, lower, lstrip, numerator, partition, pop, popitem, real, remove, replace, reverse, rfind, rindex, rjust, rpartition, rsplit, rstrip, setdefault, sort, split, splitlines, startswith, strip, swapcase, symmetric_difference, symmetric_difference_update, title, translate, union, update, upper, values, viewitems, viewkeys, viewvalues, zfill.

库存中定义的变量

实际上我们已经在另一部分已经介绍了很多关于变量的内容,到目前为止,这不应该是非常新的,而是一些复习。

通常,您需要根据机器所在的组来设置变量。 例如,波士顿的机器可能想使用'boston.ntp.example.com'作为NTP服务器。

有关如何在库存中定义变量的多种方法,请参阅使用库存文档。

在Playbook中定义的变量

在playbook中,可以直接内联定义变量,如下所示:

- hosts: webservers
  vars:
    http_port: 80

这可能很好,因为当你正在阅读playbook时它就在那里。

从包含的文件和角色定义的变量

事实证明,我们已经在另一个地方讨论过变量。

Roles中所述,变量也可以通过包含文件包含在playbook中,包含文件可能是也可能不是“ansible角色”的一部分。 首选角色的使用,因为它提供了一个很好的组织系统。

使用变量:关于Jinja2

知道如何定义变量已经足够了,但是如何使用它们呢?

Ansible允许您使用Jinja2模板系统在您的playbook中引用变量。 虽然你可以在Jinja做很多复杂的事情,但只有基础是你最初需要学习的东西。

例如,在一个简单的模板中,您可以执行以下操作:

My amp goes to {{ max_amp_value }}

这将提供最基本的变量替换形式。

这也直接在playbook中有效,你偶尔会想做以下事情:

template: src=foo.cfg.j2 dest={{ remote_install_path }}/foo.cfg

在上面的示例中,我们使用变量来帮助确定放置文件的位置。

在模板内,您可以自动访问主机范围内的所有变量。 实际上它不止于此 - 您还可以读取有关其他主机的变量。 我们将展示如何做到这一点。

Note

ansible允许模板中的Jinja2循环和条件,但在playbooks中,我们不使用它们。 Ansible playbook是纯机器可解析的YAML。 这是一个相当重要的功能,因为它意味着可以编码生成文件,或者让其他生态系统工具读取Ansible文件。 不是每个人都需要这个,但它可以解锁可能性。

参考

模板(Jinja2)
有关Jinja2模板的更多信息

Jinja2过滤器

Note

这些是不经常使用的功能。 如果它们适合您的用例,请使用它们,但这是可选知识。

Jinja2中的过滤器是一种将模板表达式从一种数据转换为另一种数据的方法。 Jinja2附带了许多这样的产品。 请参阅官方Jinja2模板文档中的内置过滤器

除此之外,Ansible还提供更多产品。 有关可用过滤器和示例使用指南的列表,请参阅过滤器文档。

一个YAML 简单教程

YAML语法要求如果您在行开头使用{{ foo }}一个值,则引用整行,因为它想确保你没有尝试启动YAML词典。 这包含在YAML语法文档中。

无效的变量引用:

- hosts: app_servers
  vars:
      app_path: {{ base_path }}/22

像这样在行开头引用变量:

- hosts: app_servers
  vars:
       app_path: "{{ base_path }}/22"

从系统发现的信息:Facts

还有来自其他地方的变量,但这些是发现的变量类型,不是由用户设置的。

Facts 是通过与您的远程系统通话而获得的信息。

例如,远程主机的IP地址或操作系统是类型。

要查看可用的信息,请尝试以下操作:

ansible hostname -m setup

这将返回大量可变数据,如下所示,取自在Ubuntu 12.04系统上运行的Ansible 1.4

{
    "ansible_all_ipv4_addresses": [
        "REDACTED IP ADDRESS"
    ],
    "ansible_all_ipv6_addresses": [
        "REDACTED IPV6 ADDRESS"
    ],
    "ansible_architecture": "x86_64",
    "ansible_bios_date": "09/20/2012",
    "ansible_bios_version": "6.00",
    "ansible_cmdline": {
        "BOOT_IMAGE": "/boot/vmlinuz-3.5.0-23-generic",
        "quiet": true,
        "ro": true,
        "root": "UUID=4195bff4-e157-4e41-8701-e93f0aec9e22",
        "splash": true
    },
    "ansible_date_time": {
        "date": "2013-10-02",
        "day": "02",
        "epoch": "1380756810",
        "hour": "19",
        "iso8601": "2013-10-02T23:33:30Z",
        "iso8601_micro": "2013-10-02T23:33:30.036070Z",
        "minute": "33",
        "month": "10",
        "second": "30",
        "time": "19:33:30",
        "tz": "EDT",
        "year": "2013"
    },
    "ansible_default_ipv4": {
        "address": "REDACTED",
        "alias": "eth0",
        "gateway": "REDACTED",
        "interface": "eth0",
        "macaddress": "REDACTED",
        "mtu": 1500,
        "netmask": "255.255.255.0",
        "network": "REDACTED",
        "type": "ether"
    },
    "ansible_default_ipv6": {},
    "ansible_devices": {
        "fd0": {
            "holders": [],
            "host": "",
            "model": null,
            "partitions": {},
            "removable": "1",
            "rotational": "1",
            "scheduler_mode": "deadline",
            "sectors": "0",
            "sectorsize": "512",
            "size": "0.00 Bytes",
            "support_discard": "0",
            "vendor": null
        },
        "sda": {
            "holders": [],
            "host": "SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)",
            "model": "VMware Virtual S",
            "partitions": {
                "sda1": {
                    "sectors": "39843840",
                    "sectorsize": 512,
                    "size": "19.00 GB",
                    "start": "2048"
                },
                "sda2": {
                    "sectors": "2",
                    "sectorsize": 512,
                    "size": "1.00 KB",
                    "start": "39847934"
                },
                "sda5": {
                    "sectors": "2093056",
                    "sectorsize": 512,
                    "size": "1022.00 MB",
                    "start": "39847936"
                }
            },
            "removable": "0",
            "rotational": "1",
            "scheduler_mode": "deadline",
            "sectors": "41943040",
            "sectorsize": "512",
            "size": "20.00 GB",
            "support_discard": "0",
            "vendor": "VMware,"
        },
        "sr0": {
            "holders": [],
            "host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)",
            "model": "VMware IDE CDR10",
            "partitions": {},
            "removable": "1",
            "rotational": "1",
            "scheduler_mode": "deadline",
            "sectors": "2097151",
            "sectorsize": "512",
            "size": "1024.00 MB",
            "support_discard": "0",
            "vendor": "NECVMWar"
        }
    },
    "ansible_distribution": "Ubuntu",
    "ansible_distribution_release": "precise",
    "ansible_distribution_version": "12.04",
    "ansible_domain": "",
    "ansible_env": {
        "COLORTERM": "gnome-terminal",
        "DISPLAY": ":0",
        "HOME": "/home/mdehaan",
        "LANG": "C",
        "LESSCLOSE": "/usr/bin/lesspipe %s %s",
        "LESSOPEN": "| /usr/bin/lesspipe %s",
        "LOGNAME": "root",
        "LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:",
        "MAIL": "/var/mail/root",
        "OLDPWD": "/root/ansible/docsite",
        "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "PWD": "/root/ansible",
        "SHELL": "/bin/bash",
        "SHLVL": "1",
        "SUDO_COMMAND": "/bin/bash",
        "SUDO_GID": "1000",
        "SUDO_UID": "1000",
        "SUDO_USER": "mdehaan",
        "TERM": "xterm",
        "USER": "root",
        "USERNAME": "root",
        "XAUTHORITY": "/home/mdehaan/.Xauthority",
        "_": "/usr/local/bin/ansible"
    },
    "ansible_eth0": {
        "active": true,
        "device": "eth0",
        "ipv4": {
            "address": "REDACTED",
            "netmask": "255.255.255.0",
            "network": "REDACTED"
        },
        "ipv6": [
            {
                "address": "REDACTED",
                "prefix": "64",
                "scope": "link"
            }
        ],
        "macaddress": "REDACTED",
        "module": "e1000",
        "mtu": 1500,
        "type": "ether"
    },
    "ansible_form_factor": "Other",
    "ansible_fqdn": "ubuntu2.example.com",
    "ansible_hostname": "ubuntu2",
    "ansible_interfaces": [
        "lo",
        "eth0"
    ],
    "ansible_kernel": "3.5.0-23-generic",
    "ansible_lo": {
        "active": true,
        "device": "lo",
        "ipv4": {
            "address": "127.0.0.1",
            "netmask": "255.0.0.0",
            "network": "127.0.0.0"
        },
        "ipv6": [
            {
                "address": "::1",
                "prefix": "128",
                "scope": "host"
            }
        ],
        "mtu": 16436,
        "type": "loopback"
    },
    "ansible_lsb": {
        "codename": "precise",
        "description": "Ubuntu 12.04.2 LTS",
        "id": "Ubuntu",
        "major_release": "12",
        "release": "12.04"
    },
    "ansible_machine": "x86_64",
    "ansible_memfree_mb": 74,
    "ansible_memtotal_mb": 991,
    "ansible_mounts": [
        {
            "device": "/dev/sda1",
            "fstype": "ext4",
            "mount": "/",
            "options": "rw,errors=remount-ro",
            "size_available": 15032406016,
            "size_total": 20079898624
        }
    ],
    "ansible_nodename": "ubuntu2.example.com",
    "ansible_os_family": "Debian",
    "ansible_pkg_mgr": "apt",
    "ansible_processor": [
        "Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz"
    ],
    "ansible_processor_cores": 1,
    "ansible_processor_count": 1,
    "ansible_processor_threads_per_core": 1,
    "ansible_processor_vcpus": 1,
    "ansible_product_name": "VMware Virtual Platform",
    "ansible_product_serial": "REDACTED",
    "ansible_product_uuid": "REDACTED",
    "ansible_product_version": "None",
    "ansible_python_version": "2.7.3",
    "ansible_selinux": false,
    "ansible_ssh_host_key_dsa_public": "REDACTED KEY VALUE",
    "ansible_ssh_host_key_ecdsa_public": "REDACTED KEY VALUE",
    "ansible_ssh_host_key_rsa_public": "REDACTED KEY VALUE",
    "ansible_swapfree_mb": 665,
    "ansible_swaptotal_mb": 1021,
    "ansible_system": "Linux",
    "ansible_system_vendor": "VMware, Inc.",
    "ansible_user_id": "root",
    "ansible_userspace_architecture": "x86_64",
    "ansible_userspace_bits": "64",
    "ansible_virtualization_role": "guest",
    "ansible_virtualization_type": "VMware"
}

在上文中,第一个硬盘的模型可以在模板或playbook中引用为:

{{ ansible_devices.sda.model }}

同样,系统报告的主机名是:

{{ ansible_nodename }}

并且非限定主机名在第一个句点之前显示字符串(.):

{{ ansible_hostname }}

Facts 经常用于条件(参见条件语句)以及模板中。

Facts还可用于创建符合特定条件的动态主机组,有关详细信息,请参阅group_by上的使用模块文档,以及所讨论的通用条件语句在条件语句章节中。

关闭Facts

如果您知道自己不需要任何有关主机的Facts数据,并且集中了解系统的所有信息,则可以关闭Facts收集。 这有利于在推送模式下使用大量系统扩展Ansible,主要是,或者如果您在实验平台上使用Ansible。 在任何playbook中,只需这样做:

- hosts: whatever
  gather_facts: no

本机 Facts(Facts.d)

版本1.3中的新功能。

正如playbook章节中所讨论的,Ansible Facts是获取有关用于playbook变量的远程系统数据的一种方式。

通常这些是由Ansible中的setup模块自动发现的。 用户还可以编写自定义 Facts模块,如API指南中所述。 但是,如果您想要一种简单的方法来提供系统或用户提供的数据以便在Ansible变量中使用而不编写 Facts模块,该怎么办?

例如,如果您希望用户能够控制有关其系统管理方式的某些方面,该怎么办? “Facts.d”就是这样一种机制。

Note

也许“本地 Facts”有点用词不当,它意味着“本地提供的用户值”而不是“集中提供的用户值”,或者说是什么 Facts - “本地动态确定的值”。

如果远程管理的系统具有/etc/ansible/facts.d目录,则此目录中以.fact结尾的任何文件都可以是JSON,INI或可执行文件返回JSON,这些可以在Ansible中提供本地 Facts。 可以使用fact_pathplay 指令指定备用目录。

例如/etc/ansible/facts.d/preferences.fact

[general]
asdf=1
bar=2

这将生成一个名为general的哈希变量fact,其中asdfbar作为成员。 要验证这一点,请运行以下命令:

ansible <hostname> -m setup -a "filter=ansible_local"

你会看到以下fact:

"ansible_local": {
        "preferences": {
            "general": {
                "asdf" : "1",
                "bar"  : "2"
            }
        }
 }

这些数据可以在template/playbook中访问:

{{ ansible_local.preferences.general.asdf }}

本地命名空间可防止任何用户提供的事实覆盖在playbook中其他地方定义的系统facts或变量。

Note

key=value 对中的关键部分将在ansible_local变量内转换为小写。 使用上面的示例,如果ini文件在[general]部分中包含XYZ=3,那么您应该访问它使用:{ { ansible_local.preferences.general.xyz }}而不是{{ ansible_local .preferences.general.XYZ }} 这是因为Ansible使用Python的ConfigParser,它通过optionxform方法传递所有选项名称,并且此方法的默认实现将选项名称转换为小写。

如果您有一个正在复制自定义 fact 然后运行它的playbook,则显式调用以重新运行 setup 模块可以允许在该特定play期间使用该fact。 否则,它将在下一个收集fact信息的playbook中提供。 以下是可能的示例:

- hosts: webservers
  tasks:
    - name: create directory for ansible custom facts
      file: state=directory recurse=yes path=/etc/ansible/facts.d
    - name: install custom impi fact
      copy: src=ipmi.fact dest=/etc/ansible/facts.d
    - name: re-read facts after adding custom fact
      setup: filter=ansible_local

但是,在这种模式中,您也可以编写一个fact模块,并且可能希望将其视为一个选项。

Ansible 详情

版本1.8中的新功能。

为了使playbook行为适应特定版本的ansible,可以使用变量ansible_version,具有以下结构:

"ansible_version": {
    "full": "2.0.0.2",
    "major": 2,
    "minor": 0,
    "revision": 0,
    "string": "2.0.0.2"
}

Facts 缓存

版本1.8中的新功能。

如文档中的其他地方所示,一个服务器可以引用另一个服务器的变量,如下所示:

{{ hostvars['asdf.example.com']['ansible_os_family'] }}

禁用“Facts缓存”后,为了做到这一点,Ansible必须已经在当前play中与'asdf.example.com'进行过交谈,或者在playbook中进行了更高级别的play。 这是ansible的默认配置。

为避免这种情况,Ansible 1.8允许在playbook运行之间保存Facts,但必须手动启用此功能。 为什么这有用呢?

例如,想象一下,拥有数千台主机的庞大基础设施。 fact 缓存可以配置为每晚运行,但是一小组服务器的配置可以在一天内临时或定期运行。 启用fact 缓存后,没有必要“命中”所有服务器以引用变量和有关它们的信息。

启用fact 缓存后,一组中的机器可能引用另一组中的机器变量,尽管事实上它们尚未在当前执行的 /usr/bin/ansible-playbook中进行通信。

要从缓存的fact中受益,您需要将gathering设置更改为smartexplicit或在大多数plays中将gather_facts更改为False

目前,Ansible附带了两个持久缓存插件:redis和jsonfile。

要使用redis配置fact缓存,请在ansible.cfg中启用它,如下所示:

[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 86400
# seconds

要启动并运行redis,请执行等效的OS命令:

yum install redis
service redis start
pip install redis

请注意,应该从pip安装Python redis库,EPEL中打包的版本太旧,不能被Ansible使用。

在当前实施例中,该特征处于beta级状态,并且Redis插件不支持端口或密码配置,预计这将在不久的将来改变。

要使用jsonfile配置fact缓存,请在ansible.cfg中启用它,如下所示:

[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /path/to/cachedir
fact_caching_timeout = 86400
# seconds

fact_caching_connection是可写目录的本地文件系统路径(如果目录不存在,ansible将尝试创建该目录)。

fact_caching_timeout是缓存记录fact的秒数。

注册变量

变量的另一个主要用途是运行命令并使用该命令的结果将结果保存到变量中。 结果因模块而异。 执行playbooks时使用-v将显示结果的可能值。

在ansible中执行的任务的值可以保存在变量中并在以后使用。 请参阅条件语句一章中的一些示例。

虽然它也在该文档的其他地方提到过,但这是一个快速的语法示例:

- hosts: web_servers

  tasks:

     - shell: /usr/bin/foo
       register: foo_result
       ignore_errors: True

     - shell: /usr/bin/bar
       when: foo_result.rc == 5

注册变量在主机上运行的其余部分有效,这与Ansible中“facts”的生命周期相同。 有效注册的变量就像facts一样。

当使用带有循环的register时,循环期间放置在变量中的数据结构将包含results 属性,该属性是来自模块的所有响应的列表。 有关其工作原理的更深入的示例,请参阅使用带循环 register 的循环语句部分。

Note

如果任务失败或被跳过,变量仍然会注册失败或跳过状态,避免注册变量的唯一方法是使用标记。

访问复杂的可变数据

我们已经在文档中谈到了更高一点的facts。

一些提供的facts(如网络信息)可用作嵌套数据结构。 要访问它们,一个简单的{{ foo }}是不够的,但它仍然很容易做到。 以下是我们获取IP地址的方式:

{{ ansible_eth0["ipv4"]["address"] }}

或者:

{{ ansible_eth0.ipv4.address }}

同样,这是我们访问数组的第一个元素的方式:

{{ foo[0] }}

魔术变量,以及如何访问有关其他主机的信息

Even if you didn’t define them yourself, Ansible provides a few variables for you automatically. The most important of these are hostvars, group_names, and groups. Users should not use these names themselves as they are reserved. environment is also reserved.

hostvars lets you ask about the variables of another host, including facts that have been gathered about that host. If, at this point, you haven’t talked to that host yet in any play in the playbook or set of playbooks, you can still get the variables, but you will not be able to see the facts.

If your database server wants to use the value of a ‘fact’ from another node, or an inventory variable assigned to another node, it’s easy to do so within a template or even an action line:

{{ hostvars['test.example.com']['ansible_distribution'] }}

Additionally, group_names is a list (array) of all the groups the current host is in. This can be used in templates using Jinja2 syntax to make template source files that vary based on the group membership (or role) of the host

{% if 'webserver' in group_names %}
   # some part of a configuration file that only applies to webservers
{% endif %}

groups is a list of all the groups (and hosts) in the inventory. This can be used to enumerate all hosts within a group. For example:

{% for host in groups['app_servers'] %}
   # something that applies to all app servers.
{% endfor %}

A frequently used idiom is walking a group to find all IP addresses in that group

{% for host in groups['app_servers'] %}
   {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}

An example of this could include pointing a frontend proxy server to all of the app servers, setting up the correct firewall rules between servers, etc. You need to make sure that the facts of those hosts have been populated before though, for example by running a play against them if the facts have not been cached recently (fact caching was added in Ansible 1.8).

Additionally, inventory_hostname is the name of the hostname as configured in Ansible’s inventory host file. This can be useful for when you don’t want to rely on the discovered hostname ansible_hostname or for other mysterious reasons. If you have a long FQDN, inventory_hostname_short also contains the part up to the first period, without the rest of the domain.

play_hosts has been deprecated in 2.2, it was the same as the new ansible_play_batch variable.

New in version 2.2.

ansible_play_hosts is the full list of all hosts still active in the current play.

New in version 2.2.

ansible_play_batch is available as a list of hostnames that are in scope for the current ‘batch’ of the play. The batch size is defined by serial, when not set it is equivalent to the whole play (making it the same as ansible_play_hosts).

New in version 2.3.

ansible_playbook_python is the path to the python executable used to invoke the Ansible command line tool.

These vars may be useful for filling out templates with multiple hostnames or for injecting the list into the rules for a load balancer.

Don’t worry about any of this unless you think you need it. You’ll know when you do.

Also available, inventory_dir is the pathname of the directory holding Ansible’s inventory host file, inventory_file is the pathname and the filename pointing to the Ansible’s inventory host file.

playbook_dir contains the playbook base directory.

We then have role_path which will return the current role’s pathname (since 1.8). This will only work inside a role.

And finally, ansible_check_mode (added in version 2.1), a boolean magic variable which will be set to True if you run Ansible with --check.

Variable File Separation

It’s a great idea to keep your playbooks under source control, but you may wish to make the playbook source public while keeping certain important variables private. Similarly, sometimes you may just want to keep certain information in different files, away from the main playbook.

You can do this by using an external variables file, or files, just like this:

---

- hosts: all
  remote_user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/external_vars.yml

  tasks:

  - name: this is just a placeholder
    command: /bin/echo foo

This removes the risk of sharing sensitive data with others when sharing your playbook source with them.

The contents of each variables file is a simple YAML dictionary, like this:

---
# in the above example, this would be vars/external_vars.yml
somevar: somevalue
password: magic

Note

It’s also possible to keep per-host and per-group variables in very similar files, this is covered in Splitting Out Host and Group Specific Data.

Passing Variables On The Command Line

In addition to vars_prompt and vars_files, it is possible to set variables at the command line using the --extra-vars (or -e) argument. Variables can be defined using a single quoted string (containing one or more variables) using one of the formats below

key=value format:

ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"

Note

Values passed in using the key=value syntax are interpreted as strings. Use the JSON format if you need to pass in anything that shouldn’t be a string (Booleans, integers, floats, lists etc).

New in version 1.2.

JSON string format:

ansible-playbook release.yml --extra-vars '{"version":"1.23.45","other_variable":"foo"}'
ansible-playbook arcade.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'

New in version 1.3.

YAML string format:

ansible-playbook release.yml --extra-vars '
version: "1.23.45"
other_variable: foo'

ansible-playbook arcade.yml --extra-vars '
pacman: mrs
ghosts:
- inky
- pinky
- clyde
- sue'

New in version 1.3.

vars from a JSON or YAML file:

ansible-playbook release.yml --extra-vars "@some_file.json"

This is useful for, among other things, setting the hosts group or the user for the playbook.

Escaping quotes and other special characters:

New in version 1.2.

Ensure you’re escaping quotes appropriately for both your markup (e.g. JSON), and for the shell you’re operating in.:

ansible-playbook arcade.yml --extra-vars "{\"name\":\"Conan O\'Brien\"}"
ansible-playbook arcade.yml --extra-vars '{"name":"Conan O'\\\''Brien"}'
ansible-playbook script.yml --extra-vars "{\"dialog\":\"He said \\\"I just can\'t get enough of those single and double-quotes"\!"\\\"\"}"

New in version 1.3.

In these cases, it’s probably best to use a JSON or YAML file containing the variable definitions.

Variable Precedence: Where Should I Put A Variable?

A lot of folks may ask about how variables override another. Ultimately it’s Ansible’s philosophy that it’s better you know where to put a variable, and then you have to think about it a lot less.

Avoid defining the variable “x” in 47 places and then ask the question “which x gets used”. Why? Because that’s not Ansible’s Zen philosophy of doing things.

There is only one Empire State Building. One Mona Lisa, etc. Figure out where to define a variable, and don’t make it complicated.

However, let’s go ahead and get precedence out of the way! It exists. It’s a real thing, and you might have a use for it.

If multiple variables of the same name are defined in different places, they get overwritten in a certain order.

In 1.x, the precedence is as follows (with the last listed variables winning prioritization):

  • “role defaults”, which lose in priority to everything and are the most easily overridden
  • variables defined in inventory
  • facts discovered about a system
  • “most everything else” (command line switches, vars in play, included vars, role vars, etc.)
  • connection variables (ansible_user, etc.)
  • extra vars (-e in the command line) always win

In Ansible version 2.x, we have made the order of precedence more specific (with the last listed variables winning prioritization):

  • role defaults [1]
  • inventory file or script group vars [2]
  • inventory group_vars/all [3]
  • playbook group_vars/all [3]
  • inventory group_vars/* [3]
  • playbook group_vars/* [3]
  • inventory file or script host vars [2]
  • inventory host_vars/*
  • playbook host_vars/*
  • host facts / cached set_facts [4]
  • inventory host_vars/* [3]
  • playbook host_vars/* [3]
  • host facts
  • play vars
  • play vars_prompt
  • play vars_files
  • role vars (defined in role/vars/main.yml)
  • block vars (only for tasks in block)
  • task vars (only for the task)
  • include_vars
  • set_facts / registered vars
  • role (and include_role) params
  • include params
  • extra vars (always win precedence)

Basically, anything that goes into “role defaults” (the defaults folder inside the role) is the most malleable and easily overridden. Anything in the vars directory of the role overrides previous versions of that variable in namespace. The idea here to follow is that the more explicit you get in scope, the more precedence it takes with command line -e extra vars always winning. Host and/or inventory variables can win over role defaults, but not explicit includes like the vars directory or an include_vars task.

Footnotes

[1]Tasks in each role will see their own role’s defaults. Tasks defined outside of a role will see the last role’s defaults.
[2](1, 2) Variables defined in inventory file or provided by dynamic inventory.
[3](1, 2, 3, 4, 5, 6) Includes vars added by ‘vars plugins’ as well as host_vars and group_vars which are added by the default vars plugin shipped with Ansible.
[4]When created with set_facts’s cacheable option, variables will have the high precedence in the play, but will be the same as a host facts precedence when they come from the cache.

Note

Within any section, redefining a var will overwrite the previous instance. If multiple groups have the same variable, the last one loaded wins. If you define a variable twice in a play’s vars: section, the 2nd one wins.

Note

the previous describes the default config hash_behavior=replace, switch to ‘merge’ to only partially overwrite.

Note

Group loading follows parent/child relationships. Groups of the same ‘patent/child’ level are then merged following alphabetical order. This last one can be superceeded by the user via ansible_group_priority, which defaults to 0 for all groups.

Another important thing to consider (for all versions) is that connection variables override config, command line and play/role/task specific options and directives. For example:

ansible -u lola myhost

This will still connect as ramon because ansible_ssh_user is set to ramon in inventory for myhost. For plays/tasks this is also true for remote_user:

- hosts: myhost
  tasks:
   - command: i'll connect as ramon still
     remote_user: lola

This is done so host-specific settings can override the general settings. These variables are normally defined per host or group in inventory, but they behave like other variables. If you want to override the remote user globally (even over inventory) you can use extra vars:

ansible... -e "ansible_user=<user>"

You can also override as a normal variable in a play:

- hosts: all
  vars:
    ansible_user: lola
  tasks:
    - command: i'll connect as lola!

Variable Scopes

Ansible has three main scopes:

  • Global: this is set by config, environment variables and the command line
  • Play: each play and contained structures, vars entries (vars; vars_files; vars_prompt), role defaults and vars.
  • Host: variables directly associated to a host, like inventory, include_vars, facts or registered task outputs

Variable Examples

Let’s show some examples and where you would choose to put what based on the kind of control you might want over values.

First off, group variables are powerful.

Site wide defaults should be defined as a group_vars/all setting. Group variables are generally placed alongside your inventory file. They can also be returned by a dynamic inventory script (see Working With Dynamic Inventory) or defined in things like Ansible Tower from the UI or API:

---
# file: /etc/ansible/group_vars/all
# this is the site wide default
ntp_server: default-time.example.com

Regional information might be defined in a group_vars/region variable. If this group is a child of the all group (which it is, because all groups are), it will override the group that is higher up and more general:

---
# file: /etc/ansible/group_vars/boston
ntp_server: boston-time.example.com

If for some crazy reason we wanted to tell just a specific host to use a specific NTP server, it would then override the group variable!:

---
# file: /etc/ansible/host_vars/xyz.boston.example.com
ntp_server: override.example.com

So that covers inventory and what you would normally set there. It’s a great place for things that deal with geography or behavior. Since groups are frequently the entity that maps roles onto hosts, it is sometimes a shortcut to set variables on the group instead of defining them on a role. You could go either way.

Remember: Child groups override parent groups, and hosts always override their groups.

Next up: learning about role variable precedence.

We’ll pretty much assume you are using roles at this point. You should be using roles for sure. Roles are great. You are using roles aren’t you? Hint hint.

If you are writing a redistributable role with reasonable defaults, put those in the roles/x/defaults/main.yml file. This means the role will bring along a default value but ANYTHING in Ansible will override it. See Roles for more info about this:

---
# file: roles/x/defaults/main.yml
# if not overridden in inventory or as a parameter, this is the value that will be used
http_port: 80

If you are writing a role and want to ensure the value in the role is absolutely used in that role, and is not going to be overridden by inventory, you should put it in roles/x/vars/main.yml like so, and inventory values cannot override it. -e however, still will:

---
# file: roles/x/vars/main.yml
# this will absolutely be used in this role
http_port: 80

This is one way to plug in constants about the role that are always true. If you are not sharing your role with others, app specific behaviors like ports is fine to put in here. But if you are sharing roles with others, putting variables in here might be bad. Nobody will be able to override them with inventory, but they still can by passing a parameter to the role.

Parameterized roles are useful.

If you are using a role and want to override a default, pass it as a parameter to the role like so:

roles:
   - { role: apache, http_port: 8080 }

This makes it clear to the playbook reader that you’ve made a conscious choice to override some default in the role, or pass in some configuration that the role can’t assume by itself. It also allows you to pass something site-specific that isn’t really part of the role you are sharing with others.

This can often be used for things that might apply to some hosts multiple times. For example:

roles:
   - { role: app_user, name: Ian    }
   - { role: app_user, name: Terry  }
   - { role: app_user, name: Graham }
   - { role: app_user, name: John   }

In this example, the same role was invoked multiple times. It’s quite likely there was no default for ‘name’ supplied at all. Ansible can warn you when variables aren’t defined – it’s the default behavior in fact.

There are a few other things that go on with roles.

Generally speaking, variables set in one role are available to others. This means if you have a roles/common/vars/main.yml you can set variables in there and make use of them in other roles and elsewhere in your playbook:

roles:
   - { role: common_settings }
   - { role: something, foo: 12 }
   - { role: something_else }

Note

There are some protections in place to avoid the need to namespace variables. In the above, variables defined in common_settings are most definitely available to ‘something’ and ‘something_else’ tasks, but if “something’s” guaranteed to have foo set at 12, even if somewhere deep in common settings it set foo to 20.

So, that’s precedence, explained in a more direct way. Don’t worry about precedence, just think about if your role is defining a variable that is a default, or a “live” variable you definitely want to use. Inventory lies in precedence right in the middle, and if you want to forcibly override something, use -e.

If you found that a little hard to understand, take a look at the ansible-examples repo on our github for a bit more about how all of these things can work together.

Advanced Syntax

For information about advanced YAML syntax used to declare variables and have more control over the data placed in YAML files used by Ansible, see Advanced Syntax.

See also

Working With Playbooks
An introduction to playbooks
Conditionals
Conditional statements in playbooks
Filters
Jinja2 filters and their uses
Loops
Looping in playbooks
Roles
Playbook organization by roles
Best Practices
Best practices in playbooks
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel