How to Include/Import Playbooks in Ansible + Examples


Percy Grunwald's Profile Picture

Written by Percy Grunwald

— Last Updated February 1, 2024

Ansible Course: Productive with Ansible (2024)
Ansible Course: Productive with Ansible (2024)
Go from Ansible beginner to Ansible pro with this full video course.

You can include playbooks inside other playbooks by using the import_playbook directive. There are a number of reasons you might want to move playbooks into separate files when using Ansible:

  1. Refactoring - chop up unfeasibly large playbooks to make the whole thing easier to read, or reduce repetition (DRY) by putting common plays into a file that can be imported by multiple playbooks
  2. Conditionally including playbooks - you may wish to have certain plays run conditionally based on some variable

How to import a playbook

Let’s say that there are some common roles that you would like to include across all your different server types. You could create a common_roles.yml playbook like this:

---
# ./common_roles.yml

- name: install common roles
  hosts: "{{ server_type }}"
  roles:
    - users
    - ntp
    - python
    - git
    - aws-cli

And then include it in your other playbooks to reduce repetition and keep your playbooks DRY:

---
# ./web.yml

- import_playbook: common_roles.yml

- name: configure web servers
  hosts: web
  roles:
    - nginx
    - my_site
---
# ./database.yml

- import_playbook: common_roles.yml

- name: configure database servers
  hosts: database
  roles:
    - postgres

If you ever need to add another common role, you just need to update a single playbook.

Note that I have set hosts: "{{ server_type }}" in the common_roles.yml playbook to limit the hosts that it runs against. For this to work you will need to set the server_type variable when calling ansible-playbook:

$ ansible-playbook web.yml --extra-vars "server_type=web"

$ ansible-playbook database.yml --extra-vars "server_type=database"

There are other ways to achieve this, but this is the best way I could think of. Another way would be to use the --limit option, but I think this is more fragile and less expressive.

How to import a playbook conditionally

Extending the example from above, let’s say that you have some roles that you would like to include only if the env is staging, such as some tools to help debug your live application. An example playbook might look like this:

---
# ./staging_roles.yml

- name: install roles for staging only
  hosts: "{{ env }}_{{ server_type }}"
  roles:
    - debug-tools

You can then update your other playbooks to conditionally include the staging_roles.yml playbook using the when keyword:

---
# ./web.yml

- import_playbook: common_roles.yml

- import_playbook: staging_roles.yml
  when: env == 'staging'

- name: configure web servers
  hosts: "{{ env }}_web"
  roles:
    - nginx
    - my_site
---
# ./database.yml

- import_playbook: common_roles.yml

- import_playbook: staging_roles.yml
  when: env == 'staging'

- name: configure database servers
  hosts: "{{ env }}_database"
  roles:
    - postgres

Note that I have included an env variable here to further limit the servers the playbooks run against and you would need to add this to your command line call:

$ ansible-playbook web.yml --extra-vars "env=staging server_type=web"

$ ansible-playbook database.yml --extra-vars "env=staging server_type=database"

How to import a playbook with dynamic filename

Further extending the examples from above, you could refactor your playbooks to an extreme level: create a master.yml and include your server-specific playbooks using import_playbook and the server_type variable:

---
# ./master.yml

- import_playbook: common_roles.yml

- import_playbook: staging_roles.yml
  when: env == 'staging'

- import_playbook: "{{ server_type }}.yml"

You can modify the server-specific playbooks to contain only a single play each:

---
# ./web.yml

- name: configure web servers
  hosts: "{{ env }}_web"
  roles:
    - nginx
    - my_site
---
# ./database.yml

- name: configure database servers
  hosts: "{{ env }}_database"
  roles:
    - postgres

Now you can configure both the web and database servers using the single master.yml playbook, using the env and server_type variables to specify which servers you would like to configure:

$ ansible-playbook master.yml --extra-vars "env=staging server_type=web"

$ ansible-playbook master.yml --extra-vars "env=staging server_type=database"

This is a pretty extreme example, but I think it demonstrates the power of Ansible when it comes to refactoring playbooks with import_playbook.

Do you have any other use cases or examples that I didn’t cover? Hit me up in the comments and let’s discuss! 😃

Comment & Share