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
- Python Interpreter Discovery
- Command Line
- Deprecated
- Modules
- Plugins
- Porting custom scripts
- Networking
Playbook
Distribution Facts
The information returned for the ansibledistribution*
group of facts may have changedslightly. 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 thedifference. However, other facts like ansible_distribution_release
andansible_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. Whenusing 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 usea 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
True
andFalse
respectively ifteardown: 'true'
- In Ansible 2.7 and earlier, both conditions evaluated as
False
ifteardown: 'false'
- In Ansible 2.8 and later, you have the option of disabling conditional bare variables, so
when: teardown
always evaluates asTrue
andwhen: not teardown
always evaluates asFalse
whenteardown
is 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_interpolated
evaluated to the value ofbare_variable
, in this case,False
. If the variablebare_variable
is undefined, the conditional fails. - In Ansible 2.8 and later, with bare variables disabled, Ansible evaluates
double_interpolated
as 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 toobey play tags. Previous to 2.8, the “Gathering Facts” task would ignore playtags 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 inheritsthe tag of webserver
instead of always
.
If no play level tags are set, the “Gathering Facts” task willbe given a tag of always
and will effectively match priorbehavior.
You can achieve similar results to the pre-2.8 behavior, byusing 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 thesetting for ansible_python_interpreter
. If you ran Ansible against a systemthat installed Python with a different name or a different path, your playbookswould fail with /usr/bin/python: bad interpreter: No such file or directory
unless you either set ansible_python_interpreter
to the correct value forthat system or added a Python interpreter and any necessary dependencies atusr/bin/python.
Starting in Ansible 2.8, Ansible searches for the correct path and executablename for Python on each target system, first in a lookup table of defaultPython interpreters for common distros, then in an ordered fallback list ofpossible Python interpreter names/paths.
It’s risky to rely on a Python interpreter set from the fallback list, becausethe interpreter may change on future runs. If an interpreter fromhigher in the fallback list gets installed (for example, as a side-effect ofinstalling other packages), your original interpreter and its dependencies willno longer be used. For this reason, Ansible warns you when it uses a Pythoninterpreter discovered from the fallback list. If you see this warning, thebest solution is to explicitly set ansible_python_interpreter
to the pathof the correct interpreter for those target systems.
You can still set ansible_python_interpreter
to a specific path at anyvariable level (as a host variable, in vars files, in playbooks, etc.).If you prefer to use the Python interpreter discovery behavior, useone of the four new values for ansible_python_interpreter
introduced inAnsible 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. Warnswhen using the fallback list.If a Python interpreter is discovered, and/usr/bin/python is present,Ansible uses /usr/bin/python andprints a deprecation warning about futuredefault behavior. Warns when using thefallback list. |
auto_legacy_silent | Behaves like auto_legacy but suppressesthe deprecation and fallback-list warnings. |
auto_silent | Behaves like auto but suppresses thefallback-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 ifpresent and falls back to the discovered Python when it is not present. auto
will alwaysuse the discovered Python, regardless of whether /usr/bin/python exists. Theauto_legacy
setting provides compatibility with previous versions of Ansible that alwaysdefaulted 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 Pythoninterpreter (for example, Ubuntu 16.04+, RHEL8, Fedora 23+), you have twooptions:
- 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 bemodified 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
- Setting the async directory using
ANSIBLE_ASYNC_DIR
as an task/play environment key is deprecated and will beremoved in Ansible 2.12. You can achieve the same result by settingansible_async_dir
as 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
FactCache
object should be aware of two deprecations:- The
FactCache
class has moved fromansible.plugins.cache.FactCache
toansible.vars.fact_cache.FactCache
. This is because theFactCache
is not part of thecache plugin API and cache plugin authors should not be subclassing it.FactCache
is stillavailable from its old location but will issue a deprecation warning when used from there. Theold location will be removed in Ansible 2.12. - The
FactCache.update()
method has been converted to follow the dict API. It now takes adictionary as its sole argument and updates itself with the dictionary’s items. The previousAPI whereupdate()
took a key and a value will now issue a deprecation warning and will beremoved 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 willbe removed in Ansible 2.12. If you maintain an inventory plugin, update it to use
self._cache
as a dictionary. For implementation details, seethe developer guide on inventory plugins.Importing cache plugins directly is deprecated and will be removed in Ansible 2.12. Use the plugin_loaderso direct options, environment variables, and other means of configuration can be reconciled using the configsystem 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 behaviorof the EAP that was accidentally removed in a previous release and ensure that modules are more resilient to errorsthat 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.
foreman
use foreman-ansible-modules instead.katello
use foreman-ansible-modules instead.github_hooks
use github_webhook and github_webhook_facts instead.digital_ocean
use digital_ocean_droplet instead.gce
use gcp_compute_instance instead.gcspanner
use gcp_spanner_instance and gcp_spanner_database instead.gcdns_record
use gcp_dns_resource_record_set instead.gcdns_zone
use gcp_dns_managed_zone instead.gcp_forwarding_rule
use gcp_compute_global_forwarding_rule or gcp_compute_forwarding_rule instead.gcp_healthcheck
use gcp_compute_health_check, gcp_compute_http_health_check, or gcp_compute_https_health_check instead.gcp_backend_service
use gcp_compute_backend_service instead.gcp_target_proxy
use gcp_compute_target_http_proxy instead.gcp_url_map
use gcp_compute_url_map instead.panos
use the Palo Alto Networks Ansible Galaxy role instead.
Noteworthy module changes
- The
foreman
andkatello
modules have been deprecated in favor of a set of modules that are broken out per entity with better idempotency in mind. - The
foreman
andkatello
modules replacement is officially part of the Foreman Community and supported there. - The
tower_credential
module originally required thessh_key_data
to be the path to a ssh_key_file.In order to work like Tower/AWX,ssh_key_data
now contains the content of the file.The previous behavior can be achieved withlookup('file', '/path/to/file')
. - The
win_scheduled_task
module deprecated support for specifying a trigger repetition as a list and this formatwill be removed in Ansible 2.12. Instead specify the repetition as a dictionary value. - The
win_feature
module has removed the deprecatedrestart_needed
return value, use the standardizedreboot_required
value instead. - The
win_package
module has removed the deprecatedrestart_required
andexit_code
return value, use thestandardizedreboot_required
andrc
value instead. - The
win_get_url
module has removed the deprecatedwin_get_url
return dictionary, contained values arereturned directly. - The
win_get_url
module has removed the deprecatedskip_certificate_validation
option, use the standardizedvalidate_certs
option instead. - The
vmware_local_role_facts
module now returns a list of dicts instead of a dict of dicts for role information. - If
docker_network
ordocker_volume
were called withdiff: yes
,check_mode: yes
ordebug: yes
,a return value calleddiff
was returned of typelist
. To enable proper diff output, this was changed totypedict
; the originallist
is returned asdiff.differences
. - The
na_ontap_cluster_peer
module has replacedsource_intercluster_lif
anddest_intercluster_lif
string options withsource_intercluster_lifs
anddest_intercluster_lifs
list options - The
modprobe
module now detects kernel builtins. Previously, attempting to remove (withstate: absent
)a builtin kernel module succeeded without any error message becausemodprobe
did not detect the module aspresent
. Now,modprobe
will fail if a kernel module is builtin andstate: absent
(with an error messagefrom the modprobe binary likemodprobe: ERROR: Module nfs is builtin.
), and it will succeed without reportingchanged ifstate: present
. Any playbooks that are usingchanged_when: no
to mask this quirk can safelyremove that workaround. To get the previous behavior when applyingstate: absent
to a builtin kernel module,usefailed_when: false
orignore_errors: true
in your playbook. - The
digital_ocean
module has been deprecated in favor of modules that do not require external dependencies.This allows for more flexibility and better module support. - The
docker_container
module has deprecated the returned factdocker_container
. The same value isavailable as the returned variablecontainer
. The returned fact will be removed in Ansible 2.12. - The
docker_network
module has deprecated the returned factdocker_container
. The same value isavailable as the returned variablenetwork
. The returned fact will be removed in Ansible 2.12. - The
docker_volume
module has deprecated the returned factdocker_container
. The same value isavailable as the returned variablevolume
. The returned fact will be removed in Ansible 2.12. - The
docker_service
module was renamed to docker_compose. - The renamed
docker_compose
module used to return one fact per service, named same as the service. A dictionaryof these facts is returned as the regular return valueservices
. The returned facts will be removed inAnsible 2.12. - The
docker_swarm_service
module 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_facts
used 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_facts
documentation for example.vmware_guest_snapshot
module used to returnresults
. Since Ansible 2.8 and onwardsresults
is a reserved keyword, it is replaced bysnapshot_results
.Please see modulevmware_guest_snapshots
documentation for example.- The
panos
modules have been deprecated in favor of using the Palo Alto Networks Ansible Galaxy role. Contributions to the role can be madehere. - The
ipa_user
module originally always sentpassword
to FreeIPA regardless of whether the password changed. Now the module only sendspassword
ifupdate_password
is set toalways
, which is the default. - The
win_psexec
has deprecated the undocumentedextra_opts
module option. This will be removed in Ansible 2.10. - The
win_nssm
module has deprecated the following options in favor of using thewin_service
module to configure the service after installing it withwin_nssm
:dependencies
, usedependencies
ofwin_service
insteadstart_mode
, usestart_mode
ofwin_service
insteaduser
, useusername
ofwin_service
insteadpassword
, usepassword
ofwin_service
insteadThese options will be removed in Ansible 2.12. - The
win_nssm
module has also deprecated thestart
,stop
, andrestart
values of thestatus
option.You should use thewin_service
module to control the running state of the service. This will be removed in Ansible 2.12. - The
status
module option forwin_nssm
has 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 usethewin_service
module to start it if needed. - The
app_parameters
module option forwin_nssm
has been deprecated; useargument
instead. This will be removed in Ansible 2.12. - The
app_parameters_free_form
module option forwin_nssm
has been aliased to the newarguments
option. - The
win_dsc
module will now validate the input options for a DSC resource. In previous versions invalid optionswould be ignored but are now not. - The
openssl_pkcs12
module 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
paramiko
connection plugin when using macOS as the control node. Ansible will now use thessh
connection plugin by default on a macOS control node. Sincessh
supports connection persistence between tasks and playbook runs, it performs better thanparamiko
. If you are using password authentication, you will need to installsshpass
when using thessh
connection plugin. Or you can explicitly set the connection type toparamiko
to maintain the pre-2.8 behavior on macOS.Connection plugins have been standardized to allow use of
ansible<conn-type>_user
andansible
<conn-type>password
variables. Variables such asansible
<conn-type>pass
andansible
<conn-type>_username
are treatedwith lower priority than the standardized names and may be deprecated in thefuture. In general, theansible_user
andansible_password
vars shouldbe used unless there is a reason to use the connection-specific variables.The
powershell
shell plugin now usesasync_dir
to define the async path for the results file and the defaulthas changed to%USERPROFILE%.ansible_async
. To control this path now, either set theansible_async_dir
variable or theasync_dir
value in thepowershell
section of the config ini.Order of enabled inventory plugins (INVENTORY_ENABLED) has been updated, auto is now before yaml and ini.
The private
_options
attribute has been removed from theCallbackBase
class of callbackplugins. 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.CLIARGS
is a read-only dictionary so normal dictionary retrieval methods likeCLIARGS.get('tags')
and CLIARGS['tags']
work as expected but you won’t be able to modifythe cli arguments at all.
Play recap now counts
ignored
andrescued
tasks as well asok
,changed
,unreachable
,failed
andskipped
tasks, thanks to two additional stat counters in thedefault
callback plugin. Tasks that fail and haveignore_errors: yes
set are listed asignored
. Tasks that fail and then execute a rescue section are listed asrescued
. Note thatrescued
tasks are no longer counted asfailed
as in Ansible 2.7 (and earlier).osx_say
callback 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 shouldimport 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_config
modules have removed the deprecatedsave
andforce
parameters, use thesave_when
parameter to replicate theirfunctionality. - The
nxos_vrf_af
module has removed thesafi
parameter. This parameter was deprecatedin Ansible 2.4 and has had no impact on the module since then.