Ansible loop over list and collect output in a list - ansible

I just don't understand how you are supposed to loop over a list and collect the results in a list in ansible:
- name: Collect all containers
command: docker ps --all --no-trunc --format {% raw %}"{{json .}}"{% endraw %}
register: docker_raw_containers
- debug:
msg: "{{ docker_raw_containers.stdout_lines }}"
- name: Convert variables
set_fact:
docker_raw_container_item: "{{ item | to_json }}"
loop: "{{ docker_raw_containers.stdout_lines }}"
- name: Convert to list
set_fact:
docker_parsed_containers: " {{ docker_raw_container_item | map(attribute='ID') | list }} "
- debug:
msg: "{{ docker_parsed_containers }}"
This code should result in a list of containers IDs and CreatedAt attributes. But it just results in a list of AnsibleUndefined objects. Where is my fault?

Converting a json string to an ansible variable requires the from_json filter. You used to_json which does exactly the opposite.
You can create a list all at once my mapping the from_json filter to each result line. The following playbook should meet your requirements with minimum ansible tasks.
---
- name: Parse docker ps output formated as json
hosts: all
gather_facts: false
tasks:
- name: Collect all containers
command: docker ps --all --no-trunc --format {% raw %}"{{json .}}"{% endraw %}
register: docker_raw_containers
# This is an info only command so it never changes the target
changed_when: false
- name: Convert variable
set_fact:
docker_parsed_containers: "{{ docker_raw_containers.stdout_lines | map('from_json') | list }}"
- debug:
msg: "{{ docker_parsed_containers }}"
Which gives the following result on my machine (launched some test containers for the occasion...):
$ ansible --version
ansible 2.9.2
config file = None
configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.6/dist-packages/ansible
executable location = /usr/local/bin/ansible
python version = 3.6.9 (default, Nov 7 2019, 10:44:02) [GCC 8.3.0]
$ ansible-playbook test.yml
PLAY [Parse docker ps output formated as json] **************************************************************************************************************************************************************************************************************************
TASK [Collect all containers] *******************************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Convert variable] *************************************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"Command": "\"bash -c 'while true; do sleep 20000; done'\"",
"CreatedAt": "2019-12-09 10:05:18 +0100 CET",
"ID": "9e6ea71499df19f5c1e33e069c533f43b3ec18c957b31bcca571b0a194b23027",
"Image": "python:3.8",
"Labels": "",
"LocalVolumes": "0",
"Mounts": "",
"Names": "demo2",
"Networks": "bridge",
"Ports": "",
"RunningFor": "39 minutes ago",
"Size": "0B",
"Status": "Up 39 minutes"
},
{
"Command": "\"bash -c 'while true; do sleep 20000; done'\"",
"CreatedAt": "2019-12-09 10:05:17 +0100 CET",
"ID": "038f1e4b1f4dd627f6ccea2ddce858e1055474c6a092f32c773e842e938dec68",
"Image": "python:3.8",
"Labels": "",
"LocalVolumes": "0",
"Mounts": "",
"Names": "demo1",
"Networks": "bridge",
"Ports": "",
"RunningFor": "39 minutes ago",
"Size": "0B",
"Status": "Up 39 minutes"
},
{
"Command": "\"/bin/sh -c 'virtualenv venv'\"",
"CreatedAt": "2019-12-04 18:26:14 +0100 CET",
"ID": "88427258f30226ee9ba8af420978ffb2d4046206a7b6b7dc3ee3f5494236e12b",
"Image": "sha256:8a3e76ff0da0dd43a305461f3a6bf6abd320770fb6c3c2c365b5ea1a0062de0b",
"Labels": "",
"LocalVolumes": "0",
"Mounts": "",
"Names": "cocky_brattain",
"Networks": "bridge",
"Ports": "",
"RunningFor": "4 days ago",
"Size": "0B",
"Status": "Exited (127) 4 days ago"
}
]
}
PLAY RECAP **************************************************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Related

Ansible remove \ and brackets from output

I am trying to print out the kubernetes version and client version with Ansible however the output comes with slashes and how can I remove the brackets for a more cleaner output?
- name: Kubernetes version
run_once: true
changed_when: False
shell: |
kubectl version
delegate_to: localhost
register: kubernetes_version
Output:
name: Output
run_once: true
delegate_to: localhost
debug:
msg: "{{ kubernetes_version.stdout_lines }}"
output:
ok: [localhost] => {
"msg": [
"Client Version: version.Info{Major:\"1\", Minor:\"18\", GitVersion:\"v1.18.4\", GitCommit:\"e0fccafd69541e3750d460ba0f9743\", GitTreeState:\"clean\", BuildDate:\"2020-04-16T11:44:03Z\", GoVersion:\"
go1.13.9\", Compiler:\"gc\", Platform:\"linux/amd64\"}",
"Server Version: version.Info{Major:\"1\", Minor:\"18\", GitVersion:\"v1.18.4\", GitCommit:\"e0fccafd69541e3750d460ba0f9743\", GitTreeState:\"clean\", BuildDate:\"2020-04-16T11:35:47Z\", GoVersion:\"
go1.13.9\", Compiler:\"gc\", Platform:\"linux/amd64\"}"
]
}
I'm replacing my original answer, because I was forgetting that
kubectl version can produce JSON output for us, which makes this
much easier.
By taking the output of kubectl version -o json and passing it
through the from_json filter, we can create an Ansible dictionary
variable from the result.
Then we can use a debug task to print out keys from this variable,
and I think you'll get something closer to what you want.
This playbook:
- hosts: localhost
gather_facts: false
tasks:
- name: run kubectl version
command: kubectl version -o json
register: kv_raw
- set_fact:
kv: "{{ kv_raw.stdout | from_json }}"
- debug:
msg:
- "{{ kv.clientVersion }}"
- "{{ kv.serverVersion }}"
Will produce output like this:
PLAY [localhost] ********************************************************************************************
TASK [run kubectl version] **********************************************************************************
changed: [localhost]
TASK [set_fact] *********************************************************************************************
ok: [localhost]
TASK [debug] ************************************************************************************************
ok: [localhost] => {
"msg": [
{
"buildDate": "2020-11-14T01:08:04Z",
"compiler": "gc",
"gitCommit": "6082e941e6d62f3a0c6ca8ba52927100948b1d0d",
"gitTreeState": "clean",
"gitVersion": "v1.18.2-0-g52c56ce",
"goVersion": "go1.13.15",
"major": "1",
"minor": "18",
"platform": "linux/amd64"
},
{
"buildDate": "2020-10-25T05:12:54Z",
"compiler": "gc",
"gitCommit": "45b9524",
"gitTreeState": "clean",
"gitVersion": "v1.18.3+45b9524",
"goVersion": "go1.13.4",
"major": "1",
"minor": "18+",
"platform": "linux/amd64"
}
]
}
PLAY RECAP **************************************************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

anible loops and subdictionaries

I'm trying to loop through some parameters in Ansible using a jinja template and a yaml variable file.
The content of the jinja template isn't important here, but I can share a version of the playbook and the yaml variable file below.
What I want to be able to do is generate 4 files, each of them with the name of the peer in the filename. At the moment the playbook works, but generates one file called test-peers. I've tried with_nested and with_subelements - subelements only seems to want to work with a list rather than a dictionary, and I can't find a way to list the values within peers with with_nested. Is there a solution to this?
The YAML file:
customer: test_customer
routers:
router1:
router_number: 01
router_model: ISR4431
peers:
America:
hostname: America
priority: Primary
number: 1
Asia:
hostname: Asia
priority: Backup
number: 2
router2:
router_number: 02
router_model: ISR4431
peers:
America:
hostname: America
priority: Primary
number: 1
Asia:
hostname: Asia
priority: Backup
number: 2
The task in the playbook:
tasks:
- name: WAN gateway testing
template:
src: my.template.j2
dest: "{{ 'config/' + customer + '-' + item.1 }}"
with_nested:
- "{{ routers }}"
- peers
You have to transform your data structure to something you can use the intended way. Here is one possible way to do it in the below playbook:
---
- hosts: localhost
gather_facts: false
vars:
customer: test_customer
# Your orig data on a single json line for compactness
routers: {"router1": {"router_number": 1, "router_model": "ISR4431", "peers": {"America": {"hostname": "America", "priority": "Primary", "number": 1}, "Asia": {"hostname": "Asia", "priority": "Backup", "number": 2}}}, "router2": {"router_number": 2, "router_model": "ISR4431", "peers": {"America": {"hostname": "America", "priority": "Primary", "number": 1}, "Asia": {"hostname": "Asia", "priority": "Backup", "number": 2}}}}
tasks:
- name: Transform our dict to something easier to loop with subelements
vars:
current_router:
router_number: "{{ item.router_number }}"
router_model: "{{ item.router_model }}"
peers: "{{ item.peers | dict2items | map(attribute='value') }}"
set_fact:
routers_transformed: "{{ routers_transformed | default([]) + [current_router] }}"
loop: "{{ routers | dict2items | map(attribute='value') }}"
- name: Show the transformed data
debug:
var: routers_transformed
- name: Create a file name for each router/peer association in the structure as per requirement
debug:
msg: "Filename would be config/{{ customer }}/router{{ item.0.router_number }}/{{ item.1.hostname}}"
loop: "{{ routers_transformed | subelements('peers') }}"
loop_control:
label: "router {{ item.0.router_number }} for peer {{ item.1.hostname }}"
which gives:
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [Transform our dict to something easier to loop with subelements] *****************************************************************************************************************************************************************
ok: [localhost] => (item={'router_number': 1, 'router_model': 'ISR4431', 'peers': {'America': {'hostname': 'America', 'priority': 'Primary', 'number': 1}, 'Asia': {'hostname': 'Asia', 'priority': 'Backup', 'number': 2}}})
ok: [localhost] => (item={'router_number': 2, 'router_model': 'ISR4431', 'peers': {'America': {'hostname': 'America', 'priority': 'Primary', 'number': 1}, 'Asia': {'hostname': 'Asia', 'priority': 'Backup', 'number': 2}}})
TASK [Show the transformed data] *******************************************************************************************************************************************************************************************************
ok: [localhost] => {
"routers_transformed": [
{
"peers": [
{
"hostname": "America",
"number": 1,
"priority": "Primary"
},
{
"hostname": "Asia",
"number": 2,
"priority": "Backup"
}
],
"router_model": "ISR4431",
"router_number": "1"
},
{
"peers": [
{
"hostname": "America",
"number": 1,
"priority": "Primary"
},
{
"hostname": "Asia",
"number": 2,
"priority": "Backup"
}
],
"router_model": "ISR4431",
"router_number": "2"
}
]
}
TASK [Create a file name for each router/peer association in the structure as per requirement] *****************************************************************************************************************************************
ok: [localhost] => (item=router 1 for peer America) => {
"msg": "Filename would be config/test_customer/router1/America"
}
ok: [localhost] => (item=router 1 for peer Asia) => {
"msg": "Filename would be config/test_customer/router1/Asia"
}
ok: [localhost] => (item=router 2 for peer America) => {
"msg": "Filename would be config/test_customer/router2/America"
}
ok: [localhost] => (item=router 2 for peer Asia) => {
"msg": "Filename would be config/test_customer/router2/Asia"
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Q: "Restructure the original yaml file."
A: Use template. For example
shell> cat templates/routers.j2
customer: {{ customer }}
routers:
{{ routers2|to_nice_yaml|indent(2) }}
The tasks below do the job
- set_fact:
routers2: "{{ routers2|default({})|
combine({item.0.key: item.0.value|
combine({'peers': item.1})}) }}"
loop: "{{ routers|dict2items|zip(peers)|list }}"
vars:
peers: "{{ routers|json_query('*.peers')|
map('dict2items')|
map('json_query', '[].value')|list }}"
- template:
src: routers.j2
dest: routers.yml
shell> cat routers.yml
customer: test_customer
routers:
router1:
peers:
- hostname: America
number: 1
priority: Primary
- hostname: Asia
number: 2
priority: Backup
router_model: ISR4431
router_number: '01'
router2:
peers:
- hostname: America
number: 1
priority: Primary
- hostname: Asia
number: 2
priority: Backup
router_model: ISR4431
router_number: '02'
Then the loop below
- include_vars: routers.yml
- debug:
msg: >-
{{ item.0.router_number }}
{{ item.0.router_model }}
{{ item.1.hostname }}
{{ item.1.number }}
{{ item.1.priority }}
with_subelements:
- "{{ routers }}"
- peers
gives (abridged)
msg: 01 ISR4431 America 1 Primary
msg: 01 ISR4431 Asia 2 Backup
msg: 02 ISR4431 America 1 Primary
msg: 02 ISR4431 Asia 2 Backup

problems with nested json_query

I have the following json structure.
"results": [
{
"ltm_pools": [
{
"members": [
{
"full_path": "/Common/10.49.128.185:8080",
},
{
"full_path": "/Common/10.49.128.186:8080",
}
"name": "Staging-1-stresslab",
},
{
"members": [
{
"full_path": "/Common/10.49.128.187:0",
},
{
"full_path": "/Common/10.49.128.188:0",
}
],
"name": "Staging-2-lab",
},
I get an error when trying to do something like this
- debug:
msg: "{{item[0].host}} --- {{ item[1] }} --- {{ item[2] }}"
with_nested:
- "{{F5_hosts}}"
- "{{bigip_facts | json_query('[results[0].ltm_pools[*].name]') | flatten }}"
- "{{bigip_facts | json_query('[results[0].ltm_pools[?name.contains(#,'Staging'].members[::2].full_path]') | flatten }}"
I am unable to get the third array working.
I want to print the even members full_path variable from all objects where name contains staging.
I hope someone can help me I've been struggling with this for days.
From what I see/read/tried myself, you fell in this bug: https://github.com/ansible/ansible/issues/27299
This is the problem of "contains" JMESPath function as it is run by Ansible, to quote:
https://github.com/ansible/ansible/issues/27299#issuecomment-331068246
The problem is related to the fact that Ansible uses own types for strings: AnsibleUnicode and AnsibleUnsafeText.
And as long as jmespath library has very strict type-checking, it fails to accept this types as string literals.
There is also a suggested workaround, if you convert the variable to json and back, the strings in there have the correct type. Making long story short, this doesn't work:
"{{bigip_facts | json_query('results[0].ltm_pools[?name.contains(#,`Staging`)==`true`].members[::2].full_path') }}"
but this does:
"{{bigip_facts | to_json | from_json | json_query('results[0].ltm_pools[?name.contains(#,`Staging`)==`true`].members[::2].full_path') }}"
I've managed to run such a code:
- hosts: all
gather_facts: no
tasks:
- set_fact:
bigip_facts:
results:
- ltm_pools:
- members:
- full_path: "/Common/10.49.128.185:8080"
- full_path: "/Common/10.49.128.186:8080"
name: "Staging-1-stresslab"
- members:
- full_path: "/Common/10.49.128.187:0"
- full_path: "/Common/10.49.128.188:0"
name: "Staging-2-stresslab"
- name: "Debug ltm-pools"
debug:
msg: "{{ item }}"
with_items:
- "{{bigip_facts | to_json | from_json | json_query('results[0].ltm_pools[?name.contains(#,`Staging`)==`true`].members[::2].full_path') }}"
And it works as you wanted:
PLAY [all] *****************************************************************************************
TASK [set_fact] ************************************************************************************
ok: [localhost]
TASK [Debug ltm-pools] *****************************************************************************
ok: [localhost] => (item=[u'/Common/10.49.128.185:8080']) => {
"msg": [
"/Common/10.49.128.185:8080"
]
}
ok: [localhost] => (item=[u'/Common/10.49.128.187:0']) => {
"msg": [
"/Common/10.49.128.187:0"
]
}
PLAY RECAP *****************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0

Ansible register result of multiple commands

I was given a task to verify some routing entries for all Linux server and here is how I did it using an Ansible playbook
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
You can see I have to repeat same task for each routing entry and I believe I should be able to avoid this. I tried use with_items loop but got following error message
One or more undefined variables: 'dict object' has no attribute 'stdout'
is there a way to register variable for each command and loop over them one by one ?
Starting in Ansible 1.6.1, the results registered with multiple items are stored in result.results as an array. So you can use result.results[0].stdout and so on.
Testing playbook:
---
- hosts: localhost
gather_facts: no
tasks:
- command: "echo {{item}}"
register: result
with_items: [1, 2]
- debug:
var: result
Result:
$ ansible-playbook -i localhost, test.yml
PLAY [localhost] **************************************************************
TASK: [command echo {{item}}] *************************************************
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)
TASK: [debug ] ****************************************************************
ok: [localhost] => {
"var": {
"result": {
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": [
"echo",
"1"
],
"delta": "0:00:00.002502",
"end": "2015-08-07 16:44:08.901313",
"invocation": {
"module_args": "echo 1",
"module_name": "command"
},
"item": 1,
"rc": 0,
"start": "2015-08-07 16:44:08.898811",
"stderr": "",
"stdout": "1",
"stdout_lines": [
"1"
],
"warnings": []
},
{
"changed": true,
"cmd": [
"echo",
"2"
],
"delta": "0:00:00.002516",
"end": "2015-08-07 16:44:09.038458",
"invocation": {
"module_args": "echo 2",
"module_name": "command"
},
"item": 2,
"rc": 0,
"start": "2015-08-07 16:44:09.035942",
"stderr": "",
"stdout": "2",
"stdout_lines": [
"2"
],
"warnings": []
}
]
}
}
}
PLAY RECAP ********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
A slightly different situation, which took a while to figure out. If you want to use the results of multiple items, but for changed_when, then the register variable will not have a var.results! Instead, changed_when, is evaluated for each item, and you can just directly use the register var.
Simple example, which will result in changed: false:
- action: command echo {{item}}
register: out
changed_when: "'z' in out.stdout"
with_items:
- hello
- foo
- bye
Another example:
- name: Create fulltext index for faster text searches.
mysql_db: name={{SO_database}} state=import target=/tmp/fulltext-{{item.tableName}}-{{item.columnName}}.sql
with_items:
- {tableName: Posts, columnName: Title}
- {tableName: Posts, columnName: Body}
- {tableName: Posts, columnName: Tags}
- {tableName: Comments, columnName: Text}
register: createfulltextcmd
changed_when: createindexcmd.msg.find('already exists') == -1
Finally, when you do want to loop through results in other contexts, it does seem a bit tricky to programmatically access the index as that is not exposed. I did find this one example that might be promising:
- name: add hosts to known_hosts
shell: 'ssh-keyscan -H {{item.host}}>> /home/testuser/known_hosts'
with_items:
- { index: 0, host: testhost1.test.dom }
- { index: 1, host: testhost2.test.dom }
- { index: 2, host: 192.168.202.100 }
when: ssh_known_hosts.results[{{item.index}}].rc == 1
Posting because I can't comment yet
Relating to gameweld's answer, since Ansible 2.5 there's another way to accessing the iteration index.
From the docs:
Tracking progress through a loop with index_var
New in version 2.5.
To keep track of where you are in a loop, use the index_var directive
with loop_control. This directive specifies a variable name to contain
the current loop index:
- name: count our fruit
debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
This also allows you to gather results from an array and act later to the same array, taking into account the previous results
- name: Ensure directories exist
file:
path: "{{ item }}"
state: directory
loop:
- "mouse"
- "lizard"
register: reg
- name: Do something only if directory is new
debug:
msg: "New dir created with name '{{ item }}'"
loop:
- "mouse"
- "lizard"
loop_control:
index_var: index
when: reg.results[index].changed
Please note that the "mouse lizard" array should be exactly the same
If what you need is to register the output of two commands separately, use different variable names.
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result0
changed_when: false
- debug: msg="{{result0.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result1
changed_when: false
- debug: msg="{{result1.stdout}}"

Ansible date variable

I'm trying to learn how to use Ansible facts as variables, and I don't get it. When I run...
$ ansible localhost -m setup
...it lists all of the facts of my system. I selected one at random to try and use it, ansible_facts.ansible_date_time.date, but I can't figure out HOW to use it. When I run...
$ ansible localhost -m setup -a "filter=ansible_date_time"
localhost | success >> {
"ansible_facts": {
"ansible_date_time": {
"date": "2015-07-09",
"day": "09",
"epoch": "1436460014",
"hour": "10",
"iso8601": "2015-07-09T16:40:14Z",
"iso8601_micro": "2015-07-09T16:40:14.795637Z",
"minute": "40",
"month": "07",
"second": "14",
"time": "10:40:14",
"tz": "MDT",
"tz_offset": "-0600",
"weekday": "Thursday",
"year": "2015"
}
},
"changed": false
}
So, it's CLEARLY there. But when I run...
$ ansible localhost -a "echo {{ ansible_facts.ansible_date_time.date }}"
localhost | FAILED => One or more undefined variables: 'ansible_facts' is undefined
$ ansible localhost -a "echo {{ ansible_date_time.date }}"
localhost | FAILED => One or more undefined variables: 'ansible_date_time' is undefined
$ ansible localhost -a "echo {{ date }}"
localhost | FAILED => One or more undefined variables: 'date' is undefined
What am I not getting here? How do I use Facts as variables?
The command ansible localhost -m setup basically says "run the setup module against localhost", and the setup module gathers the facts that you see in the output.
When you run the echo command these facts don't exist since the setup module wasn't run. A better method to testing things like this would be to use ansible-playbook to run a playbook that looks something like this:
- hosts: localhost
tasks:
- debug: var=ansible_date_time
- debug: msg="the current date is {{ ansible_date_time.date }}"
Because this runs as a playbook facts for localhost are gathered before the tasks are run. The output of the above playbook will be something like this:
PLAY [localhost] **************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [debug var=ansible_date_time] *******************************************
ok: [localhost] => {
"ansible_date_time": {
"date": "2015-07-09",
"day": "09",
"epoch": "1436461166",
"hour": "16",
"iso8601": "2015-07-09T16:59:26Z",
"iso8601_micro": "2015-07-09T16:59:26.896629Z",
"minute": "59",
"month": "07",
"second": "26",
"time": "16:59:26",
"tz": "UTC",
"tz_offset": "+0000",
"weekday": "Thursday",
"year": "2015"
}
}
TASK: [debug msg="the current date is {{ ansible_date_time.date }}"] **********
ok: [localhost] => {
"msg": "the current date is 2015-07-09"
}
PLAY RECAP ********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
The lookup module of ansible works fine for me. The yml is:
- hosts: test
vars:
time: "{{ lookup('pipe', 'date -d \"1 day ago\" +\"%Y%m%d\"') }}"
You can replace any command with date to get result of the command.
Note that the ansible command doesn't collect facts, but the ansible-playbook command does. When running ansible -m setup, the setup module happens to run the fact collection so you get the facts, but running ansible -m command does not. Therefore the facts aren't available. This is why the other answers include playbook YAML files and indicate the lookup works.
The filter option filters only the first level subkey below ansible_facts
I tried the lookup('pipe,'date') method and got trouble when I push the playbook to the tower. The tower is somehow using UTC timezone. All play executed as early as the + hours of my TZ will give me one day later of the actual date.
For example: if my TZ is Asia/Manila I supposed to have UTC+8. If I execute the playbook earlier than 8:00am in Ansible Tower, the date will follow to what was in UTC+0. It took me a while until I found this case. It let me use the date option '-d \"+8 hours\" +%F'. Now it gives me the exact date that I wanted.
Below is the variable I set in my playbook:
vars:
cur_target_wd: "{{ lookup('pipe','date -d \"+8 hours\" +%Y/%m-%b/%d-%a') }}"
That will give me the value of "cur_target_wd = 2020/05-May/28-Thu" even I run it earlier than 8:00am now.

Resources