Ansible
ansible_date_time
troubleshooting
automation
error_handling

'ansible_date_time' is undefined

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

ansible_date_time is a built-in Ansible fact that provides date and time information from the target host. The error 'ansible_date_time' is undefined means Ansible facts were not collected before the task that references this variable. This happens when gather_facts: false is set in the play, when the task runs on a host that was not included in fact gathering, or when fact caching is misconfigured.

What ansible_date_time Contains

ansible_date_time is a dictionary populated during fact gathering with keys like date, time, iso8601, epoch, year, month, day, hour, minute, and second.

yaml
1# View the full ansible_date_time dictionary
2- hosts: all
3  tasks:
4    - name: Display date time facts
5      debug:
6        var: ansible_date_time
7
8# Output:
9# ansible_date_time:
10#   date: "2025-03-02"
11#   day: "02"
12#   epoch: "1740902400"
13#   hour: "14"
14#   iso8601: "2025-03-02T14:00:00Z"
15#   minute: "00"
16#   month: "03"
17#   second: "00"
18#   time: "14:00:00"
19#   weekday: "Sunday"
20#   year: "2025"

Cause 1: gather_facts: false

The most common cause. When you disable fact gathering, no ansible_* variables are available.

yaml
1# BROKEN — facts are not collected
2- hosts: webservers
3  gather_facts: false
4  tasks:
5    - name: Create log with timestamp
6      copy:
7        content: "Deployed at {{ ansible_date_time.iso8601 }}"
8        dest: /var/log/deploy.log
9# ERROR: 'ansible_date_time' is undefined

Fix: Enable fact gathering

yaml
1# Option 1: Remove gather_facts (defaults to true)
2- hosts: webservers
3  tasks:
4    - name: Create log with timestamp
5      copy:
6        content: "Deployed at {{ ansible_date_time.iso8601 }}"
7        dest: /var/log/deploy.log
8
9# Option 2: Explicitly set gather_facts: true
10- hosts: webservers
11  gather_facts: true
12  tasks:
13    - name: Create log with timestamp
14      copy:
15        content: "Deployed at {{ ansible_date_time.iso8601 }}"
16        dest: /var/log/deploy.log

Fix: Gather facts manually with setup module

If you need gather_facts: false for performance but still need date/time:

yaml
1- hosts: webservers
2  gather_facts: false
3  tasks:
4    - name: Gather only date/time facts
5      setup:
6        filter: ansible_date_time
7
8    - name: Use the date
9      debug:
10        msg: "Today is {{ ansible_date_time.date }}"

The filter parameter limits fact collection to matching keys, which is faster than gathering all facts.

Cause 2: Using ansible_date_time in a Role or Include Before Facts Are Gathered

yaml
1# BROKEN — role runs before facts are ready
2- hosts: all
3  gather_facts: false
4  pre_tasks:
5    - name: This runs before fact gathering
6      debug:
7        msg: "{{ ansible_date_time.date }}"
8  # ERROR: undefined

Fix by ensuring fact gathering runs first, or by placing the setup task in pre_tasks:

yaml
1- hosts: all
2  gather_facts: false
3  pre_tasks:
4    - name: Gather facts first
5      setup:
6
7    - name: Now safe to use
8      debug:
9        msg: "{{ ansible_date_time.date }}"

Cause 3: Delegated Tasks

When you delegate a task to a different host, the facts available are from the delegated host, not the original. If the delegated host has not had facts gathered, ansible_date_time is undefined.

yaml
1- hosts: webservers
2  tasks:
3    - name: Run on localhost
4      debug:
5        msg: "{{ ansible_date_time.date }}"
6      delegate_to: localhost
7      # May fail if localhost facts were not gathered

Fix with delegate_facts or gather facts on localhost separately:

yaml
1- hosts: localhost
2  gather_facts: true
3  tasks: []
4
5- hosts: webservers
6  tasks:
7    - name: Use localhost facts via hostvars
8      debug:
9        msg: "{{ hostvars['localhost'].ansible_date_time.date }}"

Alternative: Use Jinja2 now() Filter

Ansible 2.8+ provides the now() function, which does not depend on fact gathering:

yaml
1- hosts: all
2  gather_facts: false
3  tasks:
4    - name: Get current time without facts
5      debug:
6        msg: "{{ now(utc=true).isoformat() }}"
7    # Output: 2025-03-02T14:00:00.000000+00:00
8
9    - name: Formatted date
10      debug:
11        msg: "{{ now().strftime('%Y-%m-%d %H:%M') }}"
12    # Output: 2025-03-02 14:00

now() runs on the controller, not the target host. If you need the target host's time, you must use ansible_date_time with fact gathering enabled.

Cause 4: Fact Caching Issues

If you use fact caching (fact_caching = jsonfile or redis in ansible.cfg) and the cache is stale or empty, facts may be missing.

ini
1# ansible.cfg
2[defaults]
3fact_caching = jsonfile
4fact_caching_connection = /tmp/ansible_facts
5fact_caching_timeout = 86400
yaml
1# Force fresh facts even with caching enabled
2- hosts: all
3  gather_facts: true
4  tasks:
5    - name: Refresh facts
6      setup:
7      tags: [always]

Common Pitfalls

  • Assuming facts are always available: gather_facts: false disables all ansible_* variables, not just hardware facts. If your playbook sets gather_facts: false for speed, you must explicitly gather the facts you need with the setup module.
  • Using ansible_date_time for the controller's time: ansible_date_time reflects the target host's clock, which may differ from the Ansible controller. Use now() for controller time or ansible_date_time for target time.
  • Time zone differences: ansible_date_time uses the target host's configured timezone. Two hosts in different timezones will return different values. Use ansible_date_time.iso8601 (UTC) for consistency.
  • Stale cached facts: With fact caching enabled, ansible_date_time may return the cached time from a previous run, not the current time. Disable caching or force refresh for time-sensitive tasks.
  • now() vs ansible_date_time: now() is evaluated at template render time on the controller. ansible_date_time is collected during fact gathering on the target. They serve different purposes and return different times if the controller and target clocks differ.

Summary

  • ansible_date_time requires fact gathering — set gather_facts: true or run the setup module manually
  • Use setup: filter=ansible_date_time to gather only date/time facts for better performance
  • For controller-side time without fact gathering, use the now() Jinja2 function (Ansible 2.8+)
  • Delegated tasks use the delegated host's facts — gather facts on that host first or use hostvars
  • ansible_date_time reflects the target host's timezone; use .iso8601 for UTC consistency

Course illustration
Course illustration

All Rights Reserved.