1.. _playbooks_filters: 2 3******************************** 4Using filters to manipulate data 5******************************** 6 7Filters let you transform JSON data into YAML data, split a URL to extract the hostname, get the SHA1 hash of a string, add or multiply integers, and much more. You can use the Ansible-specific filters documented here to manipulate your data, or use any of the standard filters shipped with Jinja2 - see the list of :ref:`built-in filters <jinja2:builtin-filters>` in the official Jinja2 template documentation. You can also use :ref:`Python methods <jinja2:python-methods>` to transform data. You can :ref:`create custom Ansible filters as plugins <developing_filter_plugins>`, though we generally welcome new filters into the ansible-base repo so everyone can use them. 8 9Because templating happens on the Ansible controller, **not** on the target host, filters execute on the controller and transform data locally. 10 11.. contents:: 12 :local: 13 14Handling undefined variables 15============================ 16 17Filters can help you manage missing or undefined variables by providing defaults or making some variables optional. If you configure Ansible to ignore most undefined variables, you can mark some variables as requiring values with the ``mandatory`` filter. 18 19.. _defaulting_undefined_variables: 20 21Providing default values 22------------------------ 23 24You can provide default values for variables directly in your templates using the Jinja2 'default' filter. This is often a better approach than failing if a variable is not defined:: 25 26 {{ some_variable | default(5) }} 27 28In the above example, if the variable 'some_variable' is not defined, Ansible uses the default value 5, rather than raising an "undefined variable" error and failing. If you are working within a role, you can also add a ``defaults/main.yml`` to define the default values for variables in your role. 29 30Beginning 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 31a default with a value in a nested data structure (in other words, :code:`{{ foo.bar.baz | default('DEFAULT') }}`) when you do not know if the intermediate values are defined. 32 33If you want to use the default value when variables evaluate to false or an empty string you have to set the second parameter to ``true``:: 34 35 {{ lookup('env', 'MY_USER') | default('admin', true) }} 36 37.. _omitting_undefined_variables: 38 39Making variables optional 40------------------------- 41 42By default Ansible requires values for all variables in a templated expression. However, you can make specific variables optional. For example, you might want to use a system default for some items and control the value for others. To make a variable optional, set the default value to the special variable ``omit``:: 43 44 - name: Touch files with an optional mode 45 ansible.builtin.file: 46 dest: "{{ item.path }}" 47 state: touch 48 mode: "{{ item.mode | default(omit) }}" 49 loop: 50 - path: /tmp/foo 51 - path: /tmp/bar 52 - path: /tmp/baz 53 mode: "0444" 54 55In this example, the default mode for the files ``/tmp/foo`` and ``/tmp/bar`` is determined by the umask of the system. Ansible does not send a value for ``mode``. Only the third file, ``/tmp/baz``, receives the `mode=0444` option. 56 57.. note:: If you are "chaining" additional filters after the ``default(omit)`` filter, you should instead do something like this: 58 ``"{{ foo | default(None) | some_filter or omit }}"``. In this example, the default ``None`` (Python null) value will cause the later filters to fail, which will trigger the ``or omit`` portion of the logic. Using ``omit`` in this manner is very specific to the later filters you are chaining though, so be prepared for some trial and error if you do this. 59 60.. _forcing_variables_to_be_defined: 61 62Defining mandatory values 63------------------------- 64 65If you configure Ansible to ignore undefined variables, you may want to define some values as mandatory. By default, Ansible fails if a variable in your playbook or command is undefined. You can configure Ansible to allow undefined variables by setting :ref:`DEFAULT_UNDEFINED_VAR_BEHAVIOR` to ``false``. In that case, you may want to require some variables to be defined. You can do this with:: 66 67 {{ variable | mandatory }} 68 69The variable value will be used as is, but the template evaluation will raise an error if it is undefined. 70 71Defining different values for true/false/null (ternary) 72======================================================= 73 74You can create a test, then define one value to use when the test returns true and another when the test returns false (new in version 1.9):: 75 76 {{ (status == 'needs_restart') | ternary('restart', 'continue') }} 77 78In addition, you can define a one value to use on true, one value on false and a third value on null (new in version 2.8):: 79 80 {{ enabled | ternary('no shutdown', 'shutdown', omit) }} 81 82Managing data types 83=================== 84 85You might need to know, change, or set the data type on a variable. For example, a registered variable might contain a dictionary when your next task needs a list, or a user :ref:`prompt <playbooks_prompts>` might return a string when your playbook needs a boolean value. Use the ``type_debug``, ``dict2items``, and ``items2dict`` filters to manage data types. You can also use the data type itself to cast a value as a specific data type. 86 87Discovering the data type 88------------------------- 89 90.. versionadded:: 2.3 91 92If you are unsure of the underlying Python type of a variable, you can use the ``type_debug`` filter to display it. This is useful in debugging when you need a particular type of variable:: 93 94 {{ myvar | type_debug }} 95 96 97.. _dict_filter: 98 99Transforming dictionaries into lists 100------------------------------------ 101 102.. versionadded:: 2.6 103 104 105Use the ``dict2items`` filter to transform a dictionary into a list of items suitable for :ref:`looping <playbooks_loops>`:: 106 107 {{ dict | dict2items }} 108 109Dictionary data (before applying the ``dict2items`` filter):: 110 111 tags: 112 Application: payment 113 Environment: dev 114 115List data (after applying the ``dict2items`` filter):: 116 117 - key: Application 118 value: payment 119 - key: Environment 120 value: dev 121 122.. versionadded:: 2.8 123 124The ``dict2items`` filter is the reverse of the ``items2dict`` filter. 125 126If you want to configure the names of the keys, the ``dict2items`` filter accepts 2 keyword arguments. Pass the ``key_name`` and ``value_name`` arguments to configure the names of the keys in the list output:: 127 128 {{ files | dict2items(key_name='file', value_name='path') }} 129 130Dictionary data (before applying the ``dict2items`` filter):: 131 132 files: 133 users: /etc/passwd 134 groups: /etc/group 135 136List data (after applying the ``dict2items`` filter):: 137 138 - file: users 139 path: /etc/passwd 140 - file: groups 141 path: /etc/group 142 143 144Transforming lists into dictionaries 145------------------------------------ 146 147.. versionadded:: 2.7 148 149Use the ``items2dict`` filter to transform a list into a dictionary, mapping the content into ``key: value`` pairs:: 150 151 {{ tags | items2dict }} 152 153List data (before applying the ``items2dict`` filter):: 154 155 tags: 156 - key: Application 157 value: payment 158 - key: Environment 159 value: dev 160 161Dictionary data (after applying the ``items2dict`` filter):: 162 163 Application: payment 164 Environment: dev 165 166The ``items2dict`` filter is the reverse of the ``dict2items`` filter. 167 168Not all lists use ``key`` to designate keys and ``value`` to designate values. For example:: 169 170 fruits: 171 - fruit: apple 172 color: red 173 - fruit: pear 174 color: yellow 175 - fruit: grapefruit 176 color: yellow 177 178In this example, you must pass the ``key_name`` and ``value_name`` arguments to configure the transformation. For example:: 179 180 {{ tags | items2dict(key_name='fruit', value_name='color') }} 181 182If you do not pass these arguments, or do not pass the correct values for your list, you will see ``KeyError: key`` or ``KeyError: my_typo``. 183 184Forcing the data type 185--------------------- 186 187You can cast values as certain types. For example, if you expect the input "True" from a :ref:`vars_prompt <playbooks_prompts>` and you want Ansible to recognize it as a boolean value instead of a string:: 188 189 - debug: 190 msg: test 191 when: some_string_value | bool 192 193If you want to perform a mathematical comparison on a fact and you want Ansible to recognize it as an integer instead of a string:: 194 195 - shell: echo "only on Red Hat 6, derivatives, and later" 196 when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release'] | int >= 6 197 198 199.. versionadded:: 1.6 200 201.. _filters_for_formatting_data: 202 203Formatting data: YAML and JSON 204============================== 205 206You can switch a data structure in a template from or to JSON or YAML format, with options for formatting, indenting, and loading data. The basic filters are occasionally useful for debugging:: 207 208 {{ some_variable | to_json }} 209 {{ some_variable | to_yaml }} 210 211For human readable output, you can use:: 212 213 {{ some_variable | to_nice_json }} 214 {{ some_variable | to_nice_yaml }} 215 216You can change the indentation of either format:: 217 218 {{ some_variable | to_nice_json(indent=2) }} 219 {{ some_variable | to_nice_yaml(indent=8) }} 220 221The ``to_yaml`` and ``to_nice_yaml`` filters use the `PyYAML library`_ which has a default 80 symbol string length limit. That causes unexpected line break after 80th symbol (if there is a space after 80th symbol) 222To avoid such behavior and generate long lines, use the ``width`` option. You must use a hardcoded number to define the width, instead of a construction like ``float("inf")``, because the filter does not support proxying Python functions. For example:: 223 224 {{ some_variable | to_yaml(indent=8, width=1337) }} 225 {{ some_variable | to_nice_yaml(indent=8, width=1337) }} 226 227The filter does support passing through other YAML parameters. For a full list, see the `PyYAML documentation`_. 228 229If you are reading in some already formatted data:: 230 231 {{ some_variable | from_json }} 232 {{ some_variable | from_yaml }} 233 234for example:: 235 236 tasks: 237 - name: Register JSON output as a variable 238 ansible.builtin.shell: cat /some/path/to/file.json 239 register: result 240 241 - name: Set a variable 242 ansible.builtin.set_fact: 243 myvar: "{{ result.stdout | from_json }}" 244 245 246Filter `to_json` and Unicode support 247------------------------------------ 248 249By default `to_json` and `to_nice_json` will convert data received to ASCII, so:: 250 251 {{ 'München'| to_json }} 252 253will return:: 254 255 'M\u00fcnchen' 256 257To keep Unicode characters, pass the parameter `ensure_ascii=False` to the filter:: 258 259 {{ 'München'| to_json(ensure_ascii=False) }} 260 261 'München' 262 263.. versionadded:: 2.7 264 265To parse multi-document YAML strings, the ``from_yaml_all`` filter is provided. 266The ``from_yaml_all`` filter will return a generator of parsed YAML documents. 267 268for example:: 269 270 tasks: 271 - name: Register a file content as a variable 272 ansible.builtin.shell: cat /some/path/to/multidoc-file.yaml 273 register: result 274 275 - name: Print the transformed variable 276 ansible.builtin.debug: 277 msg: '{{ item }}' 278 loop: '{{ result.stdout | from_yaml_all | list }}' 279 280Combining and selecting data 281============================ 282 283You can combine data from multiple sources and types, and select values from large data structures, giving you precise control over complex data. 284 285.. _zip_filter: 286 287Combining items from multiple lists: zip and zip_longest 288-------------------------------------------------------- 289 290.. versionadded:: 2.3 291 292To get a list combining the elements of other lists use ``zip``:: 293 294 - name: Give me list combo of two lists 295 ansible.builtin.debug: 296 msg: "{{ [1,2,3,4,5] | zip(['a','b','c','d','e','f']) | list }}" 297 298 - name: Give me shortest combo of two lists 299 ansible.builtin.debug: 300 msg: "{{ [1,2,3] | zip(['a','b','c','d','e','f']) | list }}" 301 302To always exhaust all lists use ``zip_longest``:: 303 304 - name: Give me longest combo of three lists , fill with X 305 ansible.builtin.debug: 306 msg: "{{ [1,2,3] | zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X') | list }}" 307 308Similarly to the output of the ``items2dict`` filter mentioned above, these filters can be used to construct a ``dict``:: 309 310 {{ dict(keys_list | zip(values_list)) }} 311 312List data (before applying the ``zip`` filter):: 313 314 keys_list: 315 - one 316 - two 317 values_list: 318 - apple 319 - orange 320 321Dictonary data (after applying the ``zip`` filter):: 322 323 one: apple 324 two: orange 325 326Combining objects and subelements 327--------------------------------- 328 329.. versionadded:: 2.7 330 331The ``subelements`` filter produces a product of an object and the subelement values of that object, similar to the ``subelements`` lookup. This lets you specify individual subelements to use in a template. For example, this expression:: 332 333 {{ users | subelements('groups', skip_missing=True) }} 334 335Data before applying the ``subelements`` filter:: 336 337 users: 338 - name: alice 339 authorized: 340 - /tmp/alice/onekey.pub 341 - /tmp/alice/twokey.pub 342 groups: 343 - wheel 344 - docker 345 - name: bob 346 authorized: 347 - /tmp/bob/id_rsa.pub 348 groups: 349 - docker 350 351Data after applying the ``subelements`` filter:: 352 353 - 354 - name: alice 355 groups: 356 - wheel 357 - docker 358 authorized: 359 - /tmp/alice/onekey.pub 360 - /tmp/alice/twokey.pub 361 - wheel 362 - 363 - name: alice 364 groups: 365 - wheel 366 - docker 367 authorized: 368 - /tmp/alice/onekey.pub 369 - /tmp/alice/twokey.pub 370 - docker 371 - 372 - name: bob 373 authorized: 374 - /tmp/bob/id_rsa.pub 375 groups: 376 - docker 377 - docker 378 379You can use the transformed data with ``loop`` to iterate over the same subelement for multiple objects:: 380 381 - name: Set authorized ssh key, extracting just that data from 'users' 382 ansible.posix.authorized_key: 383 user: "{{ item.0.name }}" 384 key: "{{ lookup('file', item.1) }}" 385 loop: "{{ users | subelements('authorized') }}" 386 387.. _combine_filter: 388 389Combining hashes/dictionaries 390----------------------------- 391 392.. versionadded:: 2.0 393 394The ``combine`` filter allows hashes to be merged. For example, the following would override keys in one hash:: 395 396 {{ {'a':1, 'b':2} | combine({'b':3}) }} 397 398The resulting hash would be:: 399 400 {'a':1, 'b':3} 401 402The filter can also take multiple arguments to merge:: 403 404 {{ a | combine(b, c, d) }} 405 {{ [a, b, c, d] | combine }} 406 407In this case, keys in ``d`` would override those in ``c``, which would override those in ``b``, and so on. 408 409The filter also accepts two optional parameters: ``recursive`` and ``list_merge``. 410 411recursive 412 Is a boolean, default to ``False``. 413 Should the ``combine`` recursively merge nested hashes. 414 Note: It does **not** depend on the value of the ``hash_behaviour`` setting in ``ansible.cfg``. 415 416list_merge 417 Is a string, its possible values are ``replace`` (default), ``keep``, ``append``, ``prepend``, ``append_rp`` or ``prepend_rp``. 418 It modifies the behaviour of ``combine`` when the hashes to merge contain arrays/lists. 419 420.. code-block:: yaml 421 422 default: 423 a: 424 x: default 425 y: default 426 b: default 427 c: default 428 patch: 429 a: 430 y: patch 431 z: patch 432 b: patch 433 434If ``recursive=False`` (the default), nested hash aren't merged:: 435 436 {{ default | combine(patch) }} 437 438This would result in:: 439 440 a: 441 y: patch 442 z: patch 443 b: patch 444 c: default 445 446If ``recursive=True``, recurse into nested hash and merge their keys:: 447 448 {{ default | combine(patch, recursive=True) }} 449 450This would result in:: 451 452 a: 453 x: default 454 y: patch 455 z: patch 456 b: patch 457 c: default 458 459If ``list_merge='replace'`` (the default), arrays from the right hash will "replace" the ones in the left hash:: 460 461 default: 462 a: 463 - default 464 patch: 465 a: 466 - patch 467 468.. code-block:: jinja 469 470 {{ default | combine(patch) }} 471 472This would result in:: 473 474 a: 475 - patch 476 477If ``list_merge='keep'``, arrays from the left hash will be kept:: 478 479 {{ default | combine(patch, list_merge='keep') }} 480 481This would result in:: 482 483 a: 484 - default 485 486If ``list_merge='append'``, arrays from the right hash will be appended to the ones in the left hash:: 487 488 {{ default | combine(patch, list_merge='append') }} 489 490This would result in:: 491 492 a: 493 - default 494 - patch 495 496If ``list_merge='prepend'``, arrays from the right hash will be prepended to the ones in the left hash:: 497 498 {{ default | combine(patch, list_merge='prepend') }} 499 500This would result in:: 501 502 a: 503 - patch 504 - default 505 506If ``list_merge='append_rp'``, arrays from the right hash will be appended to the ones in the left hash. Elements of arrays in the left hash that are also in the corresponding array of the right hash will be removed ("rp" stands for "remove present"). Duplicate elements that aren't in both hashes are kept:: 507 508 default: 509 a: 510 - 1 511 - 1 512 - 2 513 - 3 514 patch: 515 a: 516 - 3 517 - 4 518 - 5 519 - 5 520 521.. code-block:: jinja 522 523 {{ default | combine(patch, list_merge='append_rp') }} 524 525This would result in:: 526 527 a: 528 - 1 529 - 1 530 - 2 531 - 3 532 - 4 533 - 5 534 - 5 535 536If ``list_merge='prepend_rp'``, the behavior is similar to the one for ``append_rp``, but elements of arrays in the right hash are prepended:: 537 538 {{ default | combine(patch, list_merge='prepend_rp') }} 539 540This would result in:: 541 542 a: 543 - 3 544 - 4 545 - 5 546 - 5 547 - 1 548 - 1 549 - 2 550 551``recursive`` and ``list_merge`` can be used together:: 552 553 default: 554 a: 555 a': 556 x: default_value 557 y: default_value 558 list: 559 - default_value 560 b: 561 - 1 562 - 1 563 - 2 564 - 3 565 patch: 566 a: 567 a': 568 y: patch_value 569 z: patch_value 570 list: 571 - patch_value 572 b: 573 - 3 574 - 4 575 - 4 576 - key: value 577 578.. code-block:: jinja 579 580 {{ default | combine(patch, recursive=True, list_merge='append_rp') }} 581 582This would result in:: 583 584 a: 585 a': 586 x: default_value 587 y: patch_value 588 z: patch_value 589 list: 590 - default_value 591 - patch_value 592 b: 593 - 1 594 - 1 595 - 2 596 - 3 597 - 4 598 - 4 599 - key: value 600 601 602.. _extract_filter: 603 604Selecting values from arrays or hashtables 605------------------------------------------- 606 607.. versionadded:: 2.1 608 609The `extract` filter is used to map from a list of indices to a list of values from a container (hash or array):: 610 611 {{ [0,2] | map('extract', ['x','y','z']) | list }} 612 {{ ['x','y'] | map('extract', {'x': 42, 'y': 31}) | list }} 613 614The results of the above expressions would be:: 615 616 ['x', 'z'] 617 [42, 31] 618 619The filter can take another argument:: 620 621 {{ groups['x'] | map('extract', hostvars, 'ec2_ip_address') | list }} 622 623This takes the list of hosts in group 'x', looks them up in `hostvars`, and then looks up the `ec2_ip_address` of the result. The final result is a list of IP addresses for the hosts in group 'x'. 624 625The third argument to the filter can also be a list, for a recursive lookup inside the container:: 626 627 {{ ['a'] | map('extract', b, ['x','y']) | list }} 628 629This would return a list containing the value of `b['a']['x']['y']`. 630 631Combining lists 632--------------- 633 634This set of filters returns a list of combined lists. 635 636 637permutations 638^^^^^^^^^^^^ 639To get permutations of a list:: 640 641 - name: Give me largest permutations (order matters) 642 ansible.builtin.debug: 643 msg: "{{ [1,2,3,4,5] | permutations | list }}" 644 645 - name: Give me permutations of sets of three 646 ansible.builtin.debug: 647 msg: "{{ [1,2,3,4,5] | permutations(3) | list }}" 648 649combinations 650^^^^^^^^^^^^ 651Combinations always require a set size:: 652 653 - name: Give me combinations for sets of two 654 ansible.builtin.debug: 655 msg: "{{ [1,2,3,4,5] | combinations(2) | list }}" 656 657Also see the :ref:`zip_filter` 658 659products 660^^^^^^^^ 661The product filter returns the `cartesian product <https://docs.python.org/3/library/itertools.html#itertools.product>`_ of the input iterables. This is roughly equivalent to nested for-loops in a generator expression. 662 663For example:: 664 665 - name: Generate multiple hostnames 666 ansible.builtin.debug: 667 msg: "{{ ['foo', 'bar'] | product(['com']) | map('join', '.') | join(',') }}" 668 669This would result in:: 670 671 { "msg": "foo.com,bar.com" } 672 673.. json_query_filter: 674 675Selecting JSON data: JSON queries 676--------------------------------- 677 678To select a single element or a data subset from a complex data structure in JSON format (for example, Ansible facts), use the ``json_query`` filter. The ``json_query`` filter lets you query a complex JSON structure and iterate over it using a loop structure. 679 680.. note:: 681 682 This filter has migrated to the `community.general <https://galaxy.ansible.com/community/general>`_ collection. Follow the installation instructions to install that collection. 683 684 685.. note:: This filter is built upon **jmespath**, and you can use the same syntax. For examples, see `jmespath examples <http://jmespath.org/examples.html>`_. 686 687Consider this data structure:: 688 689 { 690 "domain_definition": { 691 "domain": { 692 "cluster": [ 693 { 694 "name": "cluster1" 695 }, 696 { 697 "name": "cluster2" 698 } 699 ], 700 "server": [ 701 { 702 "name": "server11", 703 "cluster": "cluster1", 704 "port": "8080" 705 }, 706 { 707 "name": "server12", 708 "cluster": "cluster1", 709 "port": "8090" 710 }, 711 { 712 "name": "server21", 713 "cluster": "cluster2", 714 "port": "9080" 715 }, 716 { 717 "name": "server22", 718 "cluster": "cluster2", 719 "port": "9090" 720 } 721 ], 722 "library": [ 723 { 724 "name": "lib1", 725 "target": "cluster1" 726 }, 727 { 728 "name": "lib2", 729 "target": "cluster2" 730 } 731 ] 732 } 733 } 734 } 735 736To extract all clusters from this structure, you can use the following query:: 737 738 - name: Display all cluster names 739 ansible.builtin.debug: 740 var: item 741 loop: "{{ domain_definition | community.general.json_query('domain.cluster[*].name') }}" 742 743To extract all server names:: 744 745 - name: Display all server names 746 ansible.builtin.debug: 747 var: item 748 loop: "{{ domain_definition | community.general.json_query('domain.server[*].name') }}" 749 750To extract ports from cluster1:: 751 752 - ansible.builtin.name: Display all ports from cluster1 753 debug: 754 var: item 755 loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}" 756 vars: 757 server_name_cluster1_query: "domain.server[?cluster=='cluster1'].port" 758 759.. note:: You can use a variable to make the query more readable. 760 761To print out the ports from cluster1 in a comma separated string:: 762 763 - name: Display all ports from cluster1 as a string 764 ansible.builtin.debug: 765 msg: "{{ domain_definition | community.general.json_query('domain.server[?cluster==`cluster1`].port') | join(', ') }}" 766 767.. note:: In the example above, quoting literals using backticks avoids escaping quotes and maintains readability. 768 769You can use YAML `single quote escaping <https://yaml.org/spec/current.html#id2534365>`_:: 770 771 - name: Display all ports from cluster1 772 ansible.builtin.debug: 773 var: item 774 loop: "{{ domain_definition | community.general.json_query('domain.server[?cluster==''cluster1''].port') }}" 775 776.. note:: Escaping single quotes within single quotes in YAML is done by doubling the single quote. 777 778To get a hash map with all ports and names of a cluster:: 779 780 - name: Display all server ports and names from cluster1 781 ansible.builtin.debug: 782 var: item 783 loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}" 784 vars: 785 server_name_cluster1_query: "domain.server[?cluster=='cluster2'].{name: name, port: port}" 786 787 788Randomizing data 789================ 790 791When you need a randomly generated value, use one of these filters. 792 793 794.. _random_mac_filter: 795 796Random MAC addresses 797-------------------- 798 799.. versionadded:: 2.6 800 801This filter can be used to generate a random MAC address from a string prefix. 802 803.. note:: 804 805 This filter has migrated to the `community.general <https://galaxy.ansible.com/community/general>`_ collection. Follow the installation instructions to install that collection. 806 807To get a random MAC address from a string prefix starting with '52:54:00':: 808 809 "{{ '52:54:00' | community.general.random_mac }}" 810 # => '52:54:00:ef:1c:03' 811 812Note that if anything is wrong with the prefix string, the filter will issue an error. 813 814 .. versionadded:: 2.9 815 816As of Ansible version 2.9, you can also initialize the random number generator from a seed to create random-but-idempotent MAC addresses:: 817 818 "{{ '52:54:00' | community.general.random_mac(seed=inventory_hostname) }}" 819 820 821.. _random_filter: 822 823Random items or numbers 824----------------------- 825 826The ``random`` filter in Ansible is an extension of the default Jinja2 random filter, and can be used to return a random item from a sequence of items or to generate a random number based on a range. 827 828To get a random item from a list:: 829 830 "{{ ['a','b','c'] | random }}" 831 # => 'c' 832 833To get a random number between 0 and a specified number:: 834 835 "{{ 60 | random }} * * * * root /script/from/cron" 836 # => '21 * * * * root /script/from/cron' 837 838To get a random number from 0 to 100 but in steps of 10:: 839 840 {{ 101 | random(step=10) }} 841 # => 70 842 843To get a random number from 1 to 100 but in steps of 10:: 844 845 {{ 101 | random(1, 10) }} 846 # => 31 847 {{ 101 | random(start=1, step=10) }} 848 # => 51 849 850You can initialize the random number generator from a seed to create random-but-idempotent numbers:: 851 852 "{{ 60 | random(seed=inventory_hostname) }} * * * * root /script/from/cron" 853 854Shuffling a list 855---------------- 856 857The ``shuffle`` filter randomizes an existing list, giving a different order every invocation. 858 859To get a random list from an existing list:: 860 861 {{ ['a','b','c'] | shuffle }} 862 # => ['c','a','b'] 863 {{ ['a','b','c'] | shuffle }} 864 # => ['b','c','a'] 865 866You can initialize the shuffle generator from a seed to generate a random-but-idempotent order:: 867 868 {{ ['a','b','c'] | shuffle(seed=inventory_hostname) }} 869 # => ['b','a','c'] 870 871The shuffle filter returns a list whenever possible. If you use it with a non 'listable' item, the filter does nothing. 872 873 874.. _list_filters: 875 876Managing list variables 877======================= 878 879You can search for the minimum or maximum value in a list, or flatten a multi-level list. 880 881To get the minimum value from list of numbers:: 882 883 {{ list1 | min }} 884 885To get the maximum value from a list of numbers:: 886 887 {{ [3, 4, 2] | max }} 888 889.. versionadded:: 2.5 890 891Flatten a list (same thing the `flatten` lookup does):: 892 893 {{ [3, [4, 2] ] | flatten }} 894 895Flatten only the first level of a list (akin to the `items` lookup):: 896 897 {{ [3, [4, [2]] ] | flatten(levels=1) }} 898 899 900.. _set_theory_filters: 901 902Selecting from sets or lists (set theory) 903========================================= 904 905You can select or combine items from sets or lists. 906 907.. versionadded:: 1.4 908 909To get a unique set from a list:: 910 911 # list1: [1, 2, 5, 1, 3, 4, 10] 912 {{ list1 | unique }} 913 # => [1, 2, 5, 3, 4, 10] 914 915To get a union of two lists:: 916 917 # list1: [1, 2, 5, 1, 3, 4, 10] 918 # list2: [1, 2, 3, 4, 5, 11, 99] 919 {{ list1 | union(list2) }} 920 # => [1, 2, 5, 1, 3, 4, 10, 11, 99] 921 922To get the intersection of 2 lists (unique list of all items in both):: 923 924 # list1: [1, 2, 5, 3, 4, 10] 925 # list2: [1, 2, 3, 4, 5, 11, 99] 926 {{ list1 | intersect(list2) }} 927 # => [1, 2, 5, 3, 4] 928 929To get the difference of 2 lists (items in 1 that don't exist in 2):: 930 931 # list1: [1, 2, 5, 1, 3, 4, 10] 932 # list2: [1, 2, 3, 4, 5, 11, 99] 933 {{ list1 | difference(list2) }} 934 # => [10] 935 936To get the symmetric difference of 2 lists (items exclusive to each list):: 937 938 # list1: [1, 2, 5, 1, 3, 4, 10] 939 # list2: [1, 2, 3, 4, 5, 11, 99] 940 {{ list1 | symmetric_difference(list2) }} 941 # => [10, 11, 99] 942 943.. _math_stuff: 944 945Calculating numbers (math) 946========================== 947 948.. versionadded:: 1.9 949 950You can calculate logs, powers, and roots of numbers with Ansible filters. Jinja2 provides other mathematical functions like abs() and round(). 951 952Get the logarithm (default is e):: 953 954 {{ myvar | log }} 955 956Get the base 10 logarithm:: 957 958 {{ myvar | log(10) }} 959 960Give me the power of 2! (or 5):: 961 962 {{ myvar | pow(2) }} 963 {{ myvar | pow(5) }} 964 965Square root, or the 5th:: 966 967 {{ myvar | root }} 968 {{ myvar | root(5) }} 969 970 971Managing network interactions 972============================= 973 974These filters help you with common network tasks. 975 976.. note:: 977 978 These filters have migrated to the `ansible.netcommon <https://galaxy.ansible.com/ansible/netcommon>`_ collection. Follow the installation instructions to install that collection. 979 980.. _ipaddr_filter: 981 982IP address filters 983------------------ 984 985.. versionadded:: 1.9 986 987To test if a string is a valid IP address:: 988 989 {{ myvar | ansible.netcommon.ipaddr }} 990 991You can also require a specific IP protocol version:: 992 993 {{ myvar | ansible.netcommon.ipv4 }} 994 {{ myvar | ansible.netcommon.ipv6 }} 995 996IP address filter can also be used to extract specific information from an IP 997address. For example, to get the IP address itself from a CIDR, you can use:: 998 999 {{ '192.0.2.1/24' | ansible.netcommon.ipaddr('address') }} 1000 1001More information about ``ipaddr`` filter and complete usage guide can be found 1002in :ref:`playbooks_filters_ipaddr`. 1003 1004.. _network_filters: 1005 1006Network CLI filters 1007------------------- 1008 1009.. versionadded:: 2.4 1010 1011To convert the output of a network device CLI command into structured JSON 1012output, use the ``parse_cli`` filter:: 1013 1014 {{ output | ansible.netcommon.parse_cli('path/to/spec') }} 1015 1016The ``parse_cli`` filter will load the spec file and pass the command output 1017through it, returning JSON output. The YAML spec file defines how to parse the CLI output. 1018 1019The spec file should be valid formatted YAML. It defines how to parse the CLI 1020output and return JSON data. Below is an example of a valid spec file that 1021will parse the output from the ``show vlan`` command. 1022 1023.. code-block:: yaml 1024 1025 --- 1026 vars: 1027 vlan: 1028 vlan_id: "{{ item.vlan_id }}" 1029 name: "{{ item.name }}" 1030 enabled: "{{ item.state != 'act/lshut' }}" 1031 state: "{{ item.state }}" 1032 1033 keys: 1034 vlans: 1035 value: "{{ vlan }}" 1036 items: "^(?P<vlan_id>\\d+)\\s+(?P<name>\\w+)\\s+(?P<state>active|act/lshut|suspended)" 1037 state_static: 1038 value: present 1039 1040 1041The spec file above will return a JSON data structure that is a list of hashes 1042with the parsed VLAN information. 1043 1044The same command could be parsed into a hash by using the key and values 1045directives. Here is an example of how to parse the output into a hash 1046value using the same ``show vlan`` command. 1047 1048.. code-block:: yaml 1049 1050 --- 1051 vars: 1052 vlan: 1053 key: "{{ item.vlan_id }}" 1054 values: 1055 vlan_id: "{{ item.vlan_id }}" 1056 name: "{{ item.name }}" 1057 enabled: "{{ item.state != 'act/lshut' }}" 1058 state: "{{ item.state }}" 1059 1060 keys: 1061 vlans: 1062 value: "{{ vlan }}" 1063 items: "^(?P<vlan_id>\\d+)\\s+(?P<name>\\w+)\\s+(?P<state>active|act/lshut|suspended)" 1064 state_static: 1065 value: present 1066 1067Another common use case for parsing CLI commands is to break a large command 1068into blocks that can be parsed. This can be done using the ``start_block`` and 1069``end_block`` directives to break the command into blocks that can be parsed. 1070 1071.. code-block:: yaml 1072 1073 --- 1074 vars: 1075 interface: 1076 name: "{{ item[0].match[0] }}" 1077 state: "{{ item[1].state }}" 1078 mode: "{{ item[2].match[0] }}" 1079 1080 keys: 1081 interfaces: 1082 value: "{{ interface }}" 1083 start_block: "^Ethernet.*$" 1084 end_block: "^$" 1085 items: 1086 - "^(?P<name>Ethernet\\d\\/\\d*)" 1087 - "admin state is (?P<state>.+)," 1088 - "Port mode is (.+)" 1089 1090 1091The example above will parse the output of ``show interface`` into a list of 1092hashes. 1093 1094The network filters also support parsing the output of a CLI command using the 1095TextFSM library. To parse the CLI output with TextFSM use the following 1096filter:: 1097 1098 {{ output.stdout[0] | ansible.netcommon.parse_cli_textfsm('path/to/fsm') }} 1099 1100Use of the TextFSM filter requires the TextFSM library to be installed. 1101 1102Network XML filters 1103------------------- 1104 1105.. versionadded:: 2.5 1106 1107To convert the XML output of a network device command into structured JSON 1108output, use the ``parse_xml`` filter:: 1109 1110 {{ output | ansible.netcommon.parse_xml('path/to/spec') }} 1111 1112The ``parse_xml`` filter will load the spec file and pass the command output 1113through formatted as JSON. 1114 1115The spec file should be valid formatted YAML. It defines how to parse the XML 1116output and return JSON data. 1117 1118Below is an example of a valid spec file that 1119will parse the output from the ``show vlan | display xml`` command. 1120 1121.. code-block:: yaml 1122 1123 --- 1124 vars: 1125 vlan: 1126 vlan_id: "{{ item.vlan_id }}" 1127 name: "{{ item.name }}" 1128 desc: "{{ item.desc }}" 1129 enabled: "{{ item.state.get('inactive') != 'inactive' }}" 1130 state: "{% if item.state.get('inactive') == 'inactive'%} inactive {% else %} active {% endif %}" 1131 1132 keys: 1133 vlans: 1134 value: "{{ vlan }}" 1135 top: configuration/vlans/vlan 1136 items: 1137 vlan_id: vlan-id 1138 name: name 1139 desc: description 1140 state: ".[@inactive='inactive']" 1141 1142 1143The spec file above will return a JSON data structure that is a list of hashes 1144with the parsed VLAN information. 1145 1146The same command could be parsed into a hash by using the key and values 1147directives. Here is an example of how to parse the output into a hash 1148value using the same ``show vlan | display xml`` command. 1149 1150.. code-block:: yaml 1151 1152 --- 1153 vars: 1154 vlan: 1155 key: "{{ item.vlan_id }}" 1156 values: 1157 vlan_id: "{{ item.vlan_id }}" 1158 name: "{{ item.name }}" 1159 desc: "{{ item.desc }}" 1160 enabled: "{{ item.state.get('inactive') != 'inactive' }}" 1161 state: "{% if item.state.get('inactive') == 'inactive'%} inactive {% else %} active {% endif %}" 1162 1163 keys: 1164 vlans: 1165 value: "{{ vlan }}" 1166 top: configuration/vlans/vlan 1167 items: 1168 vlan_id: vlan-id 1169 name: name 1170 desc: description 1171 state: ".[@inactive='inactive']" 1172 1173 1174The value of ``top`` is the XPath relative to the XML root node. 1175In the example XML output given below, the value of ``top`` is ``configuration/vlans/vlan``, 1176which is an XPath expression relative to the root node (<rpc-reply>). 1177``configuration`` in the value of ``top`` is the outer most container node, and ``vlan`` 1178is the inner-most container node. 1179 1180``items`` is a dictionary of key-value pairs that map user-defined names to XPath expressions 1181that select elements. The Xpath expression is relative to the value of the XPath value contained in ``top``. 1182For example, the ``vlan_id`` in the spec file is a user defined name and its value ``vlan-id`` is the 1183relative to the value of XPath in ``top`` 1184 1185Attributes of XML tags can be extracted using XPath expressions. The value of ``state`` in the spec 1186is an XPath expression used to get the attributes of the ``vlan`` tag in output XML.:: 1187 1188 <rpc-reply> 1189 <configuration> 1190 <vlans> 1191 <vlan inactive="inactive"> 1192 <name>vlan-1</name> 1193 <vlan-id>200</vlan-id> 1194 <description>This is vlan-1</description> 1195 </vlan> 1196 </vlans> 1197 </configuration> 1198 </rpc-reply> 1199 1200.. note:: 1201 For more information on supported XPath expressions, see `XPath Support <https://docs.python.org/2/library/xml.etree.elementtree.html#xpath-support>`_. 1202 1203Network VLAN filters 1204-------------------- 1205 1206.. versionadded:: 2.8 1207 1208Use the ``vlan_parser`` filter to transform an unsorted list of VLAN integers into a 1209sorted string list of integers according to IOS-like VLAN list rules. This list has the following properties: 1210 1211* Vlans are listed in ascending order. 1212* Three or more consecutive VLANs are listed with a dash. 1213* The first line of the list can be first_line_len characters long. 1214* Subsequent list lines can be other_line_len characters. 1215 1216To sort a VLAN list:: 1217 1218 {{ [3003, 3004, 3005, 100, 1688, 3002, 3999] | ansible.netcommon.vlan_parser }} 1219 1220This example renders the following sorted list:: 1221 1222 ['100,1688,3002-3005,3999'] 1223 1224 1225Another example Jinja template:: 1226 1227 {% set parsed_vlans = vlans | ansible.netcommon.vlan_parser %} 1228 switchport trunk allowed vlan {{ parsed_vlans[0] }} 1229 {% for i in range (1, parsed_vlans | count) %} 1230 switchport trunk allowed vlan add {{ parsed_vlans[i] }} 1231 1232This allows for dynamic generation of VLAN lists on a Cisco IOS tagged interface. You can store an exhaustive raw list of the exact VLANs required for an interface and then compare that to the parsed IOS output that would actually be generated for the configuration. 1233 1234 1235.. _hash_filters: 1236 1237Encrypting and checksumming strings and passwords 1238================================================= 1239 1240.. versionadded:: 1.9 1241 1242To get the sha1 hash of a string:: 1243 1244 {{ 'test1' | hash('sha1') }} 1245 1246To get the md5 hash of a string:: 1247 1248 {{ 'test1' | hash('md5') }} 1249 1250Get a string checksum:: 1251 1252 {{ 'test2' | checksum }} 1253 1254Other hashes (platform dependent):: 1255 1256 {{ 'test2' | hash('blowfish') }} 1257 1258To get a sha512 password hash (random salt):: 1259 1260 {{ 'passwordsaresecret' | password_hash('sha512') }} 1261 1262To get a sha256 password hash with a specific salt:: 1263 1264 {{ 'secretpassword' | password_hash('sha256', 'mysecretsalt') }} 1265 1266An idempotent method to generate unique hashes per system is to use a salt that is consistent between runs:: 1267 1268 {{ 'secretpassword' | password_hash('sha512', 65534 | random(seed=inventory_hostname) | string) }} 1269 1270Hash types available depend on the master system running Ansible, 'hash' depends on hashlib, password_hash depends on passlib (https://passlib.readthedocs.io/en/stable/lib/passlib.hash.html). 1271 1272.. versionadded:: 2.7 1273 1274Some hash types allow providing a rounds parameter:: 1275 1276 {{ 'secretpassword' | password_hash('sha256', 'mysecretsalt', rounds=10000) }} 1277 1278.. _other_useful_filters: 1279 1280Manipulating text 1281================= 1282 1283Several filters work with text, including URLs, file names, and path names. 1284 1285.. _comment_filter: 1286 1287Adding comments to files 1288------------------------ 1289 1290The ``comment`` filter lets you create comments in a file from text in a template, with a variety of comment styles. By default Ansible uses ``#`` to start a comment line and adds a blank comment line above and below your comment text. For example the following:: 1291 1292 {{ "Plain style (default)" | comment }} 1293 1294produces this output: 1295 1296.. code-block:: text 1297 1298 # 1299 # Plain style (default) 1300 # 1301 1302Ansible offers styles for comments in C (``//...``), C block 1303(``/*...*/``), Erlang (``%...``) and XML (``<!--...-->``):: 1304 1305 {{ "C style" | comment('c') }} 1306 {{ "C block style" | comment('cblock') }} 1307 {{ "Erlang style" | comment('erlang') }} 1308 {{ "XML style" | comment('xml') }} 1309 1310You can define a custom comment character. This filter:: 1311 1312 {{ "My Special Case" | comment(decoration="! ") }} 1313 1314produces: 1315 1316.. code-block:: text 1317 1318 ! 1319 ! My Special Case 1320 ! 1321 1322You can fully customize the comment style:: 1323 1324 {{ "Custom style" | comment('plain', prefix='#######\n#', postfix='#\n#######\n ###\n #') }} 1325 1326That creates the following output: 1327 1328.. code-block:: text 1329 1330 ####### 1331 # 1332 # Custom style 1333 # 1334 ####### 1335 ### 1336 # 1337 1338The filter can also be applied to any Ansible variable. For example to 1339make the output of the ``ansible_managed`` variable more readable, we can 1340change the definition in the ``ansible.cfg`` file to this: 1341 1342.. code-block:: jinja 1343 1344 [defaults] 1345 1346 ansible_managed = This file is managed by Ansible.%n 1347 template: {file} 1348 date: %Y-%m-%d %H:%M:%S 1349 user: {uid} 1350 host: {host} 1351 1352and then use the variable with the `comment` filter:: 1353 1354 {{ ansible_managed | comment }} 1355 1356which produces this output: 1357 1358.. code-block:: sh 1359 1360 # 1361 # This file is managed by Ansible. 1362 # 1363 # template: /home/ansible/env/dev/ansible_managed/roles/role1/templates/test.j2 1364 # date: 2015-09-10 11:02:58 1365 # user: ansible 1366 # host: myhost 1367 # 1368 1369Splitting URLs 1370-------------- 1371 1372.. versionadded:: 2.4 1373 1374The ``urlsplit`` filter extracts the fragment, hostname, netloc, password, path, port, query, scheme, and username from an URL. With no arguments, returns a dictionary of all the fields:: 1375 1376 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('hostname') }} 1377 # => 'www.acme.com' 1378 1379 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('netloc') }} 1380 # => 'user:password@www.acme.com:9000' 1381 1382 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('username') }} 1383 # => 'user' 1384 1385 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('password') }} 1386 # => 'password' 1387 1388 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('path') }} 1389 # => '/dir/index.html' 1390 1391 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('port') }} 1392 # => '9000' 1393 1394 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('scheme') }} 1395 # => 'http' 1396 1397 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('query') }} 1398 # => 'query=term' 1399 1400 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('fragment') }} 1401 # => 'fragment' 1402 1403 {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit }} 1404 # => 1405 # { 1406 # "fragment": "fragment", 1407 # "hostname": "www.acme.com", 1408 # "netloc": "user:password@www.acme.com:9000", 1409 # "password": "password", 1410 # "path": "/dir/index.html", 1411 # "port": 9000, 1412 # "query": "query=term", 1413 # "scheme": "http", 1414 # "username": "user" 1415 # } 1416 1417Searching strings with regular expressions 1418------------------------------------------ 1419 1420To search a string with a regex, use the "regex_search" filter:: 1421 1422 # search for "foo" in "foobar" 1423 {{ 'foobar' | regex_search('(foo)') }} 1424 1425 # will return empty if it cannot find a match 1426 {{ 'ansible' | regex_search('(foobar)') }} 1427 1428 # case insensitive search in multiline mode 1429 {{ 'foo\nBAR' | regex_search("^bar", multiline=True, ignorecase=True) }} 1430 1431 1432To search for all occurrences of regex matches, use the "regex_findall" filter:: 1433 1434 # Return a list of all IPv4 addresses in the string 1435 {{ 'Some DNS servers are 8.8.8.8 and 8.8.4.4' | regex_findall('\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b') }} 1436 1437 1438To replace text in a string with regex, use the "regex_replace" filter:: 1439 1440 # convert "ansible" to "able" 1441 {{ 'ansible' | regex_replace('^a.*i(.*)$', 'a\\1') }} 1442 1443 # convert "foobar" to "bar" 1444 {{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }} 1445 1446 # convert "localhost:80" to "localhost, 80" using named groups 1447 {{ 'localhost:80' | regex_replace('^(?P<host>.+):(?P<port>\\d+)$', '\\g<host>, \\g<port>') }} 1448 1449 # convert "localhost:80" to "localhost" 1450 {{ 'localhost:80' | regex_replace(':80') }} 1451 1452 # change a multiline string 1453 {{ var | regex_replace('^', '#CommentThis#', multiline=True) }} 1454 1455.. note:: 1456 If you want to match the whole string and you are using ``*`` make sure to always wraparound your regular expression with the start/end anchors. For example ``^(.*)$`` will always match only one result, while ``(.*)`` on some Python versions will match the whole string and an empty string at the end, which means it will make two replacements:: 1457 1458 # add "https://" prefix to each item in a list 1459 GOOD: 1460 {{ hosts | map('regex_replace', '^(.*)$', 'https://\\1') | list }} 1461 {{ hosts | map('regex_replace', '(.+)', 'https://\\1') | list }} 1462 {{ hosts | map('regex_replace', '^', 'https://') | list }} 1463 1464 BAD: 1465 {{ hosts | map('regex_replace', '(.*)', 'https://\\1') | list }} 1466 1467 # append ':80' to each item in a list 1468 GOOD: 1469 {{ hosts | map('regex_replace', '^(.*)$', '\\1:80') | list }} 1470 {{ hosts | map('regex_replace', '(.+)', '\\1:80') | list }} 1471 {{ hosts | map('regex_replace', '$', ':80') | list }} 1472 1473 BAD: 1474 {{ hosts | map('regex_replace', '(.*)', '\\1:80') | list }} 1475 1476.. note:: 1477 Prior to ansible 2.0, if "regex_replace" filter was used with variables inside YAML arguments (as opposed to simpler 'key=value' arguments), then you needed to escape backreferences (for example, ``\\1``) with 4 backslashes (``\\\\``) instead of 2 (``\\``). 1478 1479.. versionadded:: 2.0 1480 1481To escape special characters within a standard Python regex, use the "regex_escape" filter (using the default re_type='python' option):: 1482 1483 # convert '^f.*o(.*)$' to '\^f\.\*o\(\.\*\)\$' 1484 {{ '^f.*o(.*)$' | regex_escape() }} 1485 1486.. versionadded:: 2.8 1487 1488To escape special characters within a POSIX basic regex, use the "regex_escape" filter with the re_type='posix_basic' option:: 1489 1490 # convert '^f.*o(.*)$' to '\^f\.\*o(\.\*)\$' 1491 {{ '^f.*o(.*)$' | regex_escape('posix_basic') }} 1492 1493 1494Managing file names and path names 1495---------------------------------- 1496 1497To get the last name of a file path, like 'foo.txt' out of '/etc/asdf/foo.txt':: 1498 1499 {{ path | basename }} 1500 1501To get the last name of a windows style file path (new in version 2.0):: 1502 1503 {{ path | win_basename }} 1504 1505To separate the windows drive letter from the rest of a file path (new in version 2.0):: 1506 1507 {{ path | win_splitdrive }} 1508 1509To get only the windows drive letter:: 1510 1511 {{ path | win_splitdrive | first }} 1512 1513To get the rest of the path without the drive letter:: 1514 1515 {{ path | win_splitdrive | last }} 1516 1517To get the directory from a path:: 1518 1519 {{ path | dirname }} 1520 1521To get the directory from a windows path (new version 2.0):: 1522 1523 {{ path | win_dirname }} 1524 1525To expand a path containing a tilde (`~`) character (new in version 1.5):: 1526 1527 {{ path | expanduser }} 1528 1529To expand a path containing environment variables:: 1530 1531 {{ path | expandvars }} 1532 1533.. note:: `expandvars` expands local variables; using it on remote paths can lead to errors. 1534 1535.. versionadded:: 2.6 1536 1537To get the real path of a link (new in version 1.8):: 1538 1539 {{ path | realpath }} 1540 1541To get the relative path of a link, from a start point (new in version 1.7):: 1542 1543 {{ path | relpath('/etc') }} 1544 1545To get the root and extension of a path or file name (new in version 2.0):: 1546 1547 # with path == 'nginx.conf' the return would be ('nginx', '.conf') 1548 {{ path | splitext }} 1549 1550The ``splitext`` filter returns a string. The individual components can be accessed by using the ``first`` and ``last`` filters:: 1551 1552 # with path == 'nginx.conf' the return would be 'nginx' 1553 {{ path | splitext | first }} 1554 1555 # with path == 'nginx.conf' the return would be 'conf' 1556 {{ path | splitext | last }} 1557 1558To join one or more path components:: 1559 1560 {{ ('/etc', path, 'subdir', file) | path_join }} 1561 1562.. versionadded:: 2.10 1563 1564Manipulating strings 1565==================== 1566 1567To add quotes for shell usage:: 1568 1569 - name: Run a shell command 1570 ansible.builtin.shell: echo {{ string_value | quote }} 1571 1572To concatenate a list into a string:: 1573 1574 {{ list | join(" ") }} 1575 1576To work with Base64 encoded strings:: 1577 1578 {{ encoded | b64decode }} 1579 {{ decoded | string | b64encode }} 1580 1581As of version 2.6, you can define the type of encoding to use, the default is ``utf-8``:: 1582 1583 {{ encoded | b64decode(encoding='utf-16-le') }} 1584 {{ decoded | string | b64encode(encoding='utf-16-le') }} 1585 1586.. note:: The ``string`` filter is only required for Python 2 and ensures that text to encode is a unicode string. Without that filter before b64encode the wrong value will be encoded. 1587 1588.. versionadded:: 2.6 1589 1590Managing UUIDs 1591============== 1592 1593To create a namespaced UUIDv5:: 1594 1595 {{ string | to_uuid(namespace='11111111-2222-3333-4444-555555555555') }} 1596 1597.. versionadded:: 2.10 1598 1599To create a namespaced UUIDv5 using the default Ansible namespace '361E6D51-FAEC-444A-9079-341386DA8E2E':: 1600 1601 {{ string | to_uuid }} 1602 1603.. versionadded:: 1.9 1604 1605To make use of one attribute from each item in a list of complex variables, use the :func:`Jinja2 map filter <jinja2:map>`:: 1606 1607 # get a comma-separated list of the mount points (for example, "/,/mnt/stuff") on a host 1608 {{ ansible_mounts | map(attribute='mount') | join(',') }} 1609 1610Handling dates and times 1611======================== 1612 1613To get a date object from a string use the `to_datetime` filter:: 1614 1615 # Get total amount of seconds between two dates. Default date format is %Y-%m-%d %H:%M:%S but you can pass your own format 1616 {{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).total_seconds() }} 1617 1618 # Get remaining seconds after delta has been calculated. NOTE: This does NOT convert years, days, hours, and so on to seconds. For that, use total_seconds() 1619 {{ (("2016-08-14 20:00:12" | to_datetime) - ("2016-08-14 18:00:00" | to_datetime)).seconds }} 1620 # This expression evaluates to "12" and not "132". Delta is 2 hours, 12 seconds 1621 1622 # get amount of days between two dates. This returns only number of days and discards remaining hours, minutes, and seconds 1623 {{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).days }} 1624 1625.. versionadded:: 2.4 1626 1627To format a date using a string (like with the shell date command), use the "strftime" filter:: 1628 1629 # Display year-month-day 1630 {{ '%Y-%m-%d' | strftime }} 1631 1632 # Display hour:min:sec 1633 {{ '%H:%M:%S' | strftime }} 1634 1635 # Use ansible_date_time.epoch fact 1636 {{ '%Y-%m-%d %H:%M:%S' | strftime(ansible_date_time.epoch) }} 1637 1638 # Use arbitrary epoch value 1639 {{ '%Y-%m-%d' | strftime(0) }} # => 1970-01-01 1640 {{ '%Y-%m-%d' | strftime(1441357287) }} # => 2015-09-04 1641 1642.. note:: To get all string possibilities, check https://docs.python.org/3/library/time.html#time.strftime 1643 1644Getting Kubernetes resource names 1645================================= 1646 1647.. note:: 1648 1649 These filters have migrated to the `community.kubernetes <https://galaxy.ansible.com/community/kubernetes>`_ collection. Follow the installation instructions to install that collection. 1650 1651Use the "k8s_config_resource_name" filter to obtain the name of a Kubernetes ConfigMap or Secret, 1652including its hash:: 1653 1654 {{ configmap_resource_definition | community.kubernetes.k8s_config_resource_name }} 1655 1656This can then be used to reference hashes in Pod specifications:: 1657 1658 my_secret: 1659 kind: Secret 1660 name: my_secret_name 1661 1662 deployment_resource: 1663 kind: Deployment 1664 spec: 1665 template: 1666 spec: 1667 containers: 1668 - envFrom: 1669 - secretRef: 1670 name: {{ my_secret | community.kubernetes.k8s_config_resource_name }} 1671 1672.. versionadded:: 2.8 1673 1674.. _PyYAML library: https://pyyaml.org/ 1675 1676.. _PyYAML documentation: https://pyyaml.org/wiki/PyYAMLDocumentation 1677 1678 1679.. seealso:: 1680 1681 :ref:`about_playbooks` 1682 An introduction to playbooks 1683 :ref:`playbooks_conditionals` 1684 Conditional statements in playbooks 1685 :ref:`playbooks_variables` 1686 All about variables 1687 :ref:`playbooks_loops` 1688 Looping in playbooks 1689 :ref:`playbooks_reuse_roles` 1690 Playbook organization by roles 1691 :ref:`playbooks_best_practices` 1692 Tips and tricks for playbooks 1693 `User Mailing List <https://groups.google.com/group/ansible-devel>`_ 1694 Have a question? Stop by the google group! 1695 `irc.libera.chat <https://libera.chat/>`_ 1696 #ansible IRC chat channel 1697