diff --git a/environments/undercloud.yaml b/environments/undercloud.yaml
index 82af21986a..8a082c2d94 100644
--- a/environments/undercloud.yaml
+++ b/environments/undercloud.yaml
@@ -4,6 +4,7 @@ resource_registry:
   OS::TripleO::Undercloud::Net::SoftwareConfig: ../net-config-undercloud.yaml
   OS::TripleO::NodeExtraConfigPost: ../extraconfig/post_deploy/undercloud_post.yaml
   OS::TripleO::Services::DockerRegistry: ../puppet/services/docker-registry.yaml
+  OS::TripleO::Services::ContainerImagePrepare: ../puppet/services/container-image-prepare.yaml
   # Allows us to control the external VIP for Undercloud SSL
   OS::TripleO::Network::Ports::ExternalVipPort: ../network/ports/external_from_pool.yaml
 
diff --git a/overcloud-resource-registry-puppet.j2.yaml b/overcloud-resource-registry-puppet.j2.yaml
index 711cb33820..89363f06dc 100644
--- a/overcloud-resource-registry-puppet.j2.yaml
+++ b/overcloud-resource-registry-puppet.j2.yaml
@@ -305,6 +305,7 @@ resource_registry:
   OS::TripleO::Services::NeutronVppAgent: OS::Heat::None
   OS::TripleO::Services::Docker: puppet/services/docker.yaml
   OS::TripleO::Services::DockerRegistry: OS::Heat::None
+  OS::TripleO::Services::ContainerImagePrepare: OS::Heat::None
   OS::TripleO::Services::CertmongerUser: puppet/services/certmonger-user.yaml
   OS::TripleO::Services::Clustercheck: OS::Heat::None
   OS::TripleO::Services::RsyslogSidecar: OS::Heat::None
diff --git a/puppet/services/docker-registry.j2.yaml b/puppet/services/container-image-prepare.j2.yaml
similarity index 60%
rename from puppet/services/docker-registry.j2.yaml
rename to puppet/services/container-image-prepare.j2.yaml
index 83c6495b2c..2dad0638d3 100644
--- a/puppet/services/docker-registry.j2.yaml
+++ b/puppet/services/container-image-prepare.j2.yaml
@@ -1,7 +1,7 @@
 heat_template_version: rocky
 
 description: >
-  Configures docker-registry on a host.
+  Prepare container images
 
 parameters:
   EndpointMap:
@@ -30,10 +30,6 @@ parameters:
     default: {}
     description: Parameters specific to the role
     type: json
-  LocalContainerRegistry:
-    default: ''
-    description: The IP address used to bind the local container registry
-    type: string
   ContainerImagePrepare:
     default: {}
     description: Used to run "openstack tripleo container image prepare".
@@ -44,41 +40,38 @@ parameters:
     default: 'tripleo-container-image-prepare.log'
     type: string
     description: Used to store outputs of "openstack tripleo container image prepare".
+  DockerInsecureRegistryAddress:
+    description: Optional. The IP Address and Port of an insecure docker
+                 namespace that will be configured in /etc/sysconfig/docker.
+                 The value can be multiple addresses separated by commas.
+    type: comma_delimited_list
+    default: []
 
-conditions:
-  local_container_registry_is_empty: {equals : [{get_param: LocalContainerRegistry}, '']}
+{% for role in roles %}
+  # Parameters generated for {{role.name}} Role
+  {{role.name}}Services:
+    description: A list of service resources (configured in the Heat
+                 resource_registry) which represent nested stacks
+                 for each service that should get installed on the {{role.name}} role.
+    type: comma_delimited_list
+
+  {{role.name}}Count:
+    description: Number of {{role.name}} nodes to deploy
+    type: number
+    default: {{role.CountDefault|default(0)}}
+{% endfor %}
 
 outputs:
   role_data:
-    description: Role data for the docker registry service
+    description: Role data for container image prepare
     value:
-      service_name: docker_registry
-      config_settings:
-        tripleo.docker_registry.firewall_rules:
-          '155 docker-registry':
-            dport:
-              - 8787
-              - 13787
-      step_config: ''
-      host_prep_tasks:
-        - name: Install, Configure and Run Docker Distribution
-          block:
-          # NOTE(bogdando): w/a https://github.com/ansible/ansible/issues/42621
-          - set_fact:
-              container_registry_host:
-                if:
-                - local_container_registry_is_empty
-                - {get_param: [EndpointMap, DockerRegistryInternal, host]}
-                - {get_param: LocalContainerRegistry}
-              container_registry_port: {get_param: [EndpointMap, DockerRegistryInternal, port]}
-              log_file: {get_param: ContainerImagePrepareLogFile}
-          - include_role:
-              name: container-registry
-              tasks_from: docker-distribution
+      service_name: container_image_prepare
       external_deploy_tasks:
         - name: Container image prepare
           when: step|int == 1
           block:
+          - set_fact:
+              log_file: {get_param: ContainerImagePrepareLogFile}
           - name: Create temp file for prepare parameter
             tempfile:
               state: file
@@ -95,12 +88,17 @@ outputs:
               content:
                 parameter_defaults:
                   ContainerImagePrepare: {get_param: ContainerImagePrepare}
+                  DockerInsecureRegistryAddress: {get_param: DockerInsecureRegistryAddress}
+{% for role in roles %}
+                  {{role.name}}Services: {get_param: {{role.name}}Services}
+                  {{role.name}}Count: {get_param: {{role.name}}Count}
+{% endfor %}
           - name: Write role data file
             copy:
               dest: "{{ '{{' }} role_data.path {{ '}}' }}"
               content: {{ roles }}
-          - name: Run openstack tripleo container image prepare
-            command: openstack tripleo container image prepare --log-file {{ '{{' }} log_file {{ '}}' }} --roles-file {{ '{{' }} role_data.path {{ '}}' }} --environment-file {{ '{{' }} prepare_param.path {{ '}}' }} --cleanup partial --verbose
+          - name: Run tripleo-container-image-prepare
+            shell: sudo /usr/bin/tripleo-container-image-prepare --roles-file {{ '{{' }} role_data.path {{ '}}' }} --environment-file {{ '{{' }} prepare_param.path {{ '}}' }} --cleanup partial 2> {{ '{{' }} log_file {{ '}}' }}
           - name: Delete param file
             file:
               dest: "{{ '{{' }} prepare_param.path {{ '}}' }}"
@@ -109,7 +107,3 @@ outputs:
             file:
               dest: "{{ '{{' }} role_data.path {{ '}}' }}"
               state: absent
-      upgrade_tasks:
-        - name: Install docker packages on upgrade if missing
-          when: step|int == 3
-          package: name=docker-distribution state=latest
diff --git a/puppet/services/docker-registry.yaml b/puppet/services/docker-registry.yaml
new file mode 100644
index 0000000000..7cec328c87
--- /dev/null
+++ b/puppet/services/docker-registry.yaml
@@ -0,0 +1,70 @@
+heat_template_version: rocky
+
+description: >
+  Configures docker-registry on a host.
+
+parameters:
+  EndpointMap:
+    default: {}
+    description: Mapping of service endpoint -> protocol. Typically set
+                 via parameter_defaults in the resource registry.
+    type: json
+  ServiceData:
+    default: {}
+    description: Dictionary packing service data
+    type: json
+  ServiceNetMap:
+    default: {}
+    description: Mapping of service_name -> network name. Typically set
+                 via parameter_defaults in the resource registry.  This
+                 mapping overrides those in ServiceNetMapDefaults.
+    type: json
+  DefaultPasswords:
+    default: {}
+    type: json
+  RoleName:
+    default: ''
+    description: Role name on which the service is applied
+    type: string
+  RoleParameters:
+    default: {}
+    description: Parameters specific to the role
+    type: json
+  LocalContainerRegistry:
+    default: ''
+    description: The IP address used to bind the local container registry
+    type: string
+
+conditions:
+  local_container_registry_is_empty: {equals : [{get_param: LocalContainerRegistry}, '']}
+
+outputs:
+  role_data:
+    description: Role data for the docker registry service
+    value:
+      service_name: docker_registry
+      config_settings:
+        tripleo.docker_registry.firewall_rules:
+          '155 docker-registry':
+            dport:
+              - 8787
+              - 13787
+      step_config: ''
+      host_prep_tasks:
+        - name: Install, Configure and Run Docker Distribution
+          block:
+          # NOTE(bogdando): w/a https://github.com/ansible/ansible/issues/42621
+          - set_fact:
+              container_registry_host:
+                if:
+                - local_container_registry_is_empty
+                - {get_param: [EndpointMap, DockerRegistryInternal, host]}
+                - {get_param: LocalContainerRegistry}
+              container_registry_port: {get_param: [EndpointMap, DockerRegistryInternal, port]}
+          - include_role:
+              name: container-registry
+              tasks_from: docker-distribution
+      upgrade_tasks:
+        - name: Install docker packages on upgrade if missing
+          when: step|int == 3
+          package: name=docker-distribution state=latest
diff --git a/roles/Undercloud.yaml b/roles/Undercloud.yaml
index ec5259611d..821ddd62c9 100644
--- a/roles/Undercloud.yaml
+++ b/roles/Undercloud.yaml
@@ -30,6 +30,7 @@
     - OS::TripleO::Services::CinderApi
     - OS::TripleO::Services::CinderScheduler
     - OS::TripleO::Services::CinderVolume
+    - OS::TripleO::Services::ContainerImagePrepare
     - OS::TripleO::Services::ContainersLogrotateCrond
     - OS::TripleO::Services::Docker
     - OS::TripleO::Services::DockerRegistry
diff --git a/roles_data_undercloud.yaml b/roles_data_undercloud.yaml
index ef23031111..674ec50352 100644
--- a/roles_data_undercloud.yaml
+++ b/roles_data_undercloud.yaml
@@ -33,6 +33,7 @@
     - OS::TripleO::Services::CinderApi
     - OS::TripleO::Services::CinderScheduler
     - OS::TripleO::Services::CinderVolume
+    - OS::TripleO::Services::ContainerImagePrepare
     - OS::TripleO::Services::ContainersLogrotateCrond
     - OS::TripleO::Services::Docker
     - OS::TripleO::Services::DockerRegistry