diff --git a/ui_extensions/api_extension/Readme.md b/ui_extensions/api_extension/Readme.md
new file mode 100644
index 00000000..12cee5a0
--- /dev/null
+++ b/ui_extensions/api_extension/Readme.md
@@ -0,0 +1,9 @@
+# Instructions
+
+- after making changes to the code do the following
+`service httpd retstart`
+
+- files and folder should be located in.
+`/var/opt/cloudbolt/proserver/xui/`
+
+
diff --git a/ui_extensions/api_extension/__init__.py b/ui_extensions/api_extension/__init__.py
new file mode 100644
index 00000000..ce7a1a24
--- /dev/null
+++ b/ui_extensions/api_extension/__init__.py
@@ -0,0 +1,3 @@
+'''
+API Extension
+'''
diff --git a/ui_extensions/api_extension/templates/api.html b/ui_extensions/api_extension/templates/api.html
new file mode 100644
index 00000000..9843535b
--- /dev/null
+++ b/ui_extensions/api_extension/templates/api.html
@@ -0,0 +1,6 @@
+
Set-Template-Creds
+/api/v2/resource-handlers/{id}/Set-Template-Creds
+Endpoint for Setting Credentials on Template with an option sshkey, to the RH.
+ POST: { "template": "{{template_name}}", "user-name": "{{user_name}}", "password": "{{password}}", "ssh-key":"{{ssh_key}}" }
+ POST: [{ "template": "{{template_name}}", "user-name": "{{user_name}}", "password": "{{password}}", "ssh-key":"{{ssh_key}}" }, { "template": "{{template_name}}", }]
+
diff --git a/ui_extensions/api_extension/urls.py b/ui_extensions/api_extension/urls.py
new file mode 100644
index 00000000..a5128a04
--- /dev/null
+++ b/ui_extensions/api_extension/urls.py
@@ -0,0 +1,15 @@
+from __future__ import unicode_literals
+from api.routers import CloudBoltDefaultRouter
+from api.serializers import CustomJWTSerializer
+from api.v2 import viewsets
+from django.conf.urls import include, url
+from rest_framework_jwt.views import ObtainJSONWebToken
+router = CloudBoltDefaultRouter()
+from xui.api_extension.views import set_template_creds, ResourceHandlerViewSetExtend
+router.register('resource-handlers/(?P\\d+)/set-template-creds/',
+ ResourceHandlerViewSetExtend)
+xui_urlpatterns = [
+ url('^api/v2/resource-handlers/(?P\\d+)/set-template-creds/$',
+ set_template_creds, name='set_template_creds'),
+]
+
diff --git a/ui_extensions/api_extension/views.py b/ui_extensions/api_extension/views.py
new file mode 100644
index 00000000..c645eabc
--- /dev/null
+++ b/ui_extensions/api_extension/views.py
@@ -0,0 +1,152 @@
+from __future__ import unicode_literals
+from datetime import datetime
+from django.db.models import Q
+from django.utils.translation import ugettext as _
+from rest_framework.exceptions import PermissionDenied
+from rest_framework.response import Response
+from rest_framework import status
+from rest_framework import viewsets
+from api.decorators import link
+from api.exceptions import APIException
+from rest_framework.decorators import api_view
+from api.viewsets import CloudBoltViewSet, ImportExportViewsetMixin, action_return, dict_return
+from api.v2.viewsets import ResourceHandlerViewSet, SetEnabledMixin
+from resourcehandlers.models import ResourceHandler
+from resources.models import Resource
+from utilities.logger import ThreadLogger
+from resourcehandlers.serializers import ResourceHandlerSerializer
+from api.v2.pagination import ResourceHandlerPagination
+from extensions.views import admin_extension
+from django.shortcuts import render
+from utilities.permissions import cbadmin_required
+logger = ThreadLogger(__name__)
+
+
+@admin_extension(title="API Extension")
+def apiextensions(request, *args, **kwargs):
+ return render(request, "api_extension/templates/api.html")
+
+
+class ResourceHandlerViewSetExtend(ResourceHandlerViewSet, SetEnabledMixin, ImportExportViewsetMixin):
+ def __init__(self, id, request):
+ self.id = id
+ self.request = request
+ model = ResourceHandler
+ serializer_class = ResourceHandlerSerializer
+ pagination_class = ResourceHandlerPagination
+
+ #@cbadmin_required
+ @link(methods=['post'])
+ def set_template_creds(self, *args, **kwargs):
+ """
+ Endpoint for Setting Credentials on Template with an option sshkey, to
+ the RH.
+
+ Single POST:
+ {
+ "template": "{{template_name}}",
+ "user-name": "{{user_name}}",
+ "password": "{{password}}",
+ "ssh-key":"{{ssh_key}}"
+ }
+
+ Array POST:
+ [{
+ "template": "{{template_name}}",
+ "user-name": "{{user_name}}",
+ "password": "{{password}}",
+ "ssh-key":"{{ssh_key}}"
+ },
+ {
+ "template": "{{template_name}}",
+ }]
+ """
+ resp = {}
+ rh = ResourceHandler.objects.get(id=self.id)
+ handler = rh.cast()
+ if not handler.can_import_templates_api:
+ raise APIException(_('Bad Request: Invalid Resource Handler'),
+ code=400,
+ details=_('API endpoint is not currently supported for this resource handler.'))
+
+ profile = self.request.get_user_profile()
+ if not profile.is_cbadmin:
+ raise PermissionDenied(
+ _("This action requires 'CB Admin' or 'Super Admin' privileges."))
+
+ combined_requests = self._verify_api_template_creds_json(self.request.data)
+ all_reqs = []
+ for req_template, req_username, req_password, req_sshkey in combined_requests:
+ logger.info(f"Attempting to set credentials on {req_template}")
+ template = handler.os_build_attributes.filter(
+ template_name=req_template).first().cast()
+ logger.info(template)
+ logger.info(dir(template))
+ if template:
+ template.password = req_password
+ template.username = req_username
+ template.save()
+ handler.save()
+ message = f"Template Credentials updated for {template}."
+ resp['message'] = message
+ resp['status_code'] = 200
+ all_reqs.append(resp)
+ else:
+ message = f"Template {req_template} Not Found"
+ resp['message'] = message
+ resp['status_code'] = 400
+ all_reqs.append(resp)
+ overall = {}
+ overall['message'] = f"Template Credentials updated for {template}."
+ overall['status_code'] = 200
+ for resp in all_reqs:
+ if resp['status_code'] != 200:
+ overall['status_code'] = resp['status_code']
+ overall['message'] = resp['message']
+ return Response(overall, status=overall['status_code'])
+
+ def _verify_api_template_creds_json(self, request_data):
+ """
+ Validate incoming POST request data and create pairs of templates and os_builds
+ to import to a specific resource handler.
+ """
+ logger.info("Confirming Payload for set-template-creds")
+ if not isinstance(request_data, list):
+ request_data = [
+ request_data]
+ requested_templates, requested_usernames, requested_passwords, requested_sshkeys = [], [], [], []
+ for template in request_data:
+ requested_template = template.get('template', None)
+ requested_username = template.get('user-name', None)
+ requested_password = template.get('password', None)
+ requested_sshkey = template.get('ssh-key', None)
+ if requested_sshkey == '':
+ requested_sshkey = None
+ logger.info(requested_sshkey)
+ if requested_template in requested_templates:
+ raise APIException(_('Bad Request: Duplicate Names'),
+ code=400,
+ details=_("'template' and 'os_build' must be assigned unique values for each entry in POST request"))
+ else:
+ requested_templates.append(requested_template)
+ requested_usernames.append(requested_username)
+ requested_passwords.append(requested_password)
+ requested_sshkeys.append(requested_sshkey)
+ return zip(requested_templates, requested_usernames, requested_passwords, requested_sshkeys)
+
+
+@api_view(['POST'])
+def set_template_creds(request, id, *args, **kwargs):
+ rh = ResourceHandlerViewSetExtend(id=id, request=request)
+ resp = rh.set_template_creds()
+ return resp
+
+
+#Sample Payloads
+#{
+# "template": "templatename",
+# "user-name": "myuser",
+# "password": "mytemplatepassword"
+#}
+#
+