diff --git a/tests/run.yml b/tests/run.yml
index 0e5d858e1afb2ccf0691236bfda6511b5654e653..da424d9dcee4d92cdd012d5595df3155ca87d631 100644
--- a/tests/run.yml
+++ b/tests/run.yml
@@ -137,9 +137,32 @@
               dest: ironic-agent.kernel
       when: scenario == "ironic"
 
-    - name: install kolla-ansible
+    - name: ensure /etc/ansible exists
+      file:
+        path: /etc/ansible
+        state: directory
+      become: true
+
+    - name: install kolla-ansible and dependencies
+      vars:
+        # Test latest ansible version on Ubuntu, minimum supported on others.
+        ansible_version_constraint: "{{ '>=2.6' if base_distro == 'ubuntu' else '<2.7' }}"
       pip:
-        name: "{{ kolla_ansible_src_dir }}"
+        name:
+          - "{{ kolla_ansible_src_dir }}"
+          - "ansible{{ ansible_version_constraint }}"
+          - "ara<1.0.0"
+      become: true
+
+    - name: get ARA callback plugin path
+      command: "python -m ara.setup.callback_plugins"
+      changed_when: false
+      register: ara_callback_plugins
+
+    - name: template ansible.cfg
+      template:
+        src: "{{ kolla_ansible_local_src_dir }}/tests/templates/ansible.cfg.j2"
+        dest: /etc/ansible/ansible.cfg
       become: true
 
     - name: copy passwords.yml file
@@ -179,6 +202,10 @@
         need_build_image
       become: true
 
+    - name: Record the running state of the environment as seen by the setup module
+      shell:
+        cmd: ansible all -i {{ kolla_inventory_path }} -e ansible_user={{ ansible_user }} -m setup > /tmp/logs/ansible/initial-setup
+
     # NOTE(mgoddard): We are using the script module here and later to ensure
     # we use the local copy of these scripts, rather than the one on the remote
     # host, which could be checked out to a previous release (in an upgrade
diff --git a/tests/templates/ansible.cfg.j2 b/tests/templates/ansible.cfg.j2
new file mode 100644
index 0000000000000000000000000000000000000000..1285ebf6f6f6d86c928275618d842291e21995c6
--- /dev/null
+++ b/tests/templates/ansible.cfg.j2
@@ -0,0 +1,3 @@
+[defaults]
+callback_plugins = {{ ara_callback_plugins.stdout }}
+host_key_checking = False
diff --git a/tools/setup_gate.sh b/tools/setup_gate.sh
index da984743061d9f3669b8025298211c06c135ee39..1e432b050fb0081138f7b1a126979fb1d3bee2d8 100755
--- a/tools/setup_gate.sh
+++ b/tools/setup_gate.sh
@@ -83,31 +83,6 @@ EOF
     mkdir -p /tmp/logs/build
 }
 
-function setup_ansible {
-    RAW_INVENTORY=/etc/kolla/inventory
-
-    # Test latest ansible version on Ubuntu, minimum supported on others.
-    if [[ $BASE_DISTRO == "ubuntu" ]]; then
-        ANSIBLE_VERSION=">=2.6"
-    else
-        ANSIBLE_VERSION="<2.7"
-    fi
-
-    # TODO(SamYaple): Move to virtualenv
-    sudo pip install -U "ansible${ANSIBLE_VERSION}" "ara<1.0.0"
-
-    sudo mkdir /etc/ansible
-    ara_location=$(python -m ara.setup.callback_plugins)
-    sudo tee /etc/ansible/ansible.cfg<<EOF
-[defaults]
-callback_plugins = ${ara_location}
-host_key_checking = False
-EOF
-
-    # Record the running state of the environment as seen by the setup module
-    ansible all -i ${RAW_INVENTORY} -e ansible_user=$USER -m setup > /tmp/logs/ansible/initial-setup
-}
-
 function prepare_images {
     if [[ "${BUILD_IMAGE}" == "False" ]]; then
         return
@@ -127,8 +102,8 @@ function prepare_images {
 
 setup_openstack_clients
 
-setup_ansible
 setup_config
 
+RAW_INVENTORY=/etc/kolla/inventory
 tools/kolla-ansible -i ${RAW_INVENTORY} -e ansible_user=$USER -vvv bootstrap-servers &> /tmp/logs/ansible/bootstrap-servers
 prepare_images