Ansible Import Vs Include…What’s The Real Difference?
There are three module types that can be either imported or included which are: import/include_task, import/include_role, or import/include_playbook.
While these are mostly the same there are some subtle differences…so subtle in fact that I couldn’t really figure out what the difference was, so why not share what I’ve learned 🙂
So the textbook difference between the two is that:
Import is static. This means they are fully loaded before playbook execution begins.
Include is dynamic. This means they are parsed during execution as they are reached.
I’m not sure how this reads to you, but my understanding was that variables and the like would be interpreted strangely for imports…kind of like whatever the value of a variable was at load would somehow dictate their inclusion or reaction…which was wrong. Here’s a playbook I wrote to try and test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # playbook.yml --- - name: A demo on using include vs import in Ansible with incorrect info hosts: localhost gather_facts: false vars: testvar: "Initial Value" # Initial value for the variable tasks: - name: Display initial testvar value ansible.builtin.debug: msg: "Before import_tasks and include_tasks: testvar is {{ testvar }}" - name: Use import_tasks (static) import_tasks: tasks/import-tasks.yml - name: Update testvar to "Updated Value" set_fact: testvar: "Updated Value" - name: Use include_tasks (dynamic) include_tasks: tasks/include-tasks.yml - name: Re-run import_tasks after update import_tasks: tasks/import-tasks.yml |
Here are the two task files it calls(really they are just spitting out debug info…nothing special here):
1 2 3 4 5 | # tasks/import-tasks.yml - name: Import Task Display testvar if it is "Updated Value" ansible.builtin.debug: msg: "Import Task: testvar is {{ testvar }}" when: testvar == "Updated Value" |
1 2 3 4 5 | # tasks/include-tasks.yml - name: Include Task Display testvar if it is "Updated Value" ansible.builtin.debug: msg: "Include Task: testvar is {{ testvar }}" when: testvar == "Updated Value" |
I was thinking that in the above example it wouldn’t run any of the imports(as at runtime the variable would have been set incorrectly). Take a look at the job output, though:
1 2 3 4 5 6 7 8 9 10 11 12 | PLAY [A demo on using include vs import in Ansible with incorrect info] ******** TASK [Display initial testvar value] ******************************************* ok: [localhost] => { "msg": "Before import_tasks and include_tasks: testvar is Initial Value" } TASK [Import Task Display testvar if it is "Updated Value"] ******************** skipping: [localhost] TASK [Update testvar to "Updated Value"] *************************************** ok: [localhost] TASK [Use include_tasks (dynamic)] ********************************************* included: /runner/project/tasks/include-tasks.yml for localhost TASK [Include Task Display testvar if it is "Updated Value"] ******************* |
I tried all kinds of variations, but no matter what, I couldn’t get it to fail. It ignored the first import as one would expect, but when I reset the variable it went ahead and ran the second import…So, I surrender trying to make it functionally display their differences. There are a few things that are definetly different as per this documentation:
Using include* does have some limitations when compared to import* statements:
Using import* can also have some limitations when compared to dynamic includes:
Looking at the plus’ and minus’ of each, just go with include by default.
Most of the shortcomings of include are things I never run into, so not really a problem.
The shortcomings of import are much more impactful: not being able to use it with loops and losing inventory source variables.
Conclusion
In short, use include and just ignore the import stuff.
If you have any other topics of interest, let me know. If you would tweak or tune this, drop me a note on that too.
Good luck out there; happy automating, and happy including!