Ansible 2.8 Porting Guide¶
This section discusses the behavioral changes between Ansible 2.7 and Ansible 2.8.
It is intended to assist in updating your playbooks, plugins and other parts of your Ansible infrastructure so they will work with this version of Ansible.
We suggest you read this page along with Ansible Changelog for 2.8 to understand what updates you may need to make.
This document is part of a collection on porting. The complete list of porting guides can be found at porting guides.
Playbook¶
Distribution Facts¶
The information returned for the ansible_distribution_* group of facts may have changed
slightly. Ansible 2.8 uses a new backend library for information about distributions: nir0s/distro. This library runs on Python-3.8 and fixes many bugs, including correcting release and version names.
The two facts used in playbooks most often, ansible_distribution and ansible_distribution_major_version, should not change. If you discover a change in these facts, please file a bug so we can address the
difference. However, other facts like ansible_distribution_release and
ansible_distribution_version may change as erroneous information gets corrected.
Imports as handlers¶
Beginning in version 2.8, a task cannot notify import_tasks or a static include that is specified in handlers.
The goal of a static import is to act as a pre-processor, where the import is replaced by the tasks defined within the imported file. When using an import, a task can notify any of the named tasks within the imported file, but not the name of the import itself.
To achieve the results of notifying a single name but running multiple handlers, utilize include_tasks, or listen Handlers: Running Operations On Change.
Jinja Undefined values¶
Beginning in version 2.8, attempting to access an attribute of an Undefined value in Jinja will return another Undefined value, rather than throwing an error immediately. This means that you can now simply use a default with a value in a nested data structure when you don’t know if the intermediate values are defined.
In Ansible 2.8:
{{ foo.bar.baz | default('DEFAULT') }}
In Ansible 2.7 and older:
{{ ((foo | default({})).bar | default({})).baz | default('DEFAULT') }}
or
{{ foo.bar.baz if (foo is defined and foo.bar is defined and foo.bar.baz is defined) else 'DEFAULT' }}
Module option conversion to string¶
Beginning in version 2.8, Ansible will warn if a module expects a string, but a non-string value is passed and automatically converted to a string. This highlights potential problems where, for example, a yes or true (parsed as truish boolean value) would be converted to the string 'True', or where a version number 1.10 (parsed as float value) would be converted to '1.1'. Such conversions can result in unexpected behavior depending on context.
This behavior can be changed to be an error or to be ignored by setting the ANSIBLE_STRING_CONVERSION_ACTION environment variable, or by setting the string_conversion_action configuration in the defaults section of ansible.cfg.
Command line facts¶
cmdline facts returned in system will be deprecated in favor of proc_cmdline. This change handles special case where Kernel command line parameter contains multiple values with the same key.
Bare variables in conditionals¶
In Ansible 2.7 and earlier, top-level variables sometimes treated boolean strings as if they were boolean values. This led to inconsistent behavior in conditional tests built on top-level variables defined as strings. Ansible 2.8 began changing this behavior. For example, if you set two conditions like this:
tasks:
- include_tasks: teardown.yml
when: teardown
- include_tasks: provision.yml
when: not teardown
based on a variable you define as a string (with quotation marks around it):
- In Ansible 2.7 and earlier, the two conditions above evaluated as
TrueandFalserespectively ifteardown: 'true' - In Ansible 2.7 and earlier, both conditions evaluated as
Falseifteardown: 'false' - In Ansible 2.8 and later, you have the option of disabling conditional bare variables, so
when: teardownalways evaluates asTrueandwhen: not teardownalways evaluates asFalsewhenteardownis a non-empty string (including'true'or'false')
Ultimately, when: 'string' will always evaluate as True and when: not 'string' will always evaluate as False, as long as 'string' is not empty, even if the value of 'string' itself looks like a boolean. For users with playbooks that depend on the old behavior, we added a config setting that preserves it. You can use the ANSIBLE_CONDITIONAL_BARE_VARS environment variable or conditional_bare_variables in the defaults section of ansible.cfg to select the behavior you want on your control node. The default setting is true, which preserves the old behavior. Set the config value or environment variable to false to start using the new option.
Note
In 2.10 the default setting for conditional_bare_variables will change to false. In 2.12 the old behavior will be deprecated.
Updating your playbooks¶
To prepare your playbooks for the new behavior, you must update your conditional statements so they accept only boolean values. For variables, you can use the bool filter to evaluate the string 'false' as False:
vars:
teardown: 'false'
tasks:
- include_tasks: teardown.yml
when: teardown | bool
- include_tasks: provision.yml
when: not teardown | bool
Alternatively, you can re-define your variables as boolean values (without quotation marks) instead of strings:
vars:
teardown: false
tasks:
- include_tasks: teardown.yml
when: teardown
- include_tasks: provision.yml
when: not teardown
For dictionaries and lists, use the length filter to evaluate the presence of a dictionary or list as True:
- debug:
when: my_list | length > 0
- debug:
when: my_dictionary | length > 0
Do not use the bool filter with lists or dictionaries. If you use bool with a list or dict, Ansible will always evaluate it as False.
Double-interpolation¶
The conditional_bare_variables setting also affects variables set based on other variables. The old behavior unexpectedly double-interpolated those variables. For example:
vars:
double_interpolated: 'bare_variable'
bare_variable: false
tasks:
- debug:
when: double_interpolated
- In Ansible 2.7 and earlier,
when: double_interpolatedevaluated to the value ofbare_variable, in this case,False. If the variablebare_variableis undefined, the conditional fails. - In Ansible 2.8 and later, with bare variables disabled, Ansible evaluates
double_interpolatedas the string'bare_variable', which isTrue.
To double-interpolate variable values, use curly braces:
vars:
double_interpolated: "{{ other_variable }}"
other_variable: false
Nested variables¶
The conditional_bare_variables setting does not affect nested variables. Any string value assigned to a subkey is already respected and not treated as a boolean. If complex_variable['subkey'] is a non-empty string, then when: complex_variable['subkey'] is always True and when: not complex_variable['subkey'] is always False. If you want a string subkey like complex_variable['subkey'] to be evaluated as a boolean, you must use the bool filter.
Gathering Facts¶
In Ansible 2.8 the implicit “Gathering Facts” task in a play was changed to obey play tags. Previous to 2.8, the “Gathering Facts” task would ignore play tags and tags supplied from the command line and always run in a task.
The behavior change affects the following example play.
- name: Configure Webservers
hosts: webserver
tags:
- webserver
tasks:
- name: Install nginx
package:
name: nginx
tags:
- nginx
In Ansible 2.8, if you supply --tags nginx, the implicit
“Gathering Facts” task will be skipped, as the task now inherits
the tag of webserver instead of always.
If no play level tags are set, the “Gathering Facts” task will
be given a tag of always and will effectively match prior
behavior.
You can achieve similar results to the pre-2.8 behavior, by
using an explicit gather_facts task in your tasks list.
- name: Configure Webservers
hosts: webserver
gather_facts: false
tags:
- webserver
tasks:
- name: Gathering Facts
gather_facts:
tags:
- always
- name: Install nginx
package:
name: nginx
tags:
- nginx
Python Interpreter Discovery¶
In Ansible 2.7 and earlier, Ansible defaulted to /usr/bin/python as the
setting for ansible_python_interpreter. If you ran Ansible against a system
that installed Python with a different name or a different path, your playbooks
would fail with /usr/bin/python: bad interpreter: No such file or directory
unless you either set ansible_python_interpreter to the correct value for
that system or added a Python interpreter and any necessary dependencies at
usr/bin/python.
Starting in Ansible 2.8, Ansible searches for the correct path and executable name for Python on each target system, first in a lookup table of default Python interpreters for common distros, then in an ordered fallback list of possible Python interpreter names/paths.
It’s risky to rely on a Python interpreter set from the fallback list, because
the interpreter may change on future runs. If an interpreter from
higher in the fallback list gets installed (for example, as a side-effect of
installing other packages), your original interpreter and its dependencies will
no longer be used. For this reason, Ansible warns you when it uses a Python
interpreter discovered from the fallback list. If you see this warning, the
best solution is to explicitly set ansible_python_interpreter to the path
of the correct interpreter for those target systems.
You can still set ansible_python_interpreter to a specific path at any
variable level (as a host variable, in vars files, in playbooks, etc.).
If you prefer to use the Python interpreter discovery behavior, use
one of the four new values for ansible_python_interpreter introduced in
Ansible 2.8:
| New value | Behavior |
|---|---|
| auto (future default) |
If a Python interpreter is discovered, Ansible uses the discovered Python, even if /usr/bin/python is also present. Warns when using the fallback list. |
| auto_legacy (Ansible 2.8 default) |
If a Python interpreter is discovered, and /usr/bin/python is absent, Ansible uses the discovered Python. Warns when using the fallback list. If a Python interpreter is discovered, and /usr/bin/python is present, Ansible uses /usr/bin/python and prints a deprecation warning about future default behavior. Warns when using the fallback list. |
| auto_legacy_silent | Behaves like auto_legacy but suppresses
the deprecation and fallback-list warnings. |
| auto_silent | Behaves like auto but suppresses the
fallback-list warning. |
In Ansible 2.12, Ansible will switch the default from auto_legacy to auto.
The difference in behaviour is that auto_legacy uses /usr/bin/python if
present and falls back to the discovered Python when it is not present. auto will always
use the discovered Python, regardless of whether /usr/bin/python exists. The
auto_legacy setting provides compatibility with previous versions of Ansible that always
defaulted to /usr/bin/python.
If you installed Python and dependencies (boto, etc.) to
/usr/bin/python as a workaround on distros with a different default Python
interpreter (for example, Ubuntu 16.04+, RHEL8, Fedora 23+), you have two
options:
- Move existing dependencies over to the default Python for each platform/distribution/version.
- Use
auto_legacy. This setting lets Ansible find and use the workaround Python on hosts that have it, while also finding the correct default Python on newer hosts. But remember, the default will change in 4 releases.
Retry File Creation default¶
In Ansible 2.8, retry_files_enabled now defaults to False instead of True. The behavior can be
modified to previous version by editing the default ansible.cfg file and setting the value to True.
Command Line¶
Become Prompting¶
Beginning in version 2.8, by default Ansible will use the word BECOME to prompt you for a password for elevated privileges (sudo privileges on Unix systems or enable mode on network devices):
By default in Ansible 2.8:
ansible-playbook --become --ask-become-pass site.yml
BECOME password:
If you want the prompt to display the specific become_method you’re using, instead of the agnostic value BECOME, set AGNOSTIC_BECOME_PROMPT to False in your Ansible configuration.
By default in Ansible 2.7, or with AGNOSTIC_BECOME_PROMPT=False in Ansible 2.8:
ansible-playbook --become --ask-become-pass site.yml
SUDO password:
Deprecated¶
The
paramsmodule option inldap_attrandldap_entryare deprecated on a short cycle (to be removed in Ansible-2.10) due to circumventing Ansible’s normal option handling. In particular, if thebind_pwoption is set withparams, the value of the option could end up being placed in a logfile or displayed on stdout.Setting the async directory using
ANSIBLE_ASYNC_DIRas an task/play environment key is deprecated and will be removed in Ansible 2.12. You can achieve the same result by settingansible_async_diras a variable like:- name: run task with custom async directory command: sleep 5 async: 10 vars: ansible_async_dir: /tmp/.ansible_async
Plugin writers who need a
FactCacheobject should be aware of two deprecations:- The
FactCacheclass has moved fromansible.plugins.cache.FactCachetoansible.vars.fact_cache.FactCache. This is because theFactCacheis not part of the cache plugin API and cache plugin authors should not be subclassing it.FactCacheis still available from its old location but will issue a deprecation warning when used from there. The old location will be removed in Ansible 2.12. - The
FactCache.update()method has been converted to follow the dict API. It now takes a dictionary as its sole argument and updates itself with the dictionary’s items. The previous API whereupdate()took a key and a value will now issue a deprecation warning and will be removed in 2.12. If you need the old behavior switch toFactCache.first_order_merge()instead.
- The
Supporting file-backed caching via self.cache is deprecated and will be removed in Ansible 2.12. If you maintain an inventory plugin, update it to use
self._cacheas a dictionary. For implementation details, see the developer guide on inventory plugins.Importing cache plugins directly is deprecated and will be removed in Ansible 2.12. Use the plugin_loader so direct options, environment variables, and other means of configuration can be reconciled using the config system rather than constants.
from ansible.plugins.loader import cache_loader cache = cache_loader.get('redis', **kwargs)
Modules¶
Major changes in popular modules are detailed here
The exec wrapper that runs PowerShell modules has been changed to set $ErrorActionPreference = "Stop" globally.
This may mean that custom modules can fail if they implicitly relied on this behavior. To get the old behavior back,
add $ErrorActionPreference = "Continue" to the top of the module. This change was made to restore the old behavior
of the EAP that was accidentally removed in a previous release and ensure that modules are more resilient to errors
that may occur in execution.
Modules removed¶
The following modules no longer exist:
- ec2_remote_facts
- azure
- cs_nic
- netscaler
- win_msi
Deprecation notices¶
The following modules will be removed in Ansible 2.12. Please update your playbooks accordingly.
foremanuse foreman-ansible-modules instead.katellouse foreman-ansible-modules instead.github_hooksuse github_webhook and github_webhook_facts instead.digital_oceanuse digital_ocean_droplet instead.gceuse gcp_compute_instance instead.gcspanneruse gcp_spanner_instance and gcp_spanner_database instead.gcdns_recorduse gcp_dns_resource_record_set instead.gcdns_zoneuse gcp_dns_managed_zone instead.gcp_forwarding_ruleuse gcp_compute_global_forwarding_rule or gcp_compute_forwarding_rule instead.gcp_healthcheckuse gcp_compute_health_check, gcp_compute_http_health_check, or gcp_compute_https_health_check instead.gcp_backend_serviceuse gcp_compute_backend_service instead.gcp_target_proxyuse gcp_compute_target_http_proxy instead.gcp_url_mapuse gcp_compute_url_map instead.panosuse the Palo Alto Networks Ansible Galaxy role instead.
Noteworthy module changes¶
Security Issue Setting
bind_pwwith theparamsoption for theldap_entryandldap_attrmodules has been disallowed. Ifbind_pwwas set withparams, the value could have ended up in a logfile or displayed on stdout. Setbind_pwdirectly, with the modules’ options instead.The
foremanandkatellomodules have been deprecated in favor of a set of modules that are broken out per entity with better idempotency in mind.The
foremanandkatellomodules replacement is officially part of the Foreman Community and supported there.The
tower_credentialmodule originally required thessh_key_datato be the path to a ssh_key_file. In order to work like Tower/AWX,ssh_key_datanow contains the content of the file. The previous behavior can be achieved withlookup('file', '/path/to/file').The
win_scheduled_taskmodule deprecated support for specifying a trigger repetition as a list and this format will be removed in Ansible 2.12. Instead specify the repetition as a dictionary value.The
win_featuremodule has removed the deprecatedrestart_neededreturn value, use the standardizedreboot_requiredvalue instead.The
win_packagemodule has removed the deprecatedrestart_requiredandexit_codereturn value, use the standardizedreboot_requiredandrcvalue instead.The
win_get_urlmodule has removed the deprecatedwin_get_urlreturn dictionary, contained values are returned directly.The
win_get_urlmodule has removed the deprecatedskip_certificate_validationoption, use the standardizedvalidate_certsoption instead.The
vmware_local_role_factsmodule now returns a list of dicts instead of a dict of dicts for role information.If
docker_networkordocker_volumewere called withdiff: yes,check_mode: yesordebug: yes, a return value calleddiffwas returned of typelist. To enable proper diff output, this was changed to typedict; the originallistis returned asdiff.differences.The
na_ontap_cluster_peermodule has replacedsource_intercluster_lifanddest_intercluster_lifstring options withsource_intercluster_lifsanddest_intercluster_lifslist optionsThe
modprobemodule now detects kernel builtins. Previously, attempting to remove (withstate: absent) a builtin kernel module succeeded without any error message becausemodprobedid not detect the module aspresent. Now,modprobewill fail if a kernel module is builtin andstate: absent(with an error message from the modprobe binary likemodprobe: ERROR: Module nfs is builtin.), and it will succeed without reporting changed ifstate: present. Any playbooks that are usingchanged_when: noto mask this quirk can safely remove that workaround. To get the previous behavior when applyingstate: absentto a builtin kernel module, usefailed_when: falseorignore_errors: truein your playbook.The
digital_oceanmodule has been deprecated in favor of modules that do not require external dependencies. This allows for more flexibility and better module support.The
docker_containermodule has deprecated the returned factdocker_container. The same value is available as the returned variablecontainer. The returned fact will be removed in Ansible 2.12.The
docker_networkmodule has deprecated the returned factdocker_container. The same value is available as the returned variablenetwork. The returned fact will be removed in Ansible 2.12.The
docker_volumemodule has deprecated the returned factdocker_container. The same value is available as the returned variablevolume. The returned fact will be removed in Ansible 2.12.The
docker_servicemodule was renamed to docker_compose.The renamed
docker_composemodule used to return one fact per service, named same as the service. A dictionary of these facts is returned as the regular return valueservices. The returned facts will be removed in Ansible 2.12.- The
docker_swarm_servicemodule no longer sets a defaults for the following options: user. Before, the default wasroot.update_delay. Before, the default was10.update_parallelism. Before, the default was1.
- The
vmware_vm_factsused to return dict of dict with virtual machine’s facts. Ansible 2.8 and onwards will return list of dict with virtual machine’s facts. Please see modulevmware_vm_factsdocumentation for example.vmware_guest_snapshotmodule used to returnresults. Since Ansible 2.8 and onwardsresultsis a reserved keyword, it is replaced bysnapshot_results. Please see modulevmware_guest_snapshotsdocumentation for example.The
panosmodules have been deprecated in favor of using the Palo Alto Networks Ansible Galaxy role. Contributions to the role can be made here.The
ipa_usermodule originally always sentpasswordto FreeIPA regardless of whether the password changed. Now the module only sendspasswordifupdate_passwordis set toalways, which is the default.The
win_psexechas deprecated the undocumentedextra_optsmodule option. This will be removed in Ansible 2.10.The
win_nssmmodule has deprecated the following options in favor of using thewin_servicemodule to configure the service after installing it withwin_nssm: *dependencies, usedependenciesofwin_serviceinstead *start_mode, usestart_modeofwin_serviceinstead *user, useusernameofwin_serviceinstead *password, usepasswordofwin_serviceinstead These options will be removed in Ansible 2.12.The
win_nssmmodule has also deprecated thestart,stop, andrestartvalues of thestatusoption. You should use thewin_servicemodule to control the running state of the service. This will be removed in Ansible 2.12.The
statusmodule option forwin_nssmhas changed its default value topresent. Before, the default wasstart. Consequently, the service is no longer started by default after creation withwin_nssm, and you should use thewin_servicemodule to start it if needed.The
app_parametersmodule option forwin_nssmhas been deprecated; useargumentinstead. This will be removed in Ansible 2.12.The
app_parameters_free_formmodule option forwin_nssmhas been aliased to the newargumentsoption.The
win_dscmodule will now validate the input options for a DSC resource. In previous versions invalid options would be ignored but are now not.The
openssl_pkcs12module will now regenerate the pkcs12 file if there are differences between the file on disk and the parameters passed to the module.
Plugins¶
Ansible no longer defaults to the
paramikoconnection plugin when using macOS as the control node. Ansible will now use thesshconnection plugin by default on a macOS control node. Sincesshsupports connection persistence between tasks and playbook runs, it performs better thanparamiko. If you are using password authentication, you will need to installsshpasswhen using thesshconnection plugin. Or you can explicitly set the connection type toparamikoto maintain the pre-2.8 behavior on macOS.Connection plugins have been standardized to allow use of
ansible_<conn-type>_userandansible_<conn-type>_passwordvariables. Variables such asansible_<conn-type>_passandansible_<conn-type>_usernameare treated with lower priority than the standardized names and may be deprecated in the future. In general, theansible_userandansible_passwordvars should be used unless there is a reason to use the connection-specific variables.The
powershellshell plugin now usesasync_dirto define the async path for the results file and the default has changed to%USERPROFILE%\.ansible_async. To control this path now, either set theansible_async_dirvariable or theasync_dirvalue in thepowershellsection of the config ini.Order of enabled inventory plugins (INVENTORY_ENABLED) has been updated, auto is now before yaml and ini.
The private
_optionsattribute has been removed from theCallbackBaseclass of callback plugins. If you have a third-party callback plugin which needs to access the command line arguments, use code like the following instead of trying to useself._options:from ansible import context [...] tags = context.CLIARGS['tags']
context.CLIARGSis a read-only dictionary so normal dictionary retrieval methods likeCLIARGS.get('tags')andCLIARGS['tags']work as expected but you won’t be able to modify the cli arguments at all.Play recap now counts
ignoredandrescuedtasks as well asok,changed,unreachable,failedandskippedtasks, thanks to two additional stat counters in thedefaultcallback plugin. Tasks that fail and haveignore_errors: yesset are listed asignored. Tasks that fail and then execute a rescue section are listed asrescued. Note thatrescuedtasks are no longer counted asfailedas in Ansible 2.7 (and earlier).osx_saycallback plugin was renamed into say.Inventory plugins now support caching via cache plugins. To start using a cache plugin with your inventory see the section on caching in the inventory guide. To port a custom cache plugin to be compatible with inventory see developer guide on cache plugins.
Porting custom scripts¶
Display class¶
As of Ansible 2.8, the Display class is now a “singleton”. Instead of using __main__.display each file should
import and instantiate ansible.utils.display.Display on its own.
OLD In Ansible 2.7 (and earlier) the following was used to access the display object:
try:
from __main__ import display
except ImportError:
from ansible.utils.display import Display
display = Display()
NEW In Ansible 2.8 the following should be used:
from ansible.utils.display import Display
display = Display()
Networking¶
- The
eos_config,ios_config, andnxos_configmodules have removed the deprecatedsaveandforceparameters, use thesave_whenparameter to replicate their functionality. - The
nxos_vrf_afmodule has removed thesafiparameter. This parameter was deprecated in Ansible 2.4 and has had no impact on the module since then.