The Problem
Your playbook uses import_tasks to statically include task files, but it fails at parse time:
ERROR! Could not locate file in lookup: tasks/required.ymlOr:
ERROR! Error while reading file tasks/vars.yml: 'my_var' is undefinedOr when using variables in the filename:
ERROR! 'task_file' is undefinedUnlike include_tasks, import_tasks errors occur at parse time, before any tasks run.
Why This Happens
import_tasks errors occur because:
File not found at parse time - Ansible validates imports before running tasks.
Variables in filename - import_tasks can't use variables (parse-time only).
Undefined variables in imported file - Variables must exist when playbook is parsed.
Path resolution issues - Wrong relative path to task file.
Task file syntax errors - Invalid YAML in the imported file.
Understanding import_tasks
import_tasks statically includes tasks at playbook parse time:
```yaml # main.yml - hosts: webservers tasks: - import_tasks: tasks/setup.yml
# tasks/setup.yml is parsed when main.yml is parsed # All tasks become part of main.yml before execution ```
Key differences from include_tasks: - Parse-time evaluation (not runtime) - Cannot use variables in filename - Faster execution (pre-processed) - Tasks inherit play context directly
Diagnosing the Issue
Check file exists:
ls -la tasks/setup.yml
cat tasks/setup.ymlValidate playbook syntax:
ansible-playbook playbook.yml --syntax-checkCheck for variable usage in import:
grep -n "import_tasks.*{{" playbook.yml
# This pattern indicates problematic variable usageThe Fix
Fix 1: Use Static Filename Only
import_tasks requires static filenames:
```yaml # WRONG - variable in filename (fails at parse time) - import_tasks: "tasks/{{ task_type }}.yml" # Error: task_type undefined at parse time
# CORRECT - static filename only - import_tasks: tasks/setup.yml ```
For dynamic filenames, use include_tasks instead:
# Use include_tasks for dynamic includes
- include_tasks: "tasks/{{ task_type }}.yml"
when: task_type is definedFix 2: Fix File Path Issues
Use correct relative paths:
# playbook.yml in /ansible/playbooks/
- import_tasks: ../tasks/setup.yml # Relative to playbookUse playbook_dir for reliable paths:
- import_tasks: "{{ playbook_dir }}/tasks/setup.yml"
# playbook_dir is defined at parse time, safe for importVerify file location:
# From playbook directory
cat tasks/setup.yml
ansible-playbook playbook.yml --syntax-checkFix 3: Handle Variables in Imported Files
Variables must be defined when file is imported:
```yaml # WRONG - variable undefined at parse time - hosts: webservers tasks: - import_tasks: tasks/config.yml # config.yml uses app_port which isn't defined
# tasks/config.yml - name: Configure template: src: app.conf.j2 dest: /etc/app.conf # Uses {{ app_port }} in template ```
# CORRECT - define vars before import
- hosts: webservers
vars:
app_port: 8080
tasks:
- import_tasks: tasks/config.ymlFix 4: Task File Syntax Validation
Check imported file syntax separately:
ansible-playbook tasks/setup.yml --syntax-checkOr validate all YAML:
yamllint tasks/*.ymlFix 5: Import Tasks with Tags
Tags work with import_tasks:
```yaml - import_tasks: tasks/setup.yml tags: - setup - install
# All tasks in setup.yml inherit these tags ```
Run specific imports:
ansible-playbook playbook.yml --tags setupFix 6: Conditional Import Limitations
import_tasks doesn't support runtime conditions:
# WRONG - condition doesn't skip import at parse time
- import_tasks: tasks/production.yml
when: env == 'production'
# File is still imported at parse time, just tasks skipped laterFor true conditional imports, use include_tasks:
- include_tasks: tasks/production.yml
when: env == 'production'
# File only included if condition is true at runtimeFix 7: Import vs Include Comparison
When to use each:
```yaml # import_tasks - for static, predictable includes - import_tasks: tasks/common.yml # Always included, known file # Benefits: # - Faster (pre-processed) # - Tasks show in --list-tasks # - Better for roles
# include_tasks - for dynamic, conditional includes - include_tasks: "tasks/{{ os_family }}.yml" # Benefits: # - Dynamic filenames # - Runtime evaluation # - Can loop over includes ```
Fix 8: Import in Roles
Role task imports use import_tasks:
```yaml # roles/myrole/tasks/main.yml - import_tasks: install.yml - import_tasks: configure.yml - import_tasks: service.yml
# All imported at role parse time ```
For role task files with variables:
```yaml # Use include_vars first if tasks need vars - include_vars: "{{ item }}" loop: - defaults.yml - vars.yml
- import_tasks: configure.yml
`
Fix 9: Handlers Import
Import handlers statically:
```yaml - hosts: webservers tasks: - import_tasks: tasks/main.yml
handlers: - import_tasks: handlers/main.yml ```
Verifying the Fix
Test import_tasks:
```yaml # test_import.yml - hosts: localhost gather_facts: no vars: test_var: imported tasks: - import_tasks: tasks/test_sub.yml
# tasks/test_sub.yml (create this file) - name: Imported task debug: msg: "test_var is {{ test_var }}" ```
Validate syntax:
ansible-playbook test_import.yml --syntax-checkList all tasks (imports show):
ansible-playbook test_import.yml --list-tasks
# Imported tasks appear in listRun:
ansible-playbook test_import.yml -vPrevention
Create pre-flight validation:
# Script to validate imports before running
#!/bin/bash
for playbook in playbooks/*.yml; do
echo "Checking $playbook"
ansible-playbook "$playbook" --syntax-check || exit 1
doneUse ansible-lint:
ansible-lint playbook.yml
# Catches many import issues