diff --git a/ansible/roles/aodh/tasks/pull.yml b/ansible/roles/aodh/tasks/pull.yml
index 5891a4555e..53f9c5fda1 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 3de76d97e8..53f9c5fda1 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 7fcba33d36..53f9c5fda1 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 89834e3a37..53f9c5fda1 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 25898b4f5b..53f9c5fda1 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 f680c9578f..53f9c5fda1 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 629c0d1aa9..53f9c5fda1 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 da2c5c968b..53f9c5fda1 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 7f7fefbcc3..53f9c5fda1 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 eb3e4663cf..53f9c5fda1 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 1489dcc0de..53f9c5fda1 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 02435c5f59..53f9c5fda1 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 1c703daf01..53f9c5fda1 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 856c12d40a..53f9c5fda1 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 1de70396f6..53f9c5fda1 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 a86d810f80..53f9c5fda1 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 5b0a5ddc54..53f9c5fda1 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 be34f3a1c7..53f9c5fda1 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 fe202b7b99..53f9c5fda1 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 d261b997d2..53f9c5fda1 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 5cbbe24ff1..53f9c5fda1 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 354d89553b..53f9c5fda1 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 e42e0ade56..53f9c5fda1 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 cc808b8397..53f9c5fda1 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 42b3e70d4d..53f9c5fda1 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 b4827d257a..53f9c5fda1 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 af22338414..53f9c5fda1 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 eab11dc9bc..53f9c5fda1 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 0d456e5dde..53f9c5fda1 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 d82cd7cc7a..53f9c5fda1 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 e241ded0a6..53f9c5fda1 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 be6f1e1c0f..53f9c5fda1 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 bd10b20396..53f9c5fda1 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 21756b4cb6..53f9c5fda1 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 97ee460c42..53f9c5fda1 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 78b4b72cda..53f9c5fda1 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 e60c735fda..53f9c5fda1 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 3f2090e7dc..53f9c5fda1 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 4a8bd38dc9..bf8c23a07a 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 65b393cfe5..53f9c5fda1 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 52bb8fa43a..53f9c5fda1 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 180006ff58..53f9c5fda1 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 47207b65a0..53f9c5fda1 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 79ae0b297d..53f9c5fda1 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 a0ca8d0ed5..53f9c5fda1 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 f82b749393..53f9c5fda1 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 af0f82dfa0..53f9c5fda1 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 682399f378..53f9c5fda1 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 5e08a9b25a..53f9c5fda1 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 e46ca59a7e..53f9c5fda1 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 04533f4b25..53f9c5fda1 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 f2c70ee607..53f9c5fda1 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 0000000000..240ea57cbe
--- /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 a98765e693..53f9c5fda1 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 2506be77e5..53f9c5fda1 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 a2eb29f991..53f9c5fda1 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 15b4483b9b..53f9c5fda1 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 a9dfe56b79..53f9c5fda1 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 d40e3709e0..53f9c5fda1 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 11b3535b39..53f9c5fda1 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 39218b6aae..53f9c5fda1 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 de7b7f4d83..53f9c5fda1 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 7a02e4fe91..53f9c5fda1 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 bc827a389d..53f9c5fda1 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 0000000000..ad0d276ccc
--- /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.