diff --git a/ansible/roles/aodh/tasks/pull.yml b/ansible/roles/aodh/tasks/pull.yml
index 5891a4555e9654395225365e5b281fe12e7719fb..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/aodh/tasks/pull.yml
+++ b/ansible/roles/aodh/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling aodh images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ aodh_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/barbican/tasks/pull.yml b/ansible/roles/barbican/tasks/pull.yml
index 3de76d97e84df0c645d1dceeeed19e14a6a6f294..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/barbican/tasks/pull.yml
+++ b/ansible/roles/barbican/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling barbican images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ barbican_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/blazar/tasks/pull.yml b/ansible/roles/blazar/tasks/pull.yml
index 7fcba33d36b547d3576f695b2b4cef9973da1dbd..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/blazar/tasks/pull.yml
+++ b/ansible/roles/blazar/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling blazar images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ blazar_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/ceilometer/tasks/pull.yml b/ansible/roles/ceilometer/tasks/pull.yml
index 89834e3a3738e29acca68d70b85eda61273f23af..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/ceilometer/tasks/pull.yml
+++ b/ansible/roles/ceilometer/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling ceilometer images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ ceilometer_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/chrony/tasks/pull.yml b/ansible/roles/chrony/tasks/pull.yml
index 25898b4f5b4f0885adeedfac04fa90e0feae37b2..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/chrony/tasks/pull.yml
+++ b/ansible/roles/chrony/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling chrony images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ chrony_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/cinder/tasks/pull.yml b/ansible/roles/cinder/tasks/pull.yml
index f680c9578fba4c3fe9f098ad5b805d54ec495726..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/cinder/tasks/pull.yml
+++ b/ansible/roles/cinder/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling cinder images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ cinder_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/cloudkitty/tasks/pull.yml b/ansible/roles/cloudkitty/tasks/pull.yml
index 629c0d1aa9221f0d8751ccc748ee1345adc7c778..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/cloudkitty/tasks/pull.yml
+++ b/ansible/roles/cloudkitty/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling cloudkitty images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ cloudkitty_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/collectd/tasks/pull.yml b/ansible/roles/collectd/tasks/pull.yml
index da2c5c968b4e23eb0a21c69a6511a83d979c457a..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/collectd/tasks/pull.yml
+++ b/ansible/roles/collectd/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling collectd image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ collectd_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/common/tasks/pull.yml b/ansible/roles/common/tasks/pull.yml
index 7f7fefbcc392033001a18fdb5785ab00a82209a4..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/common/tasks/pull.yml
+++ b/ansible/roles/common/tasks/pull.yml
@@ -1,10 +1,3 @@
 ---
-- name: Pulling common images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - item.value | service_enabled_and_mapped_to_host
-  with_dict: "{{ common_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/cyborg/tasks/pull.yml b/ansible/roles/cyborg/tasks/pull.yml
index eb3e4663cf8525a772e3afb73c361fbe152f4ff0..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/cyborg/tasks/pull.yml
+++ b/ansible/roles/cyborg/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling cyborg images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ cyborg_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/designate/tasks/pull.yml b/ansible/roles/designate/tasks/pull.yml
index 1489dcc0de2c8df7a39fbb3bce4bbd90f1113a6c..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/designate/tasks/pull.yml
+++ b/ansible/roles/designate/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling designate images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ designate_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/elasticsearch/tasks/pull.yml b/ansible/roles/elasticsearch/tasks/pull.yml
index 02435c5f59eeb0d9df162d5a0525f2e1f46b2795..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/elasticsearch/tasks/pull.yml
+++ b/ansible/roles/elasticsearch/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling elasticsearch images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ elasticsearch_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/etcd/tasks/pull.yml b/ansible/roles/etcd/tasks/pull.yml
index 1c703daf01bffdd28ac6c88c1869dd3a29a83a41..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/etcd/tasks/pull.yml
+++ b/ansible/roles/etcd/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling etcd image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - item.value.enabled | bool
-    - inventory_hostname in groups[item.value.group]
-  with_dict: "{{ etcd_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/freezer/tasks/pull.yml b/ansible/roles/freezer/tasks/pull.yml
index 856c12d40a38ba403ee6473a53dfe6ccd4ac34d0..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/freezer/tasks/pull.yml
+++ b/ansible/roles/freezer/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling freezer images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ freezer_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/glance/tasks/pull.yml b/ansible/roles/glance/tasks/pull.yml
index 1de70396f6c7c343f1f75ecd5701fe8391f7afd6..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/glance/tasks/pull.yml
+++ b/ansible/roles/glance/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling glance images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - item.value.host_in_groups | bool
-    - item.value.enabled | bool
-  with_dict: "{{ glance_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/gnocchi/tasks/pull.yml b/ansible/roles/gnocchi/tasks/pull.yml
index a86d810f801ae5f40a22ff4e3a77ceada1e7bcec..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/gnocchi/tasks/pull.yml
+++ b/ansible/roles/gnocchi/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling gnocchi images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ gnocchi_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/grafana/tasks/pull.yml b/ansible/roles/grafana/tasks/pull.yml
index 5b0a5ddc545436bd5c9a235c019c2dcbc800ce86..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/grafana/tasks/pull.yml
+++ b/ansible/roles/grafana/tasks/pull.yml
@@ -1,8 +1,3 @@
 ---
-- name: Pulling grafana image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ grafana_image_full }}"
-  when: inventory_hostname in groups['grafana']
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/hacluster/tasks/pull.yml b/ansible/roles/hacluster/tasks/pull.yml
index be34f3a1c7baf90bf6fa507e789e6da50d9097fe..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/hacluster/tasks/pull.yml
+++ b/ansible/roles/hacluster/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling hacluster images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ hacluster_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/haproxy/tasks/pull.yml b/ansible/roles/haproxy/tasks/pull.yml
index fe202b7b992f79ba0624849e178f2567ee455bab..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/haproxy/tasks/pull.yml
+++ b/ansible/roles/haproxy/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling haproxy images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ haproxy_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/heat/tasks/pull.yml b/ansible/roles/heat/tasks/pull.yml
index d261b997d2e784978b7c4cf1de04b2f577eed2b5..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/heat/tasks/pull.yml
+++ b/ansible/roles/heat/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling heat images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ heat_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/horizon/tasks/pull.yml b/ansible/roles/horizon/tasks/pull.yml
index 5cbbe24ff1ddf887e5f89ebb20d7e4c1a343e067..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/horizon/tasks/pull.yml
+++ b/ansible/roles/horizon/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling horizon images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ horizon_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/influxdb/tasks/pull.yml b/ansible/roles/influxdb/tasks/pull.yml
index 354d89553b52726e4d6dff5137df4ddb3145240d..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/influxdb/tasks/pull.yml
+++ b/ansible/roles/influxdb/tasks/pull.yml
@@ -1,8 +1,3 @@
 ---
-- name: Pulling influxdb image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ influxdb_image_full }}"
-  when: inventory_hostname in groups['influxdb']
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/ironic/tasks/pull.yml b/ansible/roles/ironic/tasks/pull.yml
index e42e0ade56aeb7c38e878e6f6089ca5b17a7e7af..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/ironic/tasks/pull.yml
+++ b/ansible/roles/ironic/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling ironic images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ ironic_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/iscsi/tasks/pull.yml b/ansible/roles/iscsi/tasks/pull.yml
index cc808b8397ad55090153bae21460f039805c4d41..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/iscsi/tasks/pull.yml
+++ b/ansible/roles/iscsi/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling iscsi images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ iscsi_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/kafka/tasks/pull.yml b/ansible/roles/kafka/tasks/pull.yml
index 42b3e70d4d3403fd36cda397691f9ba6f2b462c4..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/kafka/tasks/pull.yml
+++ b/ansible/roles/kafka/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling kafka images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ kafka_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/keystone/tasks/pull.yml b/ansible/roles/keystone/tasks/pull.yml
index b4827d257a3619150c55ca444e6744994da9df59..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/keystone/tasks/pull.yml
+++ b/ansible/roles/keystone/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling keystone images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ keystone_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/kibana/tasks/pull.yml b/ansible/roles/kibana/tasks/pull.yml
index af223384143c97a06e4ba88d9d357bf45eeb14ba..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/kibana/tasks/pull.yml
+++ b/ansible/roles/kibana/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling Kibana image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ kibana_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/kuryr/tasks/pull.yml b/ansible/roles/kuryr/tasks/pull.yml
index eab11dc9bc137a4f3781130da2ab34cb3619c6c4..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/kuryr/tasks/pull.yml
+++ b/ansible/roles/kuryr/tasks/pull.yml
@@ -1,7 +1,3 @@
 ---
-- name: Pulling kuryr image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ kuryr_image_full }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/magnum/tasks/pull.yml b/ansible/roles/magnum/tasks/pull.yml
index 0d456e5dde8d412b25ac6876f8d47144825f2175..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/magnum/tasks/pull.yml
+++ b/ansible/roles/magnum/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling magnum images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ magnum_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/manila/tasks/pull.yml b/ansible/roles/manila/tasks/pull.yml
index d82cd7cc7a62a4db705f39ea3e9740b968b40fe5..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/manila/tasks/pull.yml
+++ b/ansible/roles/manila/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling manila images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ manila_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/mariadb/tasks/pull.yml b/ansible/roles/mariadb/tasks/pull.yml
index e241ded0a674f4a49af0eec26f9b5c445081bd9f..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/mariadb/tasks/pull.yml
+++ b/ansible/roles/mariadb/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling mariadb image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ mariadb_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/masakari/tasks/pull.yml b/ansible/roles/masakari/tasks/pull.yml
index be6f1e1c0f624cb5aaa471893e233a2b0feaf2a2..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/masakari/tasks/pull.yml
+++ b/ansible/roles/masakari/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling masakari images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ masakari_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/memcached/tasks/pull.yml b/ansible/roles/memcached/tasks/pull.yml
index bd10b20396bbfe28dcee906f60f55e5095868e4c..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/memcached/tasks/pull.yml
+++ b/ansible/roles/memcached/tasks/pull.yml
@@ -1,12 +1,3 @@
 ---
-- name: Pulling memcached image
-  vars:
-    service: "{{ memcached_services.memcached }}"
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ service.image }}"
-  when:
-    - inventory_hostname in groups[service.group]
-    - service.enabled | bool
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/mistral/tasks/pull.yml b/ansible/roles/mistral/tasks/pull.yml
index 21756b4cb6865d3a477cf3b711ff97a84f024e76..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/mistral/tasks/pull.yml
+++ b/ansible/roles/mistral/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling mistral images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  with_dict: "{{ mistral_services }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/monasca/tasks/pull.yml b/ansible/roles/monasca/tasks/pull.yml
index 97ee460c421563e7ae38921167c3a19236fff60c..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/monasca/tasks/pull.yml
+++ b/ansible/roles/monasca/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling monasca images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ monasca_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/multipathd/tasks/pull.yml b/ansible/roles/multipathd/tasks/pull.yml
index 78b4b72cdada9ce1cd88c486784cef337b60f54a..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/multipathd/tasks/pull.yml
+++ b/ansible/roles/multipathd/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling multipathd image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ multipathd_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/murano/tasks/pull.yml b/ansible/roles/murano/tasks/pull.yml
index e60c735fda961df4a96243551abfbb515797ec43..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/murano/tasks/pull.yml
+++ b/ansible/roles/murano/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling murano images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ murano_services  }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/neutron/tasks/pull.yml b/ansible/roles/neutron/tasks/pull.yml
index 3f2090e7dccaea6b64dd9953e87992b226ce3ed4..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/neutron/tasks/pull.yml
+++ b/ansible/roles/neutron/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling neutron images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - item.value.enabled | bool
-    - item.value.host_in_groups | bool
-  with_dict: "{{ neutron_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/nova-cell/defaults/main.yml b/ansible/roles/nova-cell/defaults/main.yml
index 4a8bd38dc98724d694967c1b2396e97b6e3c11ad..bf8c23a07a171fb233c0222ab15593409776ec67 100644
--- a/ansible/roles/nova-cell/defaults/main.yml
+++ b/ansible/roles/nova-cell/defaults/main.yml
@@ -1,6 +1,10 @@
 ---
 project_name: "nova"
 
+# NOTE(yoctozepto): we need this for the nova-cell role because this role's
+# vars prefix does not match "{{ project_name }}"
+kolla_role_name: "nova_cell"
+
 nova_cell_services:
   nova-libvirt:
     container_name: nova_libvirt
diff --git a/ansible/roles/nova-cell/tasks/pull.yml b/ansible/roles/nova-cell/tasks/pull.yml
index 65b393cfe5215e391b0ddd2ef182023a8e7ff677..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/nova-cell/tasks/pull.yml
+++ b/ansible/roles/nova-cell/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling nova images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ nova_cell_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/nova/tasks/pull.yml b/ansible/roles/nova/tasks/pull.yml
index 52bb8fa43ad6ffccab36ee3652191085a62a2323..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/nova/tasks/pull.yml
+++ b/ansible/roles/nova/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling nova images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ nova_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/octavia/tasks/pull.yml b/ansible/roles/octavia/tasks/pull.yml
index 180006ff5843c7a539d9093b1387216bdbab35e3..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/octavia/tasks/pull.yml
+++ b/ansible/roles/octavia/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling octavia images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ octavia_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/openvswitch/tasks/pull.yml b/ansible/roles/openvswitch/tasks/pull.yml
index 47207b65a05714105c69d534b1200a9dda32b1c0..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/openvswitch/tasks/pull.yml
+++ b/ansible/roles/openvswitch/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling Openvswitch images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - item.value.enabled | bool
-    - item.value.host_in_groups | bool
-  with_dict: "{{ openvswitch_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/ovn/tasks/pull.yml b/ansible/roles/ovn/tasks/pull.yml
index 79ae0b297d3aeeea0baf4c9d2063d8d7e89f7a9d..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/ovn/tasks/pull.yml
+++ b/ansible/roles/ovn/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling OVN images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ ovn_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/ovs-dpdk/tasks/pull.yml b/ansible/roles/ovs-dpdk/tasks/pull.yml
index a0ca8d0ed591e6f0fcb6c6b7591cd185e560976e..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/ovs-dpdk/tasks/pull.yml
+++ b/ansible/roles/ovs-dpdk/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling ovs-dpdk images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - item.value.enabled | bool
-    - item.value.host_in_groups | bool
-  with_dict: "{{ ovsdpdk_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/placement/tasks/pull.yml b/ansible/roles/placement/tasks/pull.yml
index f82b7493932c36bab81b8e3fdcbcb8900f67a746..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/placement/tasks/pull.yml
+++ b/ansible/roles/placement/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling placement images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ placement_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/prometheus/tasks/pull.yml b/ansible/roles/prometheus/tasks/pull.yml
index af0f82dfa04d2c82901cd88f5e9e720f48cb2280..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/prometheus/tasks/pull.yml
+++ b/ansible/roles/prometheus/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling prometheus images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ prometheus_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/qdrouterd/tasks/pull.yml b/ansible/roles/qdrouterd/tasks/pull.yml
index 682399f378a1c2f395a7da771c7f4265a3a3ff84..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/qdrouterd/tasks/pull.yml
+++ b/ansible/roles/qdrouterd/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling qdrouterd image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ qdrouterd_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/rabbitmq/tasks/pull.yml b/ansible/roles/rabbitmq/tasks/pull.yml
index 5e08a9b25a8a77744e100019bc4e7550b8cd333a..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/rabbitmq/tasks/pull.yml
+++ b/ansible/roles/rabbitmq/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling rabbitmq image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ rabbitmq_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/redis/tasks/pull.yml b/ansible/roles/redis/tasks/pull.yml
index e46ca59a7ee963752db279e37e3ff2e0a9732ba9..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/redis/tasks/pull.yml
+++ b/ansible/roles/redis/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling redis images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ redis_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/sahara/tasks/pull.yml b/ansible/roles/sahara/tasks/pull.yml
index 04533f4b25192cd5731850708bf4b559a84e3ad6..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/sahara/tasks/pull.yml
+++ b/ansible/roles/sahara/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling sahara images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ sahara_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/senlin/tasks/pull.yml b/ansible/roles/senlin/tasks/pull.yml
index f2c70ee60759fc239da42d8eff25d624f346f782..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/senlin/tasks/pull.yml
+++ b/ansible/roles/senlin/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling senlin images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ senlin_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/service-images-pull/tasks/main.yml b/ansible/roles/service-images-pull/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..240ea57cbe4467ca7e164265fc683c73e12dc8a7
--- /dev/null
+++ b/ansible/roles/service-images-pull/tasks/main.yml
@@ -0,0 +1,12 @@
+---
+- name: "{{ kolla_role_name | default(project_name) }} | Pull images"
+  vars:
+    service: "{{ item.value }}"
+  become: true
+  kolla_docker:
+    action: "pull_image"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ service.image }}"
+  with_dict: "{{ lookup('vars', (kolla_role_name | default(project_name)) + '_services') | select_services_enabled_and_mapped_to_host }}"
+  loop_control:
+    label: "{{ item.key }}"
diff --git a/ansible/roles/skydive/tasks/pull.yml b/ansible/roles/skydive/tasks/pull.yml
index a98765e693f18b7d7780ce516df30d76b406dc0c..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/skydive/tasks/pull.yml
+++ b/ansible/roles/skydive/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling skydive images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ skydive_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/solum/tasks/pull.yml b/ansible/roles/solum/tasks/pull.yml
index 2506be77e59f26c823dd37e38b9b6ba137c7dfa6..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/solum/tasks/pull.yml
+++ b/ansible/roles/solum/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling solum images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ solum_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/storm/tasks/pull.yml b/ansible/roles/storm/tasks/pull.yml
index a2eb29f991bff020d4fde4449742d5235ca38086..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/storm/tasks/pull.yml
+++ b/ansible/roles/storm/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling storm images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ storm_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/tacker/tasks/pull.yml b/ansible/roles/tacker/tasks/pull.yml
index 15b4483b9b8cd4b100c249898271a7ce7b56107e..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/tacker/tasks/pull.yml
+++ b/ansible/roles/tacker/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling tacker images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - item.value.host_in_groups | bool
-    - item.value.enabled | bool
-  with_dict: "{{ tacker_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/telegraf/tasks/pull.yml b/ansible/roles/telegraf/tasks/pull.yml
index a9dfe56b7903346c45325bfc28e5b478268ba7ed..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/telegraf/tasks/pull.yml
+++ b/ansible/roles/telegraf/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling telegraf image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ telegraf_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/trove/tasks/pull.yml b/ansible/roles/trove/tasks/pull.yml
index d40e3709e0b5f96561009ce6c33e15c7004785ed..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/trove/tasks/pull.yml
+++ b/ansible/roles/trove/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling trove images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ trove_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/vitrage/tasks/pull.yml b/ansible/roles/vitrage/tasks/pull.yml
index 11b3535b3945ea63240c73763d20a6dae394ba1d..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/vitrage/tasks/pull.yml
+++ b/ansible/roles/vitrage/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling vitrage images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ vitrage_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/vmtp/tasks/pull.yml b/ansible/roles/vmtp/tasks/pull.yml
index 39218b6aae284047efb072eda94a6aea8db3ff03..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/vmtp/tasks/pull.yml
+++ b/ansible/roles/vmtp/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling vmtp image
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ vmtp_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/watcher/tasks/pull.yml b/ansible/roles/watcher/tasks/pull.yml
index de7b7f4d8374083ead5d0ffe4de00ea1b40fb1e1..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/watcher/tasks/pull.yml
+++ b/ansible/roles/watcher/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling watcher images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ watcher_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/zookeeper/tasks/pull.yml b/ansible/roles/zookeeper/tasks/pull.yml
index 7a02e4fe918885a8d53e6cf585e34ab840a0ec01..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/zookeeper/tasks/pull.yml
+++ b/ansible/roles/zookeeper/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling zookeeper images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ zookeeper_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/ansible/roles/zun/tasks/pull.yml b/ansible/roles/zun/tasks/pull.yml
index bc827a389dfcce28cec9de12fc11bb15880f0ac3..53f9c5fda16b19934e47f441d035fb9048c2465c 100644
--- a/ansible/roles/zun/tasks/pull.yml
+++ b/ansible/roles/zun/tasks/pull.yml
@@ -1,11 +1,3 @@
 ---
-- name: Pulling zun images
-  become: true
-  kolla_docker:
-    action: "pull_image"
-    common_options: "{{ docker_common_options }}"
-    image: "{{ item.value.image }}"
-  when:
-    - inventory_hostname in groups[item.value.group]
-    - item.value.enabled | bool
-  with_dict: "{{ zun_services }}"
+- import_role:
+    role: service-images-pull
diff --git a/releasenotes/notes/refactor-and-optimise-image-pulling-4346d3c0840ee640.yaml b/releasenotes/notes/refactor-and-optimise-image-pulling-4346d3c0840ee640.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ad0d276cccb744136a6efdc1ff21b18a219eca07
--- /dev/null
+++ b/releasenotes/notes/refactor-and-optimise-image-pulling-4346d3c0840ee640.yaml
@@ -0,0 +1,4 @@
+---
+other:
+  - |
+    Optimised image pulling to avoid looping over disabled services.