Most Ansible users have come across the args keyword when using the ansible.builtin.command module. In this case the args keyword is used to pass a named parameter such as chdir or creates to the module that accepts a "free form" parameter.
Example:
- ansible.builtin.command: touch filename #Run this command
args:
chdir: /some/path # Change to this path before running command
creates: /some/path/filename # The command creates this file
What you might not know is that the args keyword can be used on any module for a number of different use cases . Here are some examples:
Clean loops
Let's say that you need to create a bunch of different files the same owner but different permissions. To do this you are using the ansible.builtin.file module and a loop.
Not using args:
- ansible.builtin.file:
path: "{{ item.path }}" # This
owner: "{{ item.user }}" # is
group: "{{ item.group }}" # a lot
mode: "{{ item.permissions }}" # of typing
loop:
- path: /tmp/test1
user: root
group: root
permissions: 0600
- path: /tmp/test2
user: root
group: root
permissions: 0644
Using args:
- ansible.builtin.file:
owner: root
group: root
args: "{{ item }}"
loop:
- path: /tmp/test2
mode: 0600
- path: /tmp/test2
mode: 0644
In this case items that we are looping over will be passed to the module as arguments, just like if they had been written at the "normal" location. All files will have root as user & group but the path and mode will be different for each file.
Replacing omit
Have you ever had to support a task where the input to that task is varying and you find yourself having to use | default(omit) everywhere? args can solve that and make the task more readable.
Not using args:
- ansible.builtin.cron:
name: "{{ item.name | default(omit) }}"
user: "{{ item.user | default(omit) }}"
job: "{{ item.job | default(omit) }}"
special_time: "{{ item.special_time | default(omit) }}"
month: "{{ item.month | default(omit) }}"
weekday: "{{ item.weekday | default(omit) }}"
hour: "{{ item.hour | default(omit) }}"
minute: "{{ item.minute | default(omit) }}"
etc.. etc..
loop:
- name: Job 1
user: root
job: /usr/bin/job 1
weekday: 5
hour: 5
- name: job 2
user: ovysxcczso
job: /usr/bin/job 2
minute: 2
- job: /user/bin/job 3
special_time: annually
Using args:
- ansible.builtin.cron:
args: "{{ item }}"
loop:
- name: Job 1
user: root
job: /usr/bin/job 1
weekday: 5
hour: 5
- name: job 2
user: ovysxcczso
job: /usr/bin/job 2
minute: 2
- job: /user/bin/job 3
special_time: annually
Combining tasks
Under special circumstances you find yourself wanting to use the same module with some arguments in one iteration and without those arguments in the next. Say for example that you want to set file permissions for a directory and the files in that directory, only that you want the directory and the contained files to have different permissions.
Not using args:
- name: Set file permissions
ansible.builtin.file:
path: /some/path
state: directory
recurse: true
mode: 0600
- name: Set directory permissions
ansible.builtin.file:
path: /some/path
state: directory
mode: 0644
Using args:
- name: Set file & directory permissions
ansible.builtin.file:
path: /some/path
state: directory
args: "{{ item }}"
loop:
- mode: 0644
recurse: true
- mode: 0600
You could argue that in this example it might be better to have 2 separate tasks to clearly signal the intent of what you are doing, and I'd be inclined to agree. But this method can be used in many different scenarios that might be a better use case than this example.
----
Of course args isn't a silver bullet for every type of problem, but it makes some problems easier to solve and in other cases increases readability.
Hope you learned something new :)
very useful :) thanks !