Ansible 2.9 Porting Guide

This section discusses the behavioral changes between Ansible 2.8 and Ansible 2.9.

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.9 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.



  • hash_behaviour now affects inventory sources. If you have it set to merge, the data you get from inventory might change and you will have to update playbooks accordingly. If you’re using the default setting (overwrite), you will see no changes. Inventory was ignoring this setting.


Ansible 2.9 handles “unsafe” data more robustly, ensuring that data marked “unsafe” is not templated. In previous versions, Ansible recursively marked all data returned by the direct use of lookup() as “unsafe”, but only marked structured data returned by indirect lookups using with_X style loops as “unsafe” if the returned elements were strings. Ansible 2.9 treats these two approaches consistently.

As a result, if you use with_dict to return keys with templatable values, your templates may no longer work as expected in Ansible 2.9.

To allow the old behavior, switch from using with_X to using loop with a filter as described at Migrating from with_X to loop.

Command Line

  • The location of the Galaxy token file has changed from ~/.ansible_galaxy to ~/.ansible/galaxy_token. You can configure both path and file name with the GALAXY_TOKEN_PATH config.


No notable changes

Collection loader changes

The way to import a PowerShell or C# module util from a collection has changed in the Ansible 2.9 release. In Ansible 2.8 a util was imported with the following syntax:

#AnsibleRequires -CSharpUtil AnsibleCollections.namespace_name.collection_name.util_filename
#AnsibleRequires -PowerShell AnsibleCollections.namespace_name.collection_name.util_filename

In Ansible 2.9 this was changed to:

#AnsibleRequires -CSharpUtil ansible_collections.namespace_name.collection_name.plugins.module_utils.util_filename
#AnsibleRequires -PowerShell ansible_collections.namespace_name.collection_name.plugins.module_utils.util_filename

The change in the collection import name also requires any C# util namespaces to be updated with the newer name format. This is more verbose but is designed to make sure we avoid plugin name conflicts across separate plugin types and to standardise how imports work in PowerShell with how Python modules work.


  • The win_get_url and win_uri module now sends requests with a default User-Agent of ansible-httpget. This can be changed by using the http_agent key.

  • The apt module now honors update_cache=false while installing its own dependency and skips the cache update. Explicitly setting update_cache=true or omitting the param update_cache will result in a cache update while installing its own dependency.

  • Version 2.9.12 of Ansible changed the default mode of file-based tasks to 0o600 & ~umask when the user did not specify a mode parameter on file-based tasks. This was in response to a CVE report which we have reconsidered. As a result, the mode change has been reverted in 2.9.13, and mode will now default to 0o666 & ~umask as in previous versions of Ansible.

  • If you changed any tasks to specify less restrictive permissions while using 2.9.12, those changes will be unnecessary (but will do no harm) in 2.9.13.

  • To avoid the issue raised in CVE-2020-1736, specify a mode parameter in all file-based tasks that accept it.

  • dnf and yum - As of version 2.9.13, the dnf module (and yum action when it uses dnf) now correctly validates GPG signatures of packages (CVE-2020-14365). If you see an error such as Failed to validate GPG signature for [package name], please ensure that you have imported the correct GPG key for the DNF repository and/or package you are using. One way to do this is with the rpm_key module. Although we discourage it, in some cases it may be necessary to disable the GPG check. This can be done by explicitly adding disable_gpg_check: yes in your dnf or yum task.

Renaming from _facts to _info

Ansible 2.9 renamed a lot of modules from <something>_facts to <something>_info, because the modules do not return Ansible facts. Ansible facts relate to a specific host. For example, the configuration of a network interface, the operating system on a unix server, and the list of packages installed on a Windows box are all Ansible facts. The renamed modules return values that are not unique to the host. For example, account information or region data for a cloud provider. Renaming these modules should provide more clarity about the types of return values each set of modules offers.

Writing modules

  • Module and module_utils files can now use relative imports to include other module_utils files. This is useful for shortening long import lines, especially in collections.

    Example of using a relative import in collections:

    # File: ansible_collections/my_namespace/my_collection/plugins/modules/
    # Old way to use an absolute import to import module_utils from the collection:
    from ansible_collections.my_namespace.my_collection.plugins.module_utils import my_util
    # New way using a relative import:
    from ..module_utils import my_util

    Modules and module_utils shipped with Ansible can use relative imports as well but the savings are smaller:

    # File: ansible/modules/system/
    # Old way to use an absolute import to import module_utils from core:
    from ansible.module_utils.basic import AnsibleModule
    # New way using a relative import:
    from ...module_utils.basic import AnsibleModule

    Each single dot (.) represents one level of the tree (equivalent to ../ in filesystem relative links).

    See also

    The Python Relative Import Docs go into more detail of how to write relative imports.

Modules removed

The following modules no longer exist:

Deprecation notices

The following modules will be removed in Ansible 2.13. Please update update your playbooks accordingly.

The following functionality will be removed in Ansible 2.12. Please update update your playbooks accordingly.

The following functionality will be removed in Ansible 2.13. Please update update your playbooks accordingly.

For the following modules, the PyOpenSSL-based backend pyopenssl has been deprecated and will be removed in Ansible 2.13:

Renamed modules

The following modules have been renamed. The old name is deprecated and will be removed in Ansible 2.13. Please update update your playbooks accordingly.

Noteworthy module changes

  • vmware_cluster was refactored for easier maintenance/bugfixes. Use the three new, specialized modules to configure clusters. Configure DRS with vmware_cluster_drs, HA with vmware_cluster_ha and vSAN with vmware_cluster_vsan.

  • vmware_dvswitch accepts folder parameter to place dvswitch in user defined folder. This option makes datacenter as an optional parameter.

  • vmware_datastore_cluster accepts folder parameter to place datastore cluster in user defined folder. This option makes datacenter as an optional parameter.

  • mysql_db returns new db_list parameter in addition to db parameter. This db_list parameter refers to list of database names. db parameter will be deprecated in version 2.13.

  • snow_record and snow_record_find now takes environment variables for instance, username and password parameters. This change marks these parameters as optional.

  • The deprecated force option in win_firewall_rule has been removed.

  • openssl_certificate’s ownca provider creates authority key identifiers if not explicitly disabled with ownca_create_authority_key_identifier: no. This is only the case for the cryptography backend, which is selected by default if the cryptography library is available.

  • openssl_certificate’s ownca and selfsigned providers create subject key identifiers if not explicitly disabled with ownca_create_subject_key_identifier: never_create resp. selfsigned_create_subject_key_identifier: never_create. If a subject key identifier is provided by the CSR, it is taken; if not, it is created from the public key. This is only the case for the cryptography backend, which is selected by default if the cryptography library is available.

  • openssh_keypair now applies the same file permissions and ownership to both public and private keys (both get the same mode, owner, group, and so on). If you need to change permissions / ownership on one key, use the file to modify it after it is created.


Removed Lookup Plugins

  • redis_kv use redis instead.

Porting custom scripts

No notable changes


Network resource modules

Ansible 2.9 introduced the first batch of network resource modules. Sections of a network device’s configuration can be thought of as a resource provided by that device. Network resource modules are intentionally scoped to configure a single resource and you can combine them as building blocks to configure complex network services. The older modules are deprecated in Ansible 2.9 and will be removed in Ansible 2.13. You should scan the list of deprecated modules above and replace them with the new network resource modules in your playbooks. See Ansible Network Features in 2.9 for details.

Improved gather_facts support for network devices

In Ansible 2.9, the gather_facts keyword now supports gathering network device facts in standardized key/value pairs. You can feed these network facts into further tasks to manage the network device. You can also use the new gather_network_resources parameter with the network *_facts modules (such as eos_facts) to return just a subset of the device configuration. See Gathering facts from network devices for an example.

Top-level connection arguments removed in 2.9

Top-level connection arguments like username, host, and password are removed in version 2.9.

OLD In Ansible < 2.4

- name: example of using top-level options for connection properties
    commands: show version
    host: "{{ inventory_hostname }}"
    username: cisco
    password: cisco
    authorize: yes
    auth_pass: cisco

Change your playbooks to the connection types network_cli and netconf using standard Ansible connection properties, and setting those properties in inventory by group. As you update your playbooks and inventory files, you can easily make the change to become for privilege escalation (on platforms that support it). For more information, see the using become with network modules guide and the platform documentation.