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