Merge branch 'odahu-integration' into develop
diff --git a/infrastructure-provisioning/scripts/deploy_dlab.py b/infrastructure-provisioning/scripts/deploy_dlab.py
index 40b1485..cd27a5b 100644
--- a/infrastructure-provisioning/scripts/deploy_dlab.py
+++ b/infrastructure-provisioning/scripts/deploy_dlab.py
@@ -119,6 +119,19 @@
 parser.add_argument('--cost', type=str, default='line_item_blended_cost', help='Column name in report file that contains cost tag')
 parser.add_argument('--resource_id', type=str, default='line_item_resource_id', help='Column name in report file that contains '
                                                                           'dlab resource id tag')
+parser.add_argument('--odahu_tls_crt', type=str, default='', help='TLS Certificate for Odahu cluster')
+parser.add_argument('--odahu_tls_key', type=str, default='', help='TLS Key for Odahu cluster')
+parser.add_argument('--odahu_docker_private_repo', type=str, default='', help='Odahu private repository url')
+parser.add_argument('--odahu_docker_password', type=str, default='', help='Odahu private repository base64 encoded password')
+parser.add_argument('--odahu_keysecret', type=str, default='', help='KeySecret for Odahuflow examples git')
+parser.add_argument('--odahu_oauth_local_jwks', type=str, default='', help='JWKS from keycloak')
+parser.add_argument('--odahu_airflow_secret', type=str, default='', help='Airflow keycloak service account secret')
+parser.add_argument('--odahu_operator_secret', type=str, default='', help='Operator keycloak service account secret')
+parser.add_argument('--odahu_resource_uploader_secret', type=str, default='', help='Resource uploader keycloak service'
+                                                                                      ' account secret')
+parser.add_argument('--odahu_tester_secret', type=str, default='', help='Tester keycloak service account secret')
+parser.add_argument('--odahu_tester_data_scientist_secret', type=str, default='', help='Tester data scientist keycloak'
+                                                                                          ' service account secret')
 parser.add_argument('--ldap_hostname', type=str, default='localhost', help='Ldap instance hostname')
 parser.add_argument('--ldap_dn', type=str, default='dc=example,dc=com',
                     help='Ldap distinguished name')
@@ -138,6 +151,7 @@
                                                                          ' for billing')
 parser.add_argument('--default_endpoint_name', type=str, default='local', help='Name of localhost provisioning service,'
                                                                                'that created by default')
+parser.add_argument('--ssn_gcr_creds', type=str, default='', help='Base64 encrypted json key for GCR authentication')
 parser.add_argument('--conf_stepcerts_enabled', type=str, default='false', help='Enable or disable step certificates')
 parser.add_argument('--conf_stepcerts_root_ca', type=str, default='', help='Step root CA')
 parser.add_argument('--conf_stepcerts_kid', type=str, default='', help='Step KID')
diff --git a/infrastructure-provisioning/src/general/api/create.py b/infrastructure-provisioning/src/general/api/create.py
index b2437b0..b77cd96 100644
--- a/infrastructure-provisioning/src/general/api/create.py
+++ b/infrastructure-provisioning/src/general/api/create.py
@@ -54,6 +54,14 @@
 
         with open("/response/{}.json".format(os.environ['request_id']), 'w') as response_file:
             response_file.write(json.dumps(reply))
+    elif os.environ['conf_resource'] == 'odahu':
+        reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
+                                                                              os.environ['odahu_cluster_name'],
+                                                                              os.environ['request_id'])
+
+        with open("/response/{}_{}_{}.json".format(os.environ['conf_resource'], os.environ['odahu_cluster_name'],
+                                                   os.environ['request_id']), 'w') as response_file:
+            response_file.write(json.dumps(reply))
     else:
         reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
                                                                               os.environ['project_name'],
diff --git a/infrastructure-provisioning/src/general/api/start.py b/infrastructure-provisioning/src/general/api/start.py
index 469d5a6..11ca36f 100644
--- a/infrastructure-provisioning/src/general/api/start.py
+++ b/infrastructure-provisioning/src/general/api/start.py
@@ -48,13 +48,23 @@
             reply['response']['result'] = json.loads(f.read())
     except:
         reply['response']['result'] = {"error": "Failed to open result.json"}
-    reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
-                                                                          os.environ['project_name'],
-                                                                          os.environ['request_id'])
 
-    with open("/response/{}_{}_{}.json".format(os.environ['conf_resource'], os.environ['project_name'],
-                                               os.environ['request_id']), 'w') as response_file:
-        response_file.write(json.dumps(reply))
+    if os.environ['conf_resource'] == 'odahu':
+        reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
+                                                                              os.environ['odahu_cluster_name'],
+                                                                              os.environ['request_id'])
+
+        with open("/response/{}_{}_{}.json".format(os.environ['conf_resource'], os.environ['odahu_cluster_name'],
+                                                   os.environ['request_id']), 'w') as response_file:
+            response_file.write(json.dumps(reply))
+    else:
+        reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
+                                                                              os.environ['project_name'],
+                                                                              os.environ['request_id'])
+
+        with open("/response/{}_{}_{}.json".format(os.environ['conf_resource'], os.environ['project_name'],
+                                                   os.environ['request_id']), 'w') as response_file:
+            response_file.write(json.dumps(reply))
 
     try:
         local('chmod 666 /response/*')
diff --git a/infrastructure-provisioning/src/general/api/stop.py b/infrastructure-provisioning/src/general/api/stop.py
index 8dc2a11..39d1f8e 100644
--- a/infrastructure-provisioning/src/general/api/stop.py
+++ b/infrastructure-provisioning/src/general/api/stop.py
@@ -49,13 +49,22 @@
     except:
         reply['response']['result'] = {"error": "Failed to open result.json"}
 
-    reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
-                                                                          os.environ['project_name'],
-                                                                          os.environ['request_id'])
+    if os.environ['conf_resource'] == 'odahu':
+        reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
+                                                                              os.environ['odahu_cluster_name'],
+                                                                              os.environ['request_id'])
 
-    with open("/response/{}_{}_{}.json".format(os.environ['conf_resource'], os.environ['project_name'],
-                                               os.environ['request_id']), 'w') as response_file:
-        response_file.write(json.dumps(reply))
+        with open("/response/{}_{}_{}.json".format(os.environ['conf_resource'], os.environ['odahu_cluster_name'],
+                                                   os.environ['request_id']), 'w') as response_file:
+            response_file.write(json.dumps(reply))
+    else:
+        reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
+                                                                              os.environ['project_name'],
+                                                                              os.environ['request_id'])
+
+        with open("/response/{}_{}_{}.json".format(os.environ['conf_resource'], os.environ['project_name'],
+                                                   os.environ['request_id']), 'w') as response_file:
+            response_file.write(json.dumps(reply))
 
     try:
         local('chmod 666 /response/*')
diff --git a/infrastructure-provisioning/src/general/api/terminate.py b/infrastructure-provisioning/src/general/api/terminate.py
index 933af27..f637b51 100644
--- a/infrastructure-provisioning/src/general/api/terminate.py
+++ b/infrastructure-provisioning/src/general/api/terminate.py
@@ -54,6 +54,14 @@
 
         with open("/response/{}.json".format(os.environ['request_id']), 'w') as response_file:
             response_file.write(json.dumps(reply))
+    elif os.environ['conf_resource'] == 'odahu':
+        reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
+                                                                              os.environ['odahu_cluster_name'],
+                                                                              os.environ['request_id'])
+
+        with open("/response/{}_{}_{}.json".format(os.environ['conf_resource'], os.environ['odahu_cluster_name'],
+                                                   os.environ['request_id']), 'w') as response_file:
+            response_file.write(json.dumps(reply))
     else:
         reply['response']['log'] = "/var/log/dlab/{0}/{0}_{1}_{2}.log".format(os.environ['conf_resource'],
                                                                               os.environ['project_name'],
diff --git a/infrastructure-provisioning/src/general/conf/dlab.ini b/infrastructure-provisioning/src/general/conf/dlab.ini
index 8ab5f9e..e3db900 100644
--- a/infrastructure-provisioning/src/general/conf/dlab.ini
+++ b/infrastructure-provisioning/src/general/conf/dlab.ini
@@ -53,7 +53,7 @@
 ### CIDR of second VPC
 vpc2_cidr = '172.32.0.0/16'
 ### Enable or disable duo VPC mode(true|false)
-duo_vpc_enable = false
+duo_vpc_enable = falseF
 ### Range of subnets which will be using for user's environments
 # user_subnets_range =
 ### Comma-separated CIDR of IPs which will have access to SSN and Edge nodes
@@ -210,6 +210,12 @@
 # subdomain =
 ### Role ARN for creating Route53 record
 # assume_role_arn =
+### URL to Nexus repository
+nexus_url = ec2-35-163-186-140.us-west-2.compute.amazonaws.com:8083
+### Username for GCR authentication
+# gcr_username =
+### Base64 encrypted json key for GCR authentication
+# gcr_creds =
 
 #--- [edge] section contains all parameters that are using for edge node provisioning ---#
 [edge]
@@ -327,6 +333,87 @@
 ### Depending on RAM size on instance, this parameter determines size of RAM when explicit allocation RAM is used
 expl_instance_memory = 8000
 
+#--- [odahu] odahuflow parameters ---#
+[odahu]
+###
+allowed_cidr = 0.0.0.0/0
+###
+dns_zone_name = develop-dlabanalytics-com
+###
+docker_repo = odahu
+###
+cidr = 172.31.0.0/24
+###
+grafana_admin = grafana_admin
+###
+initial_node_count = 6
+###
+istio_helm_repo = https://storage.googleapis.com/istio-release/releases/1.2.2/charts
+###
+k8s_version = 1.14
+###
+helm_repo =  https://raw.githubusercontent.com/odahu/odahu-helm/master
+###
+jupyterhub_enabled = true
+###
+automation_version = 1.1.0
+###
+infra_version = 1.1.0
+###
+ui_version = 1.1.0
+###
+examples_version = 1.1.0
+###
+odahuflow_version = 1.1.0
+###
+mlflow_toolchain_version = 1.1.0
+###
+jupyterlab_version = 1.1.0
+###
+packager_version = 1.1.0
+###
+private_subnet_cidrs = 172.10.2.0/24,172.10.4.0/24
+###
+public_subnet_cidrs = 172.10.1.0/24,172.10.3.0/24
+###
+oauth_mesh_enabled = true
+###
+node_version = 1.13.12-gke.2
+###
+pods_cidr = 10.42.0.0/17
+###
+root_domain = develop.dlabanalytics.com
+###
+service_cidr = 10.42.128.0/20
+###
+dns_project_id = or2-msq-epmc-dlab-t1iylu
+###
+infra_vpc_peering = 0
+### Link to Odahuflow deploy image
+deploy_image = gcr.io/or2-msq-epmd-legn-t1iylu/odahu/odahu-flow-automation:1.1.0
+###
+opa_policy = cGFja2FnZSBvZGFodQoKIyByb2xlcy5yZWdvCgphZG1pbiA6PSAiYWRtaW4iCmRhdGFfc2NpZW50aXN0IDo9ICJkYXRhX3NjaWVudGlzdCIKCiMgcmJhYy5yZWdvCgpyYmFjIDo9IHsKCWRhdGFfc2NpZW50aXN0OiBbCiAgICAJWyIqIiwgImFwaS92MS9tb2RlbC9kZXBsb3ltZW50LioiXSwKICAgIAlbIioiLCAiYXBpL3YxL21vZGVsL3BhY2thZ2luZy4qIl0sCiAgICAJWyIqIiwgImFwaS92MS9tb2RlbC90cmFpbmluZy4qIl0sCiAgICAJWyJHRVQiLCAiYXBpL3YxL2Nvbm5lY3Rpb24uKiJdLAogICAgCVsiUE9TVCIsICJhcGkvdjEvY29ubmVjdGlvbi4qIl0sCiAgICAJWyJHRVQiLCAiYXBpL3YxL3BhY2thZ2luZy9pbnRlZ3JhdGlvbi4qIl0sCiAgICAJWyJHRVQiLCAiYXBpL3YxL3Rvb2xjaGFpbi9pbnRlZ3JhdGlvbi4qIl0KICAgIF0sCiAgYWRtaW4gOiBbCiAgICAgIFsiLioiLCAiLioiXQogIF0KfQoKIyBpbnB1dF9tYXBwZXIKCnJvbGVzX21hcCA9IHsKCSJvZGFodV9hZG1pbiI6IGFkbWluLAogICAgIm9kYWh1X2RhdGFfc2NpZW50aXN0IjogZGF0YV9zY2llbnRpc3QKfQoKand0ID0gaW5wdXQuYXR0cmlidXRlcy5tZXRhZGF0YV9jb250ZXh0LmZpbHRlcl9tZXRhZGF0YVsiZW52b3kuZmlsdGVycy5odHRwLmp3dF9hdXRobiJdLmZpZWxkcy5qd3RfcGF5bG9hZAoKa2V5Y2xvYWtfdXNlcl9yb2xlc1tyb2xlXXsKCXJvbGUgPSBqd3QuS2luZC5TdHJ1Y3RWYWx1ZS5maWVsZHMucmVhbG1fYWNjZXNzLktpbmQuU3RydWN0VmFsdWUuZmllbGRzLnJvbGVzLktpbmQuTGlzdFZhbHVlLnZhbHVlc1tfXS5LaW5kLlN0cmluZ1ZhbHVlCn0KCnVzZXJfcm9sZXNbcm9sZV17Cglyb2xlID0gcm9sZXNfbWFwW2tleWNsb2FrX3VzZXJfcm9sZXNbX11dCn0KCgpwYXJzZWRfaW5wdXQgPSB7CgkiYWN0aW9uIjogaW5wdXQuYXR0cmlidXRlcy5yZXF1ZXN0Lmh0dHAubWV0aG9kLAogICJyZXNvdXJjZSI6IGlucHV0LmF0dHJpYnV0ZXMucmVxdWVzdC5odHRwLnBhdGgsCiAgInVzZXIiOiB7CiAgICAicm9sZXMiOiB1c2VyX3JvbGVzCiAgfQp9CgoKIyBjb3JlCgpkZWZhdWx0IGFsbG93ID0gZmFsc2UKYWxsb3cgewoJYW55X3VzZXJfcm9sZSA6PSBwYXJzZWRfaW5wdXQudXNlci5yb2xlc1tfXQogICAgYW55X3Blcm1pc3Npb25fb2ZfdXNlcl9yb2xlIDo9IHJiYWNbYW55X3VzZXJfcm9sZV1bX10KICAgIGFjdGlvbiA6PSBhbnlfcGVybWlzc2lvbl9vZl91c2VyX3JvbGVbMF0KICAgIHJlc291cmNlIDo9IGFueV9wZXJtaXNzaW9uX29mX3VzZXJfcm9sZVsxXQoKICAgIHJlX21hdGNoKGFjdGlvbiwgcGFyc2VkX2lucHV0LmFjdGlvbikKICAgIHJlX21hdGNoKHJlc291cmNlLCBwYXJzZWRfaW5wdXQucmVzb3VyY2UpCn0=
+### TLS Certificate for Odahu cluster
+# tls_crt =
+### TLS Key for Odahu cluster
+# tls_key =
+### Odahu private repository base64 encoded password
+# docker_password =
+### KeySecret for Odahuflow examples git
+# keysecret =
+### JWKS from keycloak
+# oauth_local_jwks =
+### Airflow keycloak service account secret
+# airflow_secret =
+### Operator keycloak service account secret
+# operator_secret =
+### Resource uploader keycloak service account secret
+# resource_uploader_secret =
+### Tester keycloak service account secret
+# tester_secret =
+### Tester data scientist keycloak service account secret
+# tester_data_scientist_secret =
+
 #--- [ldap] ldap parameters ---#
 [ldap]
 ### Ldap hostname
diff --git a/infrastructure-provisioning/src/general/files/aws/odahu_Dockerfile b/infrastructure-provisioning/src/general/files/aws/odahu_Dockerfile
new file mode 100644
index 0000000..d41a94a
--- /dev/null
+++ b/infrastructure-provisioning/src/general/files/aws/odahu_Dockerfile
@@ -0,0 +1,85 @@
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+FROM ODAHU_IMAGE
+ARG OS
+ARG SRC_PATH
+
+ENV PROFILE=/profile.json
+
+# Install any .deb dependecies
+RUN	apt-get update && \
+    apt-get -y upgrade && \
+    apt-get -y install python-pip python-dev build-essential python3 python3-dev && \
+    apt-get -y install groff vim less git wget nano libssl-dev libffi-dev libffi6 && \
+    apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+# Install any python dependencies
+RUN pip install -UI pip==9.0.3 && \
+    pip install boto3 backoff fabric==1.14.0 fabvenv awscli argparse requests ujson jupyter pycrypto
+
+# Configuring ssh for user
+RUN mkdir -p /root/.ssh; echo "Host *" > /root/.ssh/config; \
+    echo "StrictHostKeyChecking no" >>  /root/.ssh/config; \
+    echo "UserKnownHostsFile=/dev/null" >> /root/.ssh/config; \
+    echo "GlobalKnownHostsFile=/dev/null" >> /root/.ssh/config; \
+    echo "ConnectTimeout=30" >> /root/.ssh/config
+
+# Configuring log directories
+RUN mkdir -p /response; chmod a+rwx /response && \
+    mkdir -p /logs/ssn; chmod a+rwx /logs/ssn && \
+    mkdir -p /logs/project; chmod a+rwx /logs/project && \
+    mkdir -p /logs/edge; chmod a+rwx /logs/edge && \
+    mkdir -p /logs/notebook; chmod a+rwx /logs/notebook && \
+    mkdir -p /logs/dataengine; chmod a+rwx /logs/dataengine && \
+    mkdir -p /logs/dataengine-service; chmod a+rwx /logs/dataengine-service
+
+# Copying all base scripts to docker
+ENV PROVISION_CONFIG_DIR /root/conf/
+ENV KEYFILE_DIR /root/keys/
+ENV AWS_DIR /root/.aws
+
+RUN mkdir -p /root/conf && \
+    mkdir -p /root/scripts && \
+    mkdir -p /root/templates && \
+    mkdir -p /root/files && \
+    mkdir -p /usr/lib/python2.7/dlab && \
+    mkdir -p /root/keys/.ssh
+
+COPY ${SRC_PATH}base/ /root
+COPY ${SRC_PATH}odahu/ /root
+COPY ${SRC_PATH}general/conf/* /root/conf/
+COPY ${SRC_PATH}general/api/*.py /bin/
+COPY ${SRC_PATH}general/scripts/aws/common_* /root/scripts/
+COPY ${SRC_PATH}general/scripts/aws/odahu_* /root/scripts/
+COPY ${SRC_PATH}general/lib/aws/* /usr/lib/python2.7/dlab/
+COPY ${SRC_PATH}general/lib/os/${OS}/common_lib.py /usr/lib/python2.7/dlab/common_lib.py
+COPY ${SRC_PATH}general/lib/os/fab.py /usr/lib/python2.7/dlab/fab.py
+COPY ${SRC_PATH}general/files/os/${OS}/sources.list /root/files/
+COPY ${SRC_PATH}general/files/os/ivysettings.xml /root/templates/
+COPY ${SRC_PATH}general/files/os/local_endpoint.json /root/files/
+COPY ${SRC_PATH}project/templates/locations/ /root/locations/
+
+RUN chmod a+x /root/*.py && \
+    chmod a+x /root/scripts/* && \
+    chmod a+x /bin/*.py
+
+ENTRYPOINT ["/root/entrypoint.py"]
diff --git a/infrastructure-provisioning/src/general/files/aws/odahu_description.json b/infrastructure-provisioning/src/general/files/aws/odahu_description.json
new file mode 100644
index 0000000..160a2ec
--- /dev/null
+++ b/infrastructure-provisioning/src/general/files/aws/odahu_description.json
@@ -0,0 +1,8 @@
+{
+  "template_name": "OdahuFlow cluster",
+  "description": "OdahuFlow cluster",
+  "environment_type": "computational",
+  "templates":
+  [
+  ]
+}
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/general/files/gcp/odahu_Dockerfile b/infrastructure-provisioning/src/general/files/gcp/odahu_Dockerfile
new file mode 100644
index 0000000..995092d
--- /dev/null
+++ b/infrastructure-provisioning/src/general/files/gcp/odahu_Dockerfile
@@ -0,0 +1,85 @@
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+FROM ODAHU_IMAGE
+ARG OS
+ARG SRC_PATH
+
+ENV PROFILE=/profile.json
+
+# Install any .deb dependecies
+RUN	apt-get update && \
+    apt-get -y upgrade && \
+    apt-get -y install python-pip build-essential python3 python3-dev python-dev && \
+    apt-get -y install groff vim less git wget nano libssl-dev libffi-dev libffi6 && \
+    apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+# Install any python dependencies
+RUN pip2 install -UI pip==9.0.3 && \
+    pip2 install boto3 backoff fabric==1.14.0 fabvenv  argparse ujson pycrypto google-api-python-client google-cloud-storage \
+    pyyaml google-auth-httplib2 oauth2client
+
+# Configuring ssh for user
+RUN mkdir -p /root/.ssh; echo "Host *" > /root/.ssh/config; \
+    echo "StrictHostKeyChecking no" >>  /root/.ssh/config; \
+    echo "UserKnownHostsFile=/dev/null" >> /root/.ssh/config; \
+    echo "GlobalKnownHostsFile=/dev/null" >> /root/.ssh/config; \
+    echo "ConnectTimeout=30" >> /root/.ssh/config
+
+# Configuring log directories
+RUN mkdir -p /response; chmod a+rwx /response && \
+    mkdir -p /logs/ssn; chmod a+rwx /logs/ssn && \
+    mkdir -p /logs/project; chmod a+rwx /logs/project && \
+    mkdir -p /logs/edge; chmod a+rwx /logs/edge && \
+    mkdir -p /logs/notebook; chmod a+rwx /logs/notebook && \
+    mkdir -p /logs/dataengine; chmod a+rwx /logs/dataengine && \
+    mkdir -p /logs/dataengine-service; chmod a+rwx /logs/dataengine-service
+
+# Copying all base scripts to docker
+ENV PROVISION_CONFIG_DIR /root/conf/
+ENV KEYFILE_DIR /root/keys/
+
+RUN mkdir -p /root/conf && \
+    mkdir -p /root/scripts && \
+    mkdir -p /root/templates && \
+    mkdir -p /root/files && \
+    mkdir -p /usr/lib/python2.7/dlab && \
+    mkdir -p /root/keys/.ssh
+
+COPY ${SRC_PATH}base/ /root
+COPY ${SRC_PATH}odahu/ /root
+COPY ${SRC_PATH}general/conf/* /root/conf/
+COPY ${SRC_PATH}general/api/*.py /bin/
+COPY ${SRC_PATH}general/scripts/gcp/common_* /root/scripts/
+COPY ${SRC_PATH}general/scripts/gcp/odahu_* /root/scripts/
+COPY ${SRC_PATH}general/lib/gcp/* /usr/lib/python2.7/dlab/
+COPY ${SRC_PATH}general/lib/os/${OS}/common_lib.py /usr/lib/python2.7/dlab/common_lib.py
+COPY ${SRC_PATH}general/lib/os/fab.py /usr/lib/python2.7/dlab/fab.py
+COPY ${SRC_PATH}general/files/os/${OS}/sources.list /root/files/
+COPY ${SRC_PATH}general/files/os/ivysettings.xml /root/templates/
+COPY ${SRC_PATH}general/files/os/local_endpoint.json /root/files/
+COPY ${SRC_PATH}project/templates/locations/ /root/locations/
+
+RUN chmod a+x /root/*.py && \
+    chmod a+x /root/scripts/* && \
+    chmod a+x /bin/*.py
+
+ENTRYPOINT ["/root/entrypoint.py"]
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/general/files/gcp/odahu_description.json b/infrastructure-provisioning/src/general/files/gcp/odahu_description.json
new file mode 100644
index 0000000..160a2ec
--- /dev/null
+++ b/infrastructure-provisioning/src/general/files/gcp/odahu_description.json
@@ -0,0 +1,8 @@
+{
+  "template_name": "OdahuFlow cluster",
+  "description": "OdahuFlow cluster",
+  "environment_type": "computational",
+  "templates":
+  [
+  ]
+}
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/general/files/gcp/ssn_policy.json b/infrastructure-provisioning/src/general/files/gcp/ssn_policy.json
index bd95d12..eb4ad12 100644
--- a/infrastructure-provisioning/src/general/files/gcp/ssn_policy.json
+++ b/infrastructure-provisioning/src/general/files/gcp/ssn_policy.json
@@ -16,5 +16,23 @@
     "compute.images.get",
     "compute.images.delete",
     "compute.images.setLabels",
-    "compute.images.list"
+    "compute.images.list",
+    "container.clusters.create",
+    "container.clusters.delete",
+    "container.clusters.get",
+    "container.clusters.update",
+    "container.operations.get",
+    "compute.routers.create",
+    "compute.routers.delete",
+    "compute.routers.get",
+    "compute.routers.update",
+    "container.serviceAccounts.create",
+    "container.serviceAccounts.get",
+    "container.serviceAccounts.delete",
+    "iam.serviceAccountKeys.create",
+    "iam.serviceAccountKeys.delete",
+    "iam.serviceAccountKeys.get",
+    "dns.resourceRecordSets.list",
+    "dns.managedZones.get",
+    "dns.managedZones.delete"
 ]
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/general/files/gcp/ssn_roles.json b/infrastructure-provisioning/src/general/files/gcp/ssn_roles.json
index fe310b6..3a72260 100644
--- a/infrastructure-provisioning/src/general/files/gcp/ssn_roles.json
+++ b/infrastructure-provisioning/src/general/files/gcp/ssn_roles.json
@@ -7,5 +7,7 @@
     "iam.roleAdmin",
     "compute.instanceAdmin",
     "bigquery.dataViewer",
-    "bigquery.jobUser"
+    "bigquery.jobUser",
+    "container.admin",
+    "dns.admin"
 ]
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/general/lib/aws/actions_lib.py b/infrastructure-provisioning/src/general/lib/aws/actions_lib.py
index 9519053..7be5328 100644
--- a/infrastructure-provisioning/src/general/lib/aws/actions_lib.py
+++ b/infrastructure-provisioning/src/general/lib/aws/actions_lib.py
@@ -1034,6 +1034,9 @@
         elif bucket_type == 'edge':
             bucket_name = (os.environ['conf_service_base_name'] + '-' + "{}".format(scientist) + '-' +
                            os.environ['endpoint_name'] + '-bucket').lower().replace('_', '-')
+        elif bucket_type == 'odahu':
+            bucket_name = "{}-{}-tfstate".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                 (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
         else:
             bucket_name = (os.environ['conf_service_base_name']).lower().replace('_', '-')
         for item in client.list_buckets().get('Buckets'):
diff --git a/infrastructure-provisioning/src/general/lib/gcp/meta_lib.py b/infrastructure-provisioning/src/general/lib/gcp/meta_lib.py
index cc16028..431a859 100644
--- a/infrastructure-provisioning/src/general/lib/gcp/meta_lib.py
+++ b/infrastructure-provisioning/src/general/lib/gcp/meta_lib.py
@@ -758,6 +758,28 @@
                                    file=sys.stdout)}))
             traceback.print_exc(file=sys.stdout)
 
+    def get_available_zones(self):
+        try:
+            request = self.service.regions().get(project=self.project, region=os.environ['gcp_region'])
+            gpu_zones = ['asia-east1-a', 'asia-east1-c', 'australia1-c', 'us-central1-c', 'us-central1-f', 'us-east1-b', 'us-east1-c', 'us-west1-a', 'us-west1-b', 'europe-west1-b', 'europe-west1-d', 'europe-west4-a']
+            zone_full = []
+            zone_list = []
+            response = request.execute()
+            for zone in response['zones']:
+                zone_full.append(str(zone.split('/')[-1]))
+            for zone in zone_full:
+                if zone in gpu_zones:
+                    zone_list.append(str(zone.split('/')[-1]))
+            return zone_list
+        except Exception as err:
+            logging.info(
+                "Error with getting available zones: " + str(err) + "\n Traceback: " + traceback.print_exc(
+                    file=sys.stdout) + "\n T")
+            append_result(str({"error": "Error with getting available zones",
+                               "error_message": str(err) + "\n Traceback: " + traceback.print_exc(file=sys.stdout)}))
+            traceback.print_exc(file=sys.stdout)
+            return ''
+
 
 def get_instance_private_ip_address(tag_name, instance_name):
     try:
diff --git a/infrastructure-provisioning/src/general/scripts/aws/odahu_deploy.py b/infrastructure-provisioning/src/general/scripts/aws/odahu_deploy.py
new file mode 100644
index 0000000..e7e0363
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/aws/odahu_deploy.py
@@ -0,0 +1,308 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+import base64
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['allowed_cidr'] = os.environ['odahu_allowed_cidr'].split(',')
+    odahu_conf['project_id'] = (os.environ['gcp_project_id'])
+    odahu_conf['region'] = (os.environ['aws_region'])
+    odahu_conf['zone'] = (os.environ['aws_zone'])
+    odahu_conf['edge_user_name'] = os.environ['edge_user_name']
+    if os.environ['aws_region'] == 'us-east':
+        odahu_conf['az_list'] = ["us-east-1", "us-east-2"]
+    elif os.environ['aws_region'] == 'ap-south':
+        odahu_conf['az_list'] = ["ap-south-1"]
+    elif os.environ['aws_region'] == 'us-west':
+        odahu_conf['az_list'] = ["us-west-1", "us-west-2"]
+    elif os.environ['aws_region'] == 'eu-west':
+        odahu_conf['az_list'] = ["eu-west-1", "eu-west-2", "eu-west-3"]
+    elif os.environ['aws_region'] == 'ca-central':
+        odahu_conf['az_list'] = ["ca-central-1"]
+    odahu_conf['az_list'] = GCPMeta().get_available_zones()
+    odahu_conf['dns_zone_name'] = os.environ['odahu_dns_zone_name']
+    odahu_conf['docker_repo'] = os.environ['odahu_docker_repo']
+    odahu_conf['cidr'] = os.environ['odahu_cidr']
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    try:
+        if os.environ['gcp_vpc_name'] == '':
+            raise KeyError
+        else:
+            odahu_conf['vpc_name'] = os.environ['gcp_vpc_name']
+    except KeyError:
+        odahu_conf['vpc_name'] = odahu_conf['service_base_name'] + '-ssn-vpc'
+    odahu_conf['vpc_cidr'] = os.environ['conf_vpc_cidr']
+    odahu_conf['private_subnet_name'] = '{0}-{1}-subnet'.format(odahu_conf['service_base_name'],
+                                                                odahu_conf['project_name'])
+    odahu_conf['grafana_admin'] = os.environ['odahu_grafana_admin']
+    odahu_conf['grafana_pass'] = id_generator()
+    odahu_conf['docker_password'] = base64.b64decode(os.environ['odahu_docker_password'] + "==")
+    odahu_conf['initial_node_count'] = os.environ['odahu_initial_node_count']
+    odahu_conf['istio_helm_repo'] = os.environ['odahu_istio_helm_repo']
+    odahu_conf['helm_repo'] = os.environ['odahu_helm_repo']
+    odahu_conf['k8s_version'] = os.environ['odahu_k8s_version']
+    odahu_conf['oauth_oidc_issuer_url'] = "{}/realms/{}".format(os.environ['keycloak_auth_server_url'],
+                                                                os.environ['keycloak_realm_name'])
+    odahu_conf['oauth_oidc_host'] = os.environ['keycloak_auth_server_url'].replace('https://', '').replace('/auth', '')
+    odahu_conf['oauth_client_id'] = os.environ['keycloak_client_name']
+    odahu_conf['oauth_client_secret'] = os.environ['keycloak_client_secret']
+    odahu_conf['oauth_cookie_secret'] = base64.b64encode(id_generator(16))
+    odahu_conf['oauth_local_jwks'] = os.environ['odahu_oauth_local_jwks']
+    odahu_conf['infra_version'] = os.environ['odahu_infra_version']
+    odahu_conf['odahuflow_version'] = os.environ['odahu_odahuflow_version']
+    odahu_conf['mlflow_toolchain_version'] = os.environ['odahu_mlflow_toolchain_version']
+    odahu_conf['jupyterlab_version'] = os.environ['odahu_jupyterlab_version']
+    odahu_conf['packager_version'] = os.environ['odahu_packager_version']
+    odahu_conf['private_subnet_cidrs'] = os.environ['odahu_private_subnet_cidrs'].split(',')
+    odahu_conf['public_subnet_cidrs'] = os.environ['odahu_public_subnet_cidrs'].split(',')
+    odahu_conf['infra_cidr'] = os.environ['odahu_pods_cidr']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+    odahu_conf['service_cidr'] = os.environ['odahu_service_cidr']
+    odahu_conf['tls_crt'] = base64.b64decode(os.environ['odahu_tls_crt'] + "==")
+    odahu_conf['tls_key'] = base64.b64decode(os.environ['odahu_tls_key'] + "==")
+    odahu_conf['ssh_key'] = os.environ['ssh_key']
+    odahu_conf['dns_project_id'] = os.environ['odahu_dns_project_id']
+    odahu_conf['decrypt_token'] = id_generator()
+    odahu_conf['infra_vpc_peering'] = os.environ['odahu_infra_vpc_peering']
+    odahu_conf['automation_version'] = os.environ['odahu_automation_version']
+    odahu_conf['ui_version'] = os.environ['odahu_ui_version']
+    odahu_conf['examples_version'] = os.environ['odahu_examples_version']
+    odahu_conf['jupyterhub_enabled'] = os.environ['odahu_jupyterhub_enabled']
+    odahu_conf['oauth_mesh_enabled'] = os.environ['odahu_oauth_mesh_enabled']
+    odahu_conf['keysecret'] = os.environ['odahu_keysecret']
+    odahu_conf['airflow_secret'] = os.environ['odahu_airflow_secret']
+    odahu_conf['operator_secret'] =os.environ['odahu_operator_secret']
+    odahu_conf['resource-uploader_secret'] = os.environ['odahu_resource_uploader_secret']
+    odahu_conf['tester_secret'] = os.environ['odahu_tester_secret']
+    odahu_conf['tester-data-scientist_secret'] = os.environ['odahu_tester_data_scientist_secret']
+
+    print('Preparing parameters file')
+    try:
+        local("cp /root/templates/profile.json /tmp/")
+        with open("/tmp/profile.json", 'w') as profile:
+            prof ={
+                    "allowed_ips": odahu_conf['allowed_cidr'],
+                    "authorization_enabled": "true",
+                    "authz_dry_run": "false",
+                    "cloud": {
+                        "aws": {
+                            "az_list": odahu_conf['az_list'],
+                            "zone": "{}".format(odahu_conf['zone']),
+                        },
+                        "region": "{}".format(odahu_conf['region']),
+                        "type": "aws"
+                    },
+                    "cluster_name": "{}".format(odahu_conf['cluster_name']),
+                    "cluster_type": "aws/eks",
+                    "data_bucket": "{}-data-bucket".format(odahu_conf['cluster_name']),
+                    "dns": {
+                        "domain": "odahu.{}.{}".format(odahu_conf['cluster_name'], odahu_conf['root_domain']),
+                        "gcp_credentials": "{}",
+                        "gcp_project_id": "{}".format(odahu_conf['project_id']),
+                        "provider": "gcp",
+                        "zone_name": "{}".format(odahu_conf['dns_zone_name']),
+                    },
+                    "docker_password": "{}".format(odahu_conf['docker_password']),
+                    "docker_repo": "{}".format(odahu_conf['docker_repo']),
+                    "docker_username": "_json_key",
+                    "infra_cidr": "{}".format(odahu_conf['infra_cidr']),
+                    "examples_version": "{}".format(odahu_conf['examples_version']),
+                    "grafana_pass": "{}".format(odahu_conf['grafana_pass']),
+                    "helm_repo": "{}".format(odahu_conf['helm_repo']),
+                    "jupyterhub_enabled": odahu_conf['jupyterhub_enabled'],
+                    "jupyterlab_version": "{}".format(odahu_conf['jupyterlab_version']),
+                    "k8s_version": "{}".format(odahu_conf['k8s_version']),
+                    "mlflow_toolchain_version": "{}".format(odahu_conf['mlflow_toolchain_version']),
+                    "nat_subnet_cidr": "{}".format(odahu_conf['cidr']),
+                    "node_pools": {
+                        "main": {
+                            "init_node_count": 1,
+                            "max_node_count": 7,
+                            "min_node_count": 1
+                        },
+                        "model_deployment": {
+                            "labels": {
+                                "mode": "odahu-flow-deployment"
+                            },
+                            "max_node_count": 3,
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "deployment"
+                                }
+                            ]
+                        },
+                        "packaging": {
+                            "disk_size_gb": 100,
+                            "labels": {
+                                "mode": "odahu-flow-packaging"
+                            },
+                            "max_node_count": 3,
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "packaging"
+                                }
+                            ]
+                        },
+                        "training": {
+                            "disk_size_gb": 50,
+                            "labels": {
+                                "mode": "odahu-flow-training"
+                            },
+                            "machine_type": "c5.2xlarge",
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "training"
+                                }
+                            ]
+                        }
+                    },
+                    "oauth_client_id": "{}".format(odahu_conf['oauth_client_id']),
+                    "oauth_client_secret": "{}".format(odahu_conf['oauth_client_secret']),
+                    "oauth_cookie_secret": "{}".format(odahu_conf['oauth_cookie_secret']),
+                    "oauth_local_jwks": "{}".format(odahu_conf['oauth_local_jwks']),
+                    "oauth_mesh_enabled": odahu_conf['oauth_mesh_enabled'],
+                    "oauth_oidc_audience": "legion",
+                    "oauth_oidc_host": "{}".format(odahu_conf['oauth_oidc_host']),
+                    "oauth_oidc_issuer_url": "{}".format(odahu_conf['oauth_oidc_issuer_url']),
+                    "oauth_oidc_jwks_url": "{}/protocol/openid-connect/certs".format(odahu_conf['oauth_oidc_issuer_url']),
+                    "oauth_oidc_port": 443,
+                    "oauth_oidc_scope": "openid profile email offline_access groups",
+                    "oauth_oidc_token_endpoint": "{}/protocol/openid-connect/token".format(odahu_conf['oauth_oidc_issuer_url']),
+                    "odahu_automation_version": "{}".format(odahu_conf['automation_version']),
+                    "odahu_infra_version": "{}".format(odahu_conf['infra_version']),
+                    "odahu_ui_version": "{}".format(odahu_conf['ui_version']),
+                    "odahuflow_connection_decrypt_token": "{}".format(odahu_conf['decrypt_token']),
+                    "odahuflow_connections": [
+                        {
+                            "id": "odahu-flow-examples",
+                            "spec": {
+                                "description": "Git repository with the Odahu-Flow examples",
+                                "keySecret": "{}".format(odahu_conf['keysecret']),
+                                "reference": "{}".format(odahu_conf['examples_version']),
+                                "type": "git",
+                                "uri": "git@github.com:odahu/odahu-examples.git",
+                                "webUILink": "https://github.com/odahu/odahu-examples"
+                            }
+                        }
+                    ],
+                    "odahuflow_version": "{}".format(odahu_conf['odahuflow_version']),
+                    "opa_policies": {},
+                    "packager_version": "{}".format(odahu_conf['packager_version']),
+                    "private_subnet_cidrs": odahu_conf['private_subnet_cidrs'],
+                    "public_subnet_cidrs": odahu_conf['public_subnet_cidrs'],
+                    "service_accounts": {
+                        "airflow": {
+                            "client_id": "sa-airflow",
+                            "client_secret": "{}".format(odahu_conf['airflow_secret'])
+                        },
+                        "operator": {
+                            "client_id": "sa-operator",
+                            "client_secret": "{}".format(odahu_conf['operator_secret'])
+                        },
+                        "resource_uploader": {
+                            "client_id": "sa-resource-uploader",
+                            "client_secret": "{}".format(odahu_conf['resource-uploader_secret'])
+                        },
+                        "test": {
+                            "client_id": "sa-tester",
+                            "client_secret": "{}".format(odahu_conf['tester_secret'])
+                        },
+                        "test_data_scientist": {
+                            "client_id": "sa-tester-data-scientist",
+                            "client_secret": "{}".format(odahu_conf['tester-data-scientist_secret'])
+                        }
+                    },
+                    "service_cidr": "{}".format(odahu_conf['service_cidr']),
+                    "ssh_key": "{}".format(odahu_conf['ssh_key'].replace('\n', '')),
+                    "subnet_name": "{}".format(odahu_conf['private_subnet_name']),
+                    "tfstate_bucket": "{}-tfstate".format(odahu_conf['cluster_name']),
+                    "tls_crt": "{}".format(odahu_conf['tls_crt']),
+                    "tls_key": "{}".format(odahu_conf['tls_key']),
+                    "vpc_name": "{}".format(odahu_conf['vpc_name']),
+                    "vault": {
+                        "enabled": "true"
+                    }
+                    }
+            profile.write(json.dumps(prof))
+        local('cat /tmp/profile.json')
+        local('cp /tmp/profile.json /')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to configure parameter file.", str(err))
+        GCPActions().remove_bucket(odahu_conf['bucket_name'])
+        GCPActions().remove_static_address(odahu_conf['static_address_name'], odahu_conf['region'])
+        sys.exit(1)
+
+    try:
+        local('tf_runner create -o /tmp/result.json')
+        local("sed -i 's|name|description|g' /tmp/result.json")
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to deploy Odahu cluster.", str(err))
+        GCPActions().remove_bucket(odahu_conf['bucket_name'])
+        GCPActions().remove_static_address(odahu_conf['static_address_name'], odahu_conf['region'])
+        sys.exit(1)
+
+    # generating output information
+    print('[SUMMARY]')
+    logging.info('[SUMMARY]')
+    print('Cluster name: {}'.format(odahu_conf['cluster_name']))
+    with open('/tmp/result.json', 'r') as f:
+        output = json.load(f)
+        odahu_urls = json.dumps(output['odahu_urls']['value'], sort_keys=True, indent=4)
+    print('Odahu urls: {}'.format(odahu_urls))
+    res = dict()
+    res['odahu_urls'] = output['odahu_urls']['value']
+    res['oauth_cookie_secret'] = odahu_conf['oauth_cookie_secret']
+    res['odahuflow_connection_decrypt_token'] = odahu_conf['decrypt_token']
+    res['grafana_pass'] = odahu_conf['grafana_pass']
+    res['grafana_admin'] = odahu_conf['grafana_admin']
+    with open("/root/result.json", 'w') as result:
+        result.write(json.dumps(res))
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/general/scripts/aws/odahu_prepare.py b/infrastructure-provisioning/src/general/scripts/aws/odahu_prepare.py
new file mode 100644
index 0000000..d4168ed
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/aws/odahu_prepare.py
@@ -0,0 +1,155 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+import requests
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['endpoint_name'] = (os.environ['endpoint_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['tag_name'] = '{}-tag'.format(odahu_conf['service_base_name'])
+    odahu_conf['endpoint_tag'] = (os.environ['endpoint_name']).lower().replace('_', '-')
+    odahu_conf['project_tag'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['region'] = os.environ['gcp_region']
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    odahu_conf['keycloak_auth_server_url'] = os.environ['keycloak_auth_server_url']
+    odahu_conf['keycloak_realm_name'] = os.environ['keycloak_realm_name']
+    odahu_conf['keycloak_client_name'] = os.environ['keycloak_client_name']
+    odahu_conf['keycloak_user'] = os.environ['keycloak_user']
+    odahu_conf['keycloak_user_password'] = os.environ['keycloak_user_password']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+    if 'conf_additional_tags' in os.environ:
+        odahu_conf['bucket_additional_tags'] = ';' + os.environ['conf_additional_tags']
+        os.environ['conf_additional_tags'] = os.environ['conf_additional_tags'] + \
+                                             ';project_tag:{0};endpoint_tag:{1};'.format(
+                                                 odahu_conf['project_tag'], odahu_conf['endpoint_tag'])
+    else:
+        odahu_conf['bucket_additional_tags'] = ''
+        os.environ['conf_additional_tags'] = 'project_tag:{0};endpoint_tag:{1}'.format(odahu_conf['project_tag'],
+                                                                                       odahu_conf['endpoint_tag'])
+    print('Additional tags will be added: {}'.format(os.environ['conf_additional_tags']))
+
+
+    try:
+        logging.info('[CREATE STATE BUCKETS]')
+        print('[CREATE STATE BUCKETS]')
+
+        odahu_conf['bucket_tags'] = 'endpoint_tag:{0};{1}:{2};project_tag:{3};{4}:{5}{6}'\
+            .format(odahu_conf['endpoint_tag'], os.environ['conf_billing_tag_key'], os.environ['conf_billing_tag_value'],
+                    odahu_conf['project_tag'], odahu_conf['tag_name'], odahu_conf['bucket_name'],
+                    odahu_conf['bucket_additional_tags']).replace(';', ',')
+
+        params = "--bucket_name {0} --bucket_tags {1} --region {2} --bucket_name_tag {0}". \
+            format(odahu_conf['bucket_name'], odahu_conf['bucket_tags'], odahu_conf['region'])
+        try:
+            local("~/scripts/{}.py {}".format('common_create_bucket', params))
+        except:
+            traceback.print_exc()
+            raise Exception
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        append_result("Unable to create bucket.", str(err))
+        sys.exit(1)
+
+    try:
+        logging.info('[CREATE NAT GATEWAY]')
+        print('[CREATE NAT GATEWAY]')
+        print("Allocating Elastic IP")
+        allocation_id = allocate_elastic_ip()
+        tag = {"Key": odahu_conf['tag_name'], "Value": odahu_conf['static_address_name']}
+        tag_name = {"Key": "Name", "Value": odahu_conf['static_address_name']}
+        create_tag(allocation_id, tag)
+        create_tag(allocation_id, tag_name)
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        append_result("Unable to reserve static ip.", str(err))
+        remove_s3(bucket_type='odahu')
+        sys.exit(1)
+
+    try:
+        print('[CONFIGURE REDIRECT URI]')
+        logging.info('[CONFIGURE REDIRECT URI]')
+        keycloak_auth_server_url = '{}/realms/master/protocol/openid-connect/token'.format(
+            odahu_conf['keycloak_auth_server_url'])
+        keycloak_auth_data = {
+            "username": odahu_conf['keycloak_user'],
+            "password": odahu_conf['keycloak_user_password'],
+            "grant_type": "password",
+            "client_id": "admin-cli",
+        }
+        keycloak_client_create_url = '{0}/admin/realms/{1}/clients'.format(odahu_conf['keycloak_auth_server_url'],
+                                                                           odahu_conf['keycloak_realm_name'])
+        odahu_redirectUris = 'https://odahu.{0}.{1}/*,http://odahu.{0}.{1}/*'.format(odahu_conf['cluster_name'],
+                                                                                        odahu_conf['root_domain']).split(',')
+
+
+        try:
+            keycloak_token = requests.post(keycloak_auth_server_url, data=keycloak_auth_data, verify=False).json()
+            keycloak_get_Uris = requests.get(keycloak_client_create_url,
+                                            headers={"Authorization": "Bearer " + keycloak_token.get("access_token"),
+                                                     "Content-Type": "application/json"}, verify=False).json()
+            for dict in keycloak_get_Uris:
+                if dict["clientId"] == odahu_conf['keycloak_client_name']:
+                    ui_redirectUris = dict["redirectUris"]
+                    keycloak_client_id = dict["id"]
+            keycloak_redirectUris = odahu_redirectUris + ui_redirectUris
+            updated_client_data = {
+                "clientId": odahu_conf['keycloak_client_name'],
+                "id": keycloak_client_id,
+                "enabled": "true",
+                "redirectUris": keycloak_redirectUris,
+                "publicClient": "false",
+                "protocol": "openid-connect",
+            }
+            client_url = "{}/{}".format(keycloak_client_create_url, keycloak_client_id)
+            keycloak_update_Uris = requests.put(client_url, json=updated_client_data,
+                                            headers={"Authorization": "Bearer " + keycloak_token.get("access_token"),
+                                                     "Content-Type": "application/json"}, verify=False)
+        except Exception as err:
+            append_result("Failed to configure keycloak.")
+            sys.exit(1)
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        append_result("Failed to configure keycloak.", str(err))
+        remove_s3(bucket_type='odahu')
+        release_elastic_ip(allocation_id)
+        sys.exit(1)
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/general/scripts/aws/odahu_resume.py b/infrastructure-provisioning/src/general/scripts/aws/odahu_resume.py
new file mode 100644
index 0000000..245336a
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/aws/odahu_resume.py
@@ -0,0 +1,289 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+import string
+import random
+import base64
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['allowed_cidr'] = os.environ['odahu_allowed_cidr'].split(',')
+    odahu_conf['project_id'] = (os.environ['gcp_project_id'])
+    odahu_conf['region'] = (os.environ['aws_region'])
+    odahu_conf['zone'] = (os.environ['aws_zone'])
+    odahu_conf['edge_user_name'] = os.environ['edge_user_name']
+    if os.environ['aws_region'] == 'us-east':
+        odahu_conf['az_list'] = ["us-east-1", "us-east-2"]
+    elif os.environ['aws_region'] == 'ap-south':
+        odahu_conf['az_list'] = ["ap-south-1"]
+    elif os.environ['aws_region'] == 'us-west':
+        odahu_conf['az_list'] = ["us-west-1", "us-west-2"]
+    elif os.environ['aws_region'] == 'eu-west':
+        odahu_conf['az_list'] = ["eu-west-1", "eu-west-2", "eu-west-3"]
+    elif os.environ['aws_region'] == 'ca-central':
+        odahu_conf['az_list'] = ["ca-central-1"]
+    odahu_conf['az_list'] = GCPMeta().get_available_zones()
+    odahu_conf['dns_zone_name'] = os.environ['odahu_dns_zone_name']
+    odahu_conf['docker_repo'] = os.environ['odahu_docker_repo']
+    odahu_conf['cidr'] = os.environ['odahu_cidr']
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    try:
+        if os.environ['gcp_vpc_name'] == '':
+            raise KeyError
+        else:
+            odahu_conf['vpc_name'] = os.environ['gcp_vpc_name']
+    except KeyError:
+        odahu_conf['vpc_name'] = odahu_conf['service_base_name'] + '-ssn-vpc'
+    odahu_conf['vpc_cidr'] = os.environ['conf_vpc_cidr']
+    odahu_conf['private_subnet_name'] = '{0}-{1}-subnet'.format(odahu_conf['service_base_name'],
+                                                                odahu_conf['project_name'])
+    odahu_conf['grafana_admin'] = os.environ['grafana_admin']
+    odahu_conf['grafana_pass'] = os.environ['grafana_pass']
+    odahu_conf['docker_password'] = base64.b64decode(os.environ['odahu_docker_password'] + "==")
+    odahu_conf['initial_node_count'] = os.environ['odahu_initial_node_count']
+    odahu_conf['istio_helm_repo'] = os.environ['odahu_istio_helm_repo']
+    odahu_conf['helm_repo'] = os.environ['odahu_helm_repo']
+    odahu_conf['k8s_version'] = os.environ['odahu_k8s_version']
+    odahu_conf['oauth_oidc_issuer_url'] = "{}/realms/{}".format(os.environ['keycloak_auth_server_url'],
+                                                                os.environ['keycloak_realm_name'])
+    odahu_conf['oauth_oidc_host'] = os.environ['keycloak_auth_server_url'].replace('https://', '').replace('/auth', '')
+    odahu_conf['oauth_client_id'] = os.environ['keycloak_client_name']
+    odahu_conf['oauth_client_secret'] = os.environ['keycloak_client_secret']
+    odahu_conf['oauth_cookie_secret'] = os.environ['oauth_cookie_secret']
+    odahu_conf['oauth_local_jwks'] = os.environ['odahu_oauth_local_jwks']
+    odahu_conf['infra_version'] = os.environ['odahu_infra_version']
+    odahu_conf['odahuflow_version'] = os.environ['odahu_odahuflow_version']
+    odahu_conf['mlflow_toolchain_version'] = os.environ['odahu_mlflow_toolchain_version']
+    odahu_conf['jupyterlab_version'] = os.environ['odahu_jupyterlab_version']
+    odahu_conf['packager_version'] = os.environ['odahu_packager_version']
+    odahu_conf['private_subnet_cidrs'] = os.environ['odahu_private_subnet_cidrs'].split(',')
+    odahu_conf['public_subnet_cidrs'] = os.environ['odahu_public_subnet_cidrs'].split(',')
+    odahu_conf['infra_cidr'] = os.environ['odahu_pods_cidr']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+    odahu_conf['service_cidr'] = os.environ['odahu_service_cidr']
+    odahu_conf['tls_crt'] = base64.b64decode(os.environ['odahu_tls_crt'] + "==")
+    odahu_conf['tls_key'] = base64.b64decode(os.environ['odahu_tls_key'] + "==")
+    odahu_conf['ssh_key'] = os.environ['ssh_key']
+    odahu_conf['dns_project_id'] = os.environ['odahu_dns_project_id']
+    odahu_conf['decrypt_token'] = os.environ['decrypt_token']
+    odahu_conf['infra_vpc_peering'] = os.environ['odahu_infra_vpc_peering']
+    odahu_conf['automation_version'] = os.environ['odahu_automation_version']
+    odahu_conf['ui_version'] = os.environ['odahu_ui_version']
+    odahu_conf['examples_version'] = os.environ['odahu_examples_version']
+    odahu_conf['jupyterhub_enabled'] = os.environ['odahu_jupyterhub_enabled']
+    odahu_conf['oauth_mesh_enabled'] = os.environ['odahu_oauth_mesh_enabled']
+    odahu_conf['keysecret'] = os.environ['odahu_keysecret']
+    odahu_conf['airflow_secret'] = os.environ['odahu_airflow_secret']
+    odahu_conf['operator_secret'] = os.environ['odahu_operator_secret']
+    odahu_conf['resource-uploader_secret'] = os.environ['odahu_resource_uploader_secret']
+    odahu_conf['tester_secret'] = os.environ['odahu_tester_secret']
+    odahu_conf['tester-data-scientist_secret'] = os.environ['odahu_tester_data_scientist_secret']
+
+    print('Preparing parameters file')
+    try:
+        local("cp /root/templates/profile.json /tmp/")
+        with open("/tmp/profile.json", 'w') as profile:
+            prof = {
+                "allowed_ips": odahu_conf['allowed_cidr'],
+                "authorization_enabled": "true",
+                "authz_dry_run": "false",
+                "cloud": {
+                    "aws": {
+                        "az_list": odahu_conf['az_list'],
+                        "zone": "{}".format(odahu_conf['zone']),
+                    },
+                    "region": "{}".format(odahu_conf['region']),
+                    "type": "aws"
+                },
+                "cluster_name": "{}".format(odahu_conf['cluster_name']),
+                "cluster_type": "aws/eks",
+                "data_bucket": "{}-data-bucket".format(odahu_conf['cluster_name']),
+                "dns": {
+                    "domain": "odahu.{}.{}".format(odahu_conf['cluster_name'], odahu_conf['root_domain']),
+                    "gcp_credentials": "{}",
+                    "gcp_project_id": "{}".format(odahu_conf['project_id']),
+                    "provider": "gcp",
+                    "zone_name": "{}".format(odahu_conf['dns_zone_name']),
+                },
+                "docker_password": "{}".format(odahu_conf['docker_password']),
+                "docker_repo": "{}".format(odahu_conf['docker_repo']),
+                "docker_username": "_json_key",
+                "infra_cidr": "{}".format(odahu_conf['infra_cidr']),
+                "examples_version": "{}".format(odahu_conf['examples_version']),
+                "grafana_pass": "{}".format(odahu_conf['grafana_pass']),
+                "helm_repo": "{}".format(odahu_conf['helm_repo']),
+                "jupyterhub_enabled": odahu_conf['jupyterhub_enabled'],
+                "jupyterlab_version": "{}".format(odahu_conf['jupyterlab_version']),
+                "k8s_version": "{}".format(odahu_conf['k8s_version']),
+                "mlflow_toolchain_version": "{}".format(odahu_conf['mlflow_toolchain_version']),
+                "nat_subnet_cidr": "{}".format(odahu_conf['cidr']),
+                "node_pools": {
+                    "main": {
+                        "init_node_count": 1,
+                        "max_node_count": 7,
+                        "min_node_count": 1
+                    },
+                    "model_deployment": {
+                        "labels": {
+                            "mode": "odahu-flow-deployment"
+                        },
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "deployment"
+                            }
+                        ]
+                    },
+                    "packaging": {
+                        "disk_size_gb": 100,
+                        "labels": {
+                            "mode": "odahu-flow-packaging"
+                        },
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "packaging"
+                            }
+                        ]
+                    },
+                    "training": {
+                        "disk_size_gb": 50,
+                        "labels": {
+                            "mode": "odahu-flow-training"
+                        },
+                        "machine_type": "c5.2xlarge",
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "training"
+                            }
+                        ]
+                    }
+                },
+                "oauth_client_id": "{}".format(odahu_conf['oauth_client_id']),
+                "oauth_client_secret": "{}".format(odahu_conf['oauth_client_secret']),
+                "oauth_cookie_secret": "{}".format(odahu_conf['oauth_cookie_secret']),
+                "oauth_local_jwks": "{}".format(odahu_conf['oauth_local_jwks']),
+                "oauth_mesh_enabled": odahu_conf['oauth_mesh_enabled'],
+                "oauth_oidc_audience": "legion",
+                "oauth_oidc_host": "{}".format(odahu_conf['oauth_oidc_host']),
+                "oauth_oidc_issuer_url": "{}".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_jwks_url": "{}/protocol/openid-connect/certs".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_port": 443,
+                "oauth_oidc_scope": "openid profile email offline_access groups",
+                "oauth_oidc_token_endpoint": "{}/protocol/openid-connect/token".format(
+                    odahu_conf['oauth_oidc_issuer_url']),
+                "odahu_automation_version": "{}".format(odahu_conf['automation_version']),
+                "odahu_infra_version": "{}".format(odahu_conf['infra_version']),
+                "odahu_ui_version": "{}".format(odahu_conf['ui_version']),
+                "odahuflow_connection_decrypt_token": "{}".format(odahu_conf['decrypt_token']),
+                "odahuflow_connections": [
+                    {
+                        "id": "odahu-flow-examples",
+                        "spec": {
+                            "description": "Git repository with the Odahu-Flow examples",
+                            "keySecret": "{}".format(odahu_conf['keysecret']),
+                            "reference": "{}".format(odahu_conf['examples_version']),
+                            "type": "git",
+                            "uri": "git@github.com:odahu/odahu-examples.git",
+                            "webUILink": "https://github.com/odahu/odahu-examples"
+                        }
+                    }
+                ],
+                "odahuflow_version": "{}".format(odahu_conf['odahuflow_version']),
+                "opa_policies": {},
+                "packager_version": "{}".format(odahu_conf['packager_version']),
+                "private_subnet_cidrs": odahu_conf['private_subnet_cidrs'],
+                "public_subnet_cidrs": odahu_conf['public_subnet_cidrs'],
+                "service_accounts": {
+                    "airflow": {
+                        "client_id": "sa-airflow",
+                        "client_secret": "{}".format(odahu_conf['airflow_secret'])
+                    },
+                    "operator": {
+                        "client_id": "sa-operator",
+                        "client_secret": "{}".format(odahu_conf['operator_secret'])
+                    },
+                    "resource_uploader": {
+                        "client_id": "sa-resource-uploader",
+                        "client_secret": "{}".format(odahu_conf['resource-uploader_secret'])
+                    },
+                    "test": {
+                        "client_id": "sa-tester",
+                        "client_secret": "{}".format(odahu_conf['tester_secret'])
+                    },
+                    "test_data_scientist": {
+                        "client_id": "sa-tester-data-scientist",
+                        "client_secret": "{}".format(odahu_conf['tester-data-scientist_secret'])
+                    }
+                },
+                "service_cidr": "{}".format(odahu_conf['service_cidr']),
+                "ssh_key": "{}".format(odahu_conf['ssh_key'].replace('\n', '')),
+                "subnet_name": "{}".format(odahu_conf['private_subnet_name']),
+                "tfstate_bucket": "{}-tfstate".format(odahu_conf['cluster_name']),
+                "tls_crt": "{}".format(odahu_conf['tls_crt']),
+                "tls_key": "{}".format(odahu_conf['tls_key']),
+                "vpc_name": "{}".format(odahu_conf['vpc_name']),
+                "vault": {
+                    "enabled": "true"
+                }
+            }
+            profile.write(json.dumps(prof))
+        local('cat /tmp/profile.json')
+        local('cp /tmp/profile.json /')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to configure parameter file.", str(err))
+        sys.exit(1)
+
+    try:
+        local('tf_runner resume -v')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to deploy Odahu cluster.", str(err))
+        sys.exit(1)
diff --git a/infrastructure-provisioning/src/general/scripts/aws/odahu_suspend.py b/infrastructure-provisioning/src/general/scripts/aws/odahu_suspend.py
new file mode 100644
index 0000000..7a1faa8
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/aws/odahu_suspend.py
@@ -0,0 +1,287 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+import base64
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['allowed_cidr'] = os.environ['odahu_allowed_cidr'].split(',')
+    odahu_conf['project_id'] = (os.environ['gcp_project_id'])
+    odahu_conf['region'] = (os.environ['aws_region'])
+    odahu_conf['zone'] = (os.environ['aws_zone'])
+    odahu_conf['edge_user_name'] = os.environ['edge_user_name']
+    if os.environ['aws_region'] == 'us-east':
+        odahu_conf['az_list'] = ["us-east-1", "us-east-2"]
+    elif os.environ['aws_region'] == 'ap-south':
+        odahu_conf['az_list'] = ["ap-south-1"]
+    elif os.environ['aws_region'] == 'us-west':
+        odahu_conf['az_list'] = ["us-west-1", "us-west-2"]
+    elif os.environ['aws_region'] == 'eu-west':
+        odahu_conf['az_list'] = ["eu-west-1", "eu-west-2", "eu-west-3"]
+    elif os.environ['aws_region'] == 'ca-central':
+        odahu_conf['az_list'] = ["ca-central-1"]
+    odahu_conf['az_list'] = GCPMeta().get_available_zones()
+    odahu_conf['dns_zone_name'] = os.environ['odahu_dns_zone_name']
+    odahu_conf['docker_repo'] = os.environ['odahu_docker_repo']
+    odahu_conf['cidr'] = os.environ['odahu_cidr']
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    try:
+        if os.environ['gcp_vpc_name'] == '':
+            raise KeyError
+        else:
+            odahu_conf['vpc_name'] = os.environ['gcp_vpc_name']
+    except KeyError:
+        odahu_conf['vpc_name'] = odahu_conf['service_base_name'] + '-ssn-vpc'
+    odahu_conf['vpc_cidr'] = os.environ['conf_vpc_cidr']
+    odahu_conf['private_subnet_name'] = '{0}-{1}-subnet'.format(odahu_conf['service_base_name'],
+                                                                odahu_conf['project_name'])
+    odahu_conf['grafana_admin'] = os.environ['grafana_admin']
+    odahu_conf['grafana_pass'] = os.environ['grafana_pass']
+    odahu_conf['docker_password'] = base64.b64decode(os.environ['odahu_docker_password'] + "==")
+    odahu_conf['initial_node_count'] = os.environ['odahu_initial_node_count']
+    odahu_conf['istio_helm_repo'] = os.environ['odahu_istio_helm_repo']
+    odahu_conf['helm_repo'] = os.environ['odahu_helm_repo']
+    odahu_conf['k8s_version'] = os.environ['odahu_k8s_version']
+    odahu_conf['oauth_oidc_issuer_url'] = "{}/realms/{}".format(os.environ['keycloak_auth_server_url'],
+                                                                os.environ['keycloak_realm_name'])
+    odahu_conf['oauth_oidc_host'] = os.environ['keycloak_auth_server_url'].replace('https://', '').replace('/auth', '')
+    odahu_conf['oauth_client_id'] = os.environ['keycloak_client_name']
+    odahu_conf['oauth_client_secret'] = os.environ['keycloak_client_secret']
+    odahu_conf['oauth_cookie_secret'] = os.environ['oauth_cookie_secret']
+    odahu_conf['oauth_local_jwks'] = os.environ['odahu_oauth_local_jwks']
+    odahu_conf['infra_version'] = os.environ['odahu_infra_version']
+    odahu_conf['odahuflow_version'] = os.environ['odahu_odahuflow_version']
+    odahu_conf['mlflow_toolchain_version'] = os.environ['odahu_mlflow_toolchain_version']
+    odahu_conf['jupyterlab_version'] = os.environ['odahu_jupyterlab_version']
+    odahu_conf['packager_version'] = os.environ['odahu_packager_version']
+    odahu_conf['private_subnet_cidrs'] = os.environ['odahu_private_subnet_cidrs'].split(',')
+    odahu_conf['public_subnet_cidrs'] = os.environ['odahu_public_subnet_cidrs'].split(',')
+    odahu_conf['infra_cidr'] = os.environ['odahu_pods_cidr']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+    odahu_conf['service_cidr'] = os.environ['odahu_service_cidr']
+    odahu_conf['tls_crt'] = base64.b64decode(os.environ['odahu_tls_crt'] + "==")
+    odahu_conf['tls_key'] = base64.b64decode(os.environ['odahu_tls_key'] + "==")
+    odahu_conf['ssh_key'] = os.environ['ssh_key']
+    odahu_conf['dns_project_id'] = os.environ['odahu_dns_project_id']
+    odahu_conf['decrypt_token'] = os.environ['decrypt_token']
+    odahu_conf['infra_vpc_peering'] = os.environ['odahu_infra_vpc_peering']
+    odahu_conf['automation_version'] = os.environ['odahu_automation_version']
+    odahu_conf['ui_version'] = os.environ['odahu_ui_version']
+    odahu_conf['examples_version'] = os.environ['odahu_examples_version']
+    odahu_conf['jupyterhub_enabled'] = os.environ['odahu_jupyterhub_enabled']
+    odahu_conf['oauth_mesh_enabled'] = os.environ['odahu_oauth_mesh_enabled']
+    odahu_conf['keysecret'] = os.environ['odahu_keysecret']
+    odahu_conf['airflow_secret'] = os.environ['odahu_airflow_secret']
+    odahu_conf['operator_secret'] = os.environ['odahu_operator_secret']
+    odahu_conf['resource-uploader_secret'] = os.environ['odahu_resource_uploader_secret']
+    odahu_conf['tester_secret'] = os.environ['odahu_tester_secret']
+    odahu_conf['tester-data-scientist_secret'] = os.environ['odahu_tester_data_scientist_secret']
+
+    print('Preparing parameters file')
+    try:
+        local("cp /root/templates/profile.json /tmp/")
+        with open("/tmp/profile.json", 'w') as profile:
+            prof = {
+                "allowed_ips": odahu_conf['allowed_cidr'],
+                "authorization_enabled": "true",
+                "authz_dry_run": "false",
+                "cloud": {
+                    "aws": {
+                        "az_list": odahu_conf['az_list'],
+                        "zone": "{}".format(odahu_conf['zone']),
+                    },
+                    "region": "{}".format(odahu_conf['region']),
+                    "type": "aws"
+                },
+                "cluster_name": "{}".format(odahu_conf['cluster_name']),
+                "cluster_type": "aws/eks",
+                "data_bucket": "{}-data-bucket".format(odahu_conf['cluster_name']),
+                "dns": {
+                    "domain": "odahu.{}.{}".format(odahu_conf['cluster_name'], odahu_conf['root_domain']),
+                    "gcp_credentials": "{}",
+                    "gcp_project_id": "{}".format(odahu_conf['project_id']),
+                    "provider": "gcp",
+                    "zone_name": "{}".format(odahu_conf['dns_zone_name']),
+                },
+                "docker_password": "{}".format(odahu_conf['docker_password']),
+                "docker_repo": "{}".format(odahu_conf['docker_repo']),
+                "docker_username": "_json_key",
+                "infra_cidr": "{}".format(odahu_conf['infra_cidr']),
+                "examples_version": "{}".format(odahu_conf['examples_version']),
+                "grafana_pass": "{}".format(odahu_conf['grafana_pass']),
+                "helm_repo": "{}".format(odahu_conf['helm_repo']),
+                "jupyterhub_enabled": odahu_conf['jupyterhub_enabled'],
+                "jupyterlab_version": "{}".format(odahu_conf['jupyterlab_version']),
+                "k8s_version": "{}".format(odahu_conf['k8s_version']),
+                "mlflow_toolchain_version": "{}".format(odahu_conf['mlflow_toolchain_version']),
+                "nat_subnet_cidr": "{}".format(odahu_conf['cidr']),
+                "node_pools": {
+                    "main": {
+                        "init_node_count": 1,
+                        "max_node_count": 7,
+                        "min_node_count": 1
+                    },
+                    "model_deployment": {
+                        "labels": {
+                            "mode": "odahu-flow-deployment"
+                        },
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "deployment"
+                            }
+                        ]
+                    },
+                    "packaging": {
+                        "disk_size_gb": 100,
+                        "labels": {
+                            "mode": "odahu-flow-packaging"
+                        },
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "packaging"
+                            }
+                        ]
+                    },
+                    "training": {
+                        "disk_size_gb": 50,
+                        "labels": {
+                            "mode": "odahu-flow-training"
+                        },
+                        "machine_type": "c5.2xlarge",
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "training"
+                            }
+                        ]
+                    }
+                },
+                "oauth_client_id": "{}".format(odahu_conf['oauth_client_id']),
+                "oauth_client_secret": "{}".format(odahu_conf['oauth_client_secret']),
+                "oauth_cookie_secret": "{}".format(odahu_conf['oauth_cookie_secret']),
+                "oauth_local_jwks": "{}".format(odahu_conf['oauth_local_jwks']),
+                "oauth_mesh_enabled": odahu_conf['oauth_mesh_enabled'],
+                "oauth_oidc_audience": "legion",
+                "oauth_oidc_host": "{}".format(odahu_conf['oauth_oidc_host']),
+                "oauth_oidc_issuer_url": "{}".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_jwks_url": "{}/protocol/openid-connect/certs".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_port": 443,
+                "oauth_oidc_scope": "openid profile email offline_access groups",
+                "oauth_oidc_token_endpoint": "{}/protocol/openid-connect/token".format(
+                    odahu_conf['oauth_oidc_issuer_url']),
+                "odahu_automation_version": "{}".format(odahu_conf['automation_version']),
+                "odahu_infra_version": "{}".format(odahu_conf['infra_version']),
+                "odahu_ui_version": "{}".format(odahu_conf['ui_version']),
+                "odahuflow_connection_decrypt_token": "{}".format(odahu_conf['decrypt_token']),
+                "odahuflow_connections": [
+                    {
+                        "id": "odahu-flow-examples",
+                        "spec": {
+                            "description": "Git repository with the Odahu-Flow examples",
+                            "keySecret": "{}".format(odahu_conf['keysecret']),
+                            "reference": "{}".format(odahu_conf['examples_version']),
+                            "type": "git",
+                            "uri": "git@github.com:odahu/odahu-examples.git",
+                            "webUILink": "https://github.com/odahu/odahu-examples"
+                        }
+                    }
+                ],
+                "odahuflow_version": "{}".format(odahu_conf['odahuflow_version']),
+                "opa_policies": {},
+                "packager_version": "{}".format(odahu_conf['packager_version']),
+                "private_subnet_cidrs": odahu_conf['private_subnet_cidrs'],
+                "public_subnet_cidrs": odahu_conf['public_subnet_cidrs'],
+                "service_accounts": {
+                    "airflow": {
+                        "client_id": "sa-airflow",
+                        "client_secret": "{}".format(odahu_conf['airflow_secret'])
+                    },
+                    "operator": {
+                        "client_id": "sa-operator",
+                        "client_secret": "{}".format(odahu_conf['operator_secret'])
+                    },
+                    "resource_uploader": {
+                        "client_id": "sa-resource-uploader",
+                        "client_secret": "{}".format(odahu_conf['resource-uploader_secret'])
+                    },
+                    "test": {
+                        "client_id": "sa-tester",
+                        "client_secret": "{}".format(odahu_conf['tester_secret'])
+                    },
+                    "test_data_scientist": {
+                        "client_id": "sa-tester-data-scientist",
+                        "client_secret": "{}".format(odahu_conf['tester-data-scientist_secret'])
+                    }
+                },
+                "service_cidr": "{}".format(odahu_conf['service_cidr']),
+                "ssh_key": "{}".format(odahu_conf['ssh_key'].replace('\n', '')),
+                "subnet_name": "{}".format(odahu_conf['private_subnet_name']),
+                "tfstate_bucket": "{}-tfstate".format(odahu_conf['cluster_name']),
+                "tls_crt": "{}".format(odahu_conf['tls_crt']),
+                "tls_key": "{}".format(odahu_conf['tls_key']),
+                "vpc_name": "{}".format(odahu_conf['vpc_name']),
+                "vault": {
+                    "enabled": "true"
+                }
+            }
+            profile.write(json.dumps(prof))
+        local('cat /tmp/profile.json')
+        local('cp /tmp/profile.json /')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to configure parameter file.", str(err))
+        sys.exit(1)
+
+    try:
+        local('tf_runner suspend -v')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to suspend Odahu cluster.", str(err))
+        sys.exit(1)
diff --git a/infrastructure-provisioning/src/general/scripts/aws/odahu_terminate.py b/infrastructure-provisioning/src/general/scripts/aws/odahu_terminate.py
new file mode 100644
index 0000000..20f5468
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/aws/odahu_terminate.py
@@ -0,0 +1,307 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+import base64
+
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['allowed_cidr'] = os.environ['odahu_allowed_cidr'].split(',')
+    odahu_conf['project_id'] = (os.environ['gcp_project_id'])
+    odahu_conf['region'] = (os.environ['aws_region'])
+    odahu_conf['zone'] = (os.environ['aws_zone'])
+    odahu_conf['edge_user_name'] = os.environ['edge_user_name']
+    if os.environ['aws_region'] == 'us-east':
+        odahu_conf['az_list'] = ["us-east-1", "us-east-2"]
+    elif os.environ['aws_region'] == 'ap-south':
+        odahu_conf['az_list'] = ["ap-south-1"]
+    elif os.environ['aws_region'] == 'us-west':
+        odahu_conf['az_list'] = ["us-west-1", "us-west-2"]
+    elif os.environ['aws_region'] == 'eu-west':
+        odahu_conf['az_list'] = ["eu-west-1", "eu-west-2", "eu-west-3"]
+    elif os.environ['aws_region'] == 'ca-central':
+        odahu_conf['az_list'] = ["ca-central-1"]
+    odahu_conf['az_list'] = GCPMeta().get_available_zones()
+    odahu_conf['dns_zone_name'] = os.environ['odahu_dns_zone_name']
+    odahu_conf['docker_repo'] = os.environ['odahu_docker_repo']
+    odahu_conf['cidr'] = os.environ['odahu_cidr']
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    try:
+        if os.environ['gcp_vpc_name'] == '':
+            raise KeyError
+        else:
+            odahu_conf['vpc_name'] = os.environ['gcp_vpc_name']
+    except KeyError:
+        odahu_conf['vpc_name'] = odahu_conf['service_base_name'] + '-ssn-vpc'
+    odahu_conf['vpc_cidr'] = os.environ['conf_vpc_cidr']
+    odahu_conf['private_subnet_name'] = '{0}-{1}-subnet'.format(odahu_conf['service_base_name'],
+                                                                odahu_conf['project_name'])
+    odahu_conf['grafana_admin'] = os.environ['grafana_admin']
+    odahu_conf['grafana_pass'] = os.environ['grafana_pass']
+    odahu_conf['docker_password'] = base64.b64decode(os.environ['odahu_docker_password'] + "==")
+    odahu_conf['initial_node_count'] = os.environ['odahu_initial_node_count']
+    odahu_conf['istio_helm_repo'] = os.environ['odahu_istio_helm_repo']
+    odahu_conf['helm_repo'] = os.environ['odahu_helm_repo']
+    odahu_conf['k8s_version'] = os.environ['odahu_k8s_version']
+    odahu_conf['oauth_oidc_issuer_url'] = "{}/realms/{}".format(os.environ['keycloak_auth_server_url'],
+                                                                os.environ['keycloak_realm_name'])
+    odahu_conf['oauth_oidc_host'] = os.environ['keycloak_auth_server_url'].replace('https://', '').replace('/auth', '')
+    odahu_conf['oauth_client_id'] = os.environ['keycloak_client_name']
+    odahu_conf['oauth_client_secret'] = os.environ['keycloak_client_secret']
+    odahu_conf['oauth_cookie_secret'] = os.environ['oauth_cookie_secret']
+    odahu_conf['oauth_local_jwks'] = os.environ['odahu_oauth_local_jwks']
+    odahu_conf['infra_version'] = os.environ['odahu_infra_version']
+    odahu_conf['odahuflow_version'] = os.environ['odahu_odahuflow_version']
+    odahu_conf['mlflow_toolchain_version'] = os.environ['odahu_mlflow_toolchain_version']
+    odahu_conf['jupyterlab_version'] = os.environ['odahu_jupyterlab_version']
+    odahu_conf['packager_version'] = os.environ['odahu_packager_version']
+    odahu_conf['private_subnet_cidrs'] = os.environ['odahu_private_subnet_cidrs'].split(',')
+    odahu_conf['public_subnet_cidrs'] = os.environ['odahu_public_subnet_cidrs'].split(',')
+    odahu_conf['infra_cidr'] = os.environ['odahu_pods_cidr']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+    odahu_conf['service_cidr'] = os.environ['odahu_service_cidr']
+    odahu_conf['tls_crt'] = base64.b64decode(os.environ['odahu_tls_crt'] + "==")
+    odahu_conf['tls_key'] = base64.b64decode(os.environ['odahu_tls_key'] + "==")
+    odahu_conf['ssh_key'] = os.environ['ssh_key']
+    odahu_conf['dns_project_id'] = os.environ['odahu_dns_project_id']
+    odahu_conf['decrypt_token'] = os.environ['decrypt_token']
+    odahu_conf['infra_vpc_peering'] = os.environ['odahu_infra_vpc_peering']
+    odahu_conf['automation_version'] = os.environ['odahu_automation_version']
+    odahu_conf['ui_version'] = os.environ['odahu_ui_version']
+    odahu_conf['examples_version'] = os.environ['odahu_examples_version']
+    odahu_conf['jupyterhub_enabled'] = os.environ['odahu_jupyterhub_enabled']
+    odahu_conf['oauth_mesh_enabled'] = os.environ['odahu_oauth_mesh_enabled']
+    odahu_conf['keysecret'] = os.environ['odahu_keysecret']
+    odahu_conf['airflow_secret'] = os.environ['odahu_airflow_secret']
+    odahu_conf['operator_secret'] = os.environ['odahu_operator_secret']
+    odahu_conf['resource-uploader_secret'] = os.environ['odahu_resource_uploader_secret']
+    odahu_conf['tester_secret'] = os.environ['odahu_tester_secret']
+    odahu_conf['tester-data-scientist_secret'] = os.environ['odahu_tester_data_scientist_secret']
+
+    print('Preparing parameters file')
+    try:
+        local("cp /root/templates/profile.json /tmp/")
+        with open("/tmp/profile.json", 'w') as profile:
+            prof = {
+                "allowed_ips": odahu_conf['allowed_cidr'],
+                "authorization_enabled": "true",
+                "authz_dry_run": "false",
+                "cloud": {
+                    "aws": {
+                        "az_list": odahu_conf['az_list'],
+                        "zone": "{}".format(odahu_conf['zone']),
+                    },
+                    "region": "{}".format(odahu_conf['region']),
+                    "type": "aws"
+                },
+                "cluster_name": "{}".format(odahu_conf['cluster_name']),
+                "cluster_type": "aws/eks",
+                "data_bucket": "{}-data-bucket".format(odahu_conf['cluster_name']),
+                "dns": {
+                    "domain": "odahu.{}.{}".format(odahu_conf['cluster_name'], odahu_conf['root_domain']),
+                    "gcp_credentials": "{}",
+                    "gcp_project_id": "{}".format(odahu_conf['project_id']),
+                    "provider": "gcp",
+                    "zone_name": "{}".format(odahu_conf['dns_zone_name']),
+                },
+                "docker_password": "{}".format(odahu_conf['docker_password']),
+                "docker_repo": "{}".format(odahu_conf['docker_repo']),
+                "docker_username": "_json_key",
+                "infra_cidr": "{}".format(odahu_conf['infra_cidr']),
+                "examples_version": "{}".format(odahu_conf['examples_version']),
+                "grafana_pass": "{}".format(odahu_conf['grafana_pass']),
+                "helm_repo": "{}".format(odahu_conf['helm_repo']),
+                "jupyterhub_enabled": odahu_conf['jupyterhub_enabled'],
+                "jupyterlab_version": "{}".format(odahu_conf['jupyterlab_version']),
+                "k8s_version": "{}".format(odahu_conf['k8s_version']),
+                "mlflow_toolchain_version": "{}".format(odahu_conf['mlflow_toolchain_version']),
+                "nat_subnet_cidr": "{}".format(odahu_conf['cidr']),
+                "node_pools": {
+                    "main": {
+                        "init_node_count": 1,
+                        "max_node_count": 7,
+                        "min_node_count": 1
+                    },
+                    "model_deployment": {
+                        "labels": {
+                            "mode": "odahu-flow-deployment"
+                        },
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "deployment"
+                            }
+                        ]
+                    },
+                    "packaging": {
+                        "disk_size_gb": 100,
+                        "labels": {
+                            "mode": "odahu-flow-packaging"
+                        },
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "packaging"
+                            }
+                        ]
+                    },
+                    "training": {
+                        "disk_size_gb": 50,
+                        "labels": {
+                            "mode": "odahu-flow-training"
+                        },
+                        "machine_type": "c5.2xlarge",
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "training"
+                            }
+                        ]
+                    }
+                },
+                "oauth_client_id": "{}".format(odahu_conf['oauth_client_id']),
+                "oauth_client_secret": "{}".format(odahu_conf['oauth_client_secret']),
+                "oauth_cookie_secret": "{}".format(odahu_conf['oauth_cookie_secret']),
+                "oauth_local_jwks": "{}".format(odahu_conf['oauth_local_jwks']),
+                "oauth_mesh_enabled": odahu_conf['oauth_mesh_enabled'],
+                "oauth_oidc_audience": "legion",
+                "oauth_oidc_host": "{}".format(odahu_conf['oauth_oidc_host']),
+                "oauth_oidc_issuer_url": "{}".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_jwks_url": "{}/protocol/openid-connect/certs".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_port": 443,
+                "oauth_oidc_scope": "openid profile email offline_access groups",
+                "oauth_oidc_token_endpoint": "{}/protocol/openid-connect/token".format(
+                    odahu_conf['oauth_oidc_issuer_url']),
+                "odahu_automation_version": "{}".format(odahu_conf['automation_version']),
+                "odahu_infra_version": "{}".format(odahu_conf['infra_version']),
+                "odahu_ui_version": "{}".format(odahu_conf['ui_version']),
+                "odahuflow_connection_decrypt_token": "{}".format(odahu_conf['decrypt_token']),
+                "odahuflow_connections": [
+                    {
+                        "id": "odahu-flow-examples",
+                        "spec": {
+                            "description": "Git repository with the Odahu-Flow examples",
+                            "keySecret": "{}".format(odahu_conf['keysecret']),
+                            "reference": "{}".format(odahu_conf['examples_version']),
+                            "type": "git",
+                            "uri": "git@github.com:odahu/odahu-examples.git",
+                            "webUILink": "https://github.com/odahu/odahu-examples"
+                        }
+                    }
+                ],
+                "odahuflow_version": "{}".format(odahu_conf['odahuflow_version']),
+                "opa_policies": {},
+                "packager_version": "{}".format(odahu_conf['packager_version']),
+                "private_subnet_cidrs": odahu_conf['private_subnet_cidrs'],
+                "public_subnet_cidrs": odahu_conf['public_subnet_cidrs'],
+                "service_accounts": {
+                    "airflow": {
+                        "client_id": "sa-airflow",
+                        "client_secret": "{}".format(odahu_conf['airflow_secret'])
+                    },
+                    "operator": {
+                        "client_id": "sa-operator",
+                        "client_secret": "{}".format(odahu_conf['operator_secret'])
+                    },
+                    "resource_uploader": {
+                        "client_id": "sa-resource-uploader",
+                        "client_secret": "{}".format(odahu_conf['resource-uploader_secret'])
+                    },
+                    "test": {
+                        "client_id": "sa-tester",
+                        "client_secret": "{}".format(odahu_conf['tester_secret'])
+                    },
+                    "test_data_scientist": {
+                        "client_id": "sa-tester-data-scientist",
+                        "client_secret": "{}".format(odahu_conf['tester-data-scientist_secret'])
+                    }
+                },
+                "service_cidr": "{}".format(odahu_conf['service_cidr']),
+                "ssh_key": "{}".format(odahu_conf['ssh_key'].replace('\n', '')),
+                "subnet_name": "{}".format(odahu_conf['private_subnet_name']),
+                "tfstate_bucket": "{}-tfstate".format(odahu_conf['cluster_name']),
+                "tls_crt": "{}".format(odahu_conf['tls_crt']),
+                "tls_key": "{}".format(odahu_conf['tls_key']),
+                "vpc_name": "{}".format(odahu_conf['vpc_name']),
+                "vault": {
+                    "enabled": "true"
+                }
+            }
+            profile.write(json.dumps(prof))
+        local('cat /tmp/profile.json')
+        local('cp /tmp/profile.json /')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to configure parameter file.", str(err))
+        sys.exit(1)
+
+    print('Removing Odahu cluster')
+    try:
+        local('tf_runner destroy')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to terminate Odahu cluster.", str(err))
+        sys.exit(1)
+
+    try:
+        buckets = GCPMeta().get_list_buckets(odahu_conf['cluster_name'])
+        if 'items' in buckets:
+            for i in buckets['items']:
+                GCPActions().remove_bucket(i['name'])
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        sys.exit(1)
+
+    try:
+        static_addresses = GCPMeta().get_list_static_addresses(odahu_conf['region'], odahu_conf['cluster_name'])
+        if 'items' in static_addresses:
+            for i in static_addresses['items']:
+                GCPActions().remove_static_address(i['name'], odahu_conf['region'])
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        sys.exit(1)
diff --git a/infrastructure-provisioning/src/general/scripts/aws/ssn_configure.py b/infrastructure-provisioning/src/general/scripts/aws/ssn_configure.py
index 87587d8..64fd85b 100644
--- a/infrastructure-provisioning/src/general/scripts/aws/ssn_configure.py
+++ b/infrastructure-provisioning/src/general/scripts/aws/ssn_configure.py
@@ -243,6 +243,7 @@
         additional_config = [{"name": "base", "tag": "latest"},
                              {"name": "edge", "tag": "latest"},
                              {"name": "project", "tag": "latest"},
+                             {"name": "odahu", "tag": "latest"},
                              {"name": "jupyter", "tag": "latest"},
                              {"name": "rstudio", "tag": "latest"},
                              {"name": "zeppelin", "tag": "latest"},
@@ -253,13 +254,13 @@
                              {"name": "dataengine-service", "tag": "latest"},
                              {"name": "dataengine", "tag": "latest"}]
         params = "--hostname {} --keyfile {} --additional_config '{}' --os_family {} --os_user {} --dlab_path {} " \
-                 "--cloud_provider {} --region {}".format(ssn_conf['instance_hostname'],
+                 "--cloud_provider {} --region {} --gcr_creds {} --odahu_image {}".format(ssn_conf['instance_hostname'],
                                                           "{}{}.pem".format(os.environ['conf_key_dir'],
                                                                             os.environ['conf_key_name']),
                                                           json.dumps(additional_config), os.environ['conf_os_family'],
                                                           ssn_conf['dlab_ssh_user'], os.environ['ssn_dlab_path'],
-                                                          os.environ['conf_cloud_provider'], os.environ['aws_region'])
-
+                                                          os.environ['conf_cloud_provider'], os.environ['aws_region'],
+                                                          os.environ['ssn_gcr_creds'], os.environ['odahu_deploy_image'])
         try:
             local("~/scripts/{}.py {}".format('configure_docker', params))
         except:
diff --git a/infrastructure-provisioning/src/general/scripts/gcp/odahu_deploy.py b/infrastructure-provisioning/src/general/scripts/gcp/odahu_deploy.py
new file mode 100644
index 0000000..0277d43
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/gcp/odahu_deploy.py
@@ -0,0 +1,319 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+import base64
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['allowed_cidr'] = os.environ['odahu_allowed_cidr'].split(',')
+    odahu_conf['project_id'] = (os.environ['gcp_project_id'])
+    odahu_conf['region'] = (os.environ['gcp_region'])
+    odahu_conf['zone'] = (os.environ['gcp_zone'])
+    odahu_conf['edge_user_name'] = os.environ['edge_user_name']
+    odahu_conf['node_locations'] = GCPMeta().get_available_zones()
+    odahu_conf['dns_zone_name'] = os.environ['odahu_dns_zone_name']
+    odahu_conf['docker_repo'] = os.environ['odahu_docker_repo']
+    odahu_conf['cidr'] = os.environ['odahu_cidr']
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    try:
+        if os.environ['gcp_vpc_name'] == '':
+            raise KeyError
+        else:
+            odahu_conf['vpc_name'] = os.environ['gcp_vpc_name']
+    except KeyError:
+        odahu_conf['vpc_name'] = odahu_conf['service_base_name'] + '-ssn-vpc'
+    odahu_conf['vpc_cidr'] = os.environ['conf_vpc_cidr']
+    odahu_conf['private_subnet_name'] = '{0}-{1}-subnet'.format(odahu_conf['service_base_name'],
+                                                                odahu_conf['project_name'])
+    odahu_conf['grafana_admin'] = os.environ['odahu_grafana_admin']
+    odahu_conf['grafana_pass'] = id_generator()
+    odahu_conf['docker_password'] = base64.b64decode(os.environ['odahu_docker_password'] + "==")
+    odahu_conf['initial_node_count'] = os.environ['odahu_initial_node_count']
+    odahu_conf['istio_helm_repo'] = os.environ['odahu_istio_helm_repo']
+    odahu_conf['helm_repo'] = os.environ['odahu_helm_repo']
+    odahu_conf['k8s_version'] = os.environ['odahu_k8s_version']
+    odahu_conf['oauth_oidc_issuer_url'] = "{}/realms/{}".format(os.environ['keycloak_auth_server_url'],
+                                                                os.environ['keycloak_realm_name'])
+    odahu_conf['oauth_oidc_host'] = os.environ['keycloak_auth_server_url'].replace('https://', '').replace('/auth', '')
+    odahu_conf['oauth_client_id'] = os.environ['keycloak_client_name']
+    odahu_conf['oauth_client_secret'] = os.environ['keycloak_client_secret']
+    odahu_conf['oauth_cookie_secret'] = base64.b64encode(id_generator(16))
+    odahu_conf['oauth_local_jwks'] = os.environ['odahu_oauth_local_jwks']
+    odahu_conf['infra_version'] = os.environ['odahu_infra_version']
+    odahu_conf['odahuflow_version'] = os.environ['odahu_odahuflow_version']
+    odahu_conf['mlflow_toolchain_version'] = os.environ['odahu_mlflow_toolchain_version']
+    odahu_conf['jupyterlab_version'] = os.environ['odahu_jupyterlab_version']
+    odahu_conf['packager_version'] = os.environ['odahu_packager_version']
+    odahu_conf['node_version'] = os.environ['odahu_node_version']
+    odahu_conf['pods_cidr'] = os.environ['odahu_pods_cidr']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+    odahu_conf['service_cidr'] = os.environ['odahu_service_cidr']
+    odahu_conf['tls_crt'] = base64.b64decode(os.environ['odahu_tls_crt'] + "==")
+    odahu_conf['tls_key'] = base64.b64decode(os.environ['odahu_tls_key'] + "==")
+    odahu_conf['ssh_key'] = os.environ['ssh_key']
+    odahu_conf['dns_project_id'] = os.environ['odahu_dns_project_id']
+    odahu_conf['decrypt_token'] = id_generator()
+    odahu_conf['infra_vpc_peering'] = os.environ['odahu_infra_vpc_peering']
+    odahu_conf['automation_version'] = os.environ['odahu_automation_version']
+    odahu_conf['ui_version'] = os.environ['odahu_ui_version']
+    odahu_conf['examples_version'] = os.environ['odahu_examples_version']
+    odahu_conf['jupyterhub_enabled'] = os.environ['odahu_jupyterhub_enabled']
+    odahu_conf['oauth_mesh_enabled'] = os.environ['odahu_oauth_mesh_enabled']
+    odahu_conf['keysecret'] = os.environ['odahu_keysecret']
+    odahu_conf['airflow_secret'] = os.environ['odahu_airflow_secret']
+    odahu_conf['operator_secret'] =os.environ['odahu_operator_secret']
+    odahu_conf['resource-uploader_secret'] = os.environ['odahu_resource_uploader_secret']
+    odahu_conf['tester_secret'] = os.environ['odahu_tester_secret']
+    odahu_conf['tester-data-scientist_secret'] = os.environ['odahu_tester_data_scientist_secret']
+
+    print('Preparing parameters file')
+    try:
+        local("cp /root/templates/profile.json /tmp/")
+        with open("/tmp/profile.json", 'w') as profile:
+            prof ={
+                    "allowed_ips": odahu_conf['allowed_cidr'],
+                    "authorization_enabled": "true",
+                    "authz_dry_run": "false",
+                    "cloud": {
+                        "gcp": {
+                            "node_locations": odahu_conf['node_locations'],
+                            "project_id": "{}".format(odahu_conf['project_id']),
+                            "region": "{}".format(odahu_conf['region']),
+                            "zone": "{}".format(odahu_conf['zone']),
+                        },
+                        "type": "gcp"
+                    },
+                    "cluster_name": "{}".format(odahu_conf['cluster_name']),
+                    "cluster_type": "gcp/gke",
+                    "data_bucket": "{}-data-bucket".format(odahu_conf['cluster_name']),
+
+                    "dns": {
+                        "domain": "odahu.{}.{}".format(odahu_conf['cluster_name'], odahu_conf['root_domain']),
+                        "gcp_project_id": "{}".format(odahu_conf['project_id']),
+                        "provider": "gcp",
+                        "zone_name": "{}".format(odahu_conf['dns_zone_name']),
+                    },
+                    "docker_password": "{}".format(odahu_conf['docker_password']),
+                    "docker_repo": "{}".format(odahu_conf['docker_repo']),
+                    "docker_username": "_json_key",
+                    "gcp_cidr": "{}".format(odahu_conf['cidr']),
+                    "examples_version": "{}".format(odahu_conf['examples_version']),
+                    "grafana_pass": "{}".format(odahu_conf['grafana_pass']),
+                    "helm_repo": "{}".format(odahu_conf['helm_repo']),
+                    "jupyterhub_enabled": odahu_conf['jupyterhub_enabled'],
+                    "jupyterlab_version": "{}".format(odahu_conf['jupyterlab_version']),
+                    "k8s_version": "{}".format(odahu_conf['k8s_version']),
+                    "mlflow_toolchain_version": "{}".format(odahu_conf['mlflow_toolchain_version']),
+                    "node_pools": {
+                        "main": {
+                            "disk_size_gb": 64,
+                            "init_node_count": 3,
+                            "max_node_count": 5,
+                            "min_node_count": 1
+                        },
+                        "model_deployment": {
+                            "labels": {
+                                "mode": "odahu-flow-deployment"
+                            },
+                            "max_node_count": 3,
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "deployment"
+                                }
+                            ]
+                        },
+                        "packaging": {
+                            "disk_size_gb": 64,
+                            "disk_type": "pd-ssd",
+                            "labels": {
+                                "mode": "odahu-flow-packaging"
+                            },
+                            "machine_type": "n1-standard-4",
+                            "max_node_count": 3,
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "packaging"
+                                }
+                            ]
+                        },
+                        "training": {
+                            "disk_size_gb": 100,
+                            "labels": {
+                                "mode": "odahu-flow-training"
+                            },
+                            "machine_type": "n1-highcpu-8",
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "training"
+                                }
+                            ]
+                        },
+                        "training_gpu": {
+                            "disk_size_gb": 100,
+                            "gpu": [
+                                {
+                                    "count": 2,
+                                    "type": "nvidia-tesla-p100"
+                                }
+                            ],
+                            "labels": {
+                                "mode": "odahu-flow-training-gpu"
+                            },
+                            "machine_type": "n1-standard-8",
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "training-gpu"
+                                }
+                            ]
+                        }
+                    },
+                    "oauth_client_id": "{}".format(odahu_conf['oauth_client_id']),
+                    "oauth_client_secret": "{}".format(odahu_conf['oauth_client_secret']),
+                    "oauth_cookie_secret": "{}".format(odahu_conf['oauth_cookie_secret']),
+                    "oauth_local_jwks": "{}".format(odahu_conf['oauth_local_jwks']),
+                    "oauth_mesh_enabled": odahu_conf['oauth_mesh_enabled'],
+                    "oauth_oidc_audience": "legion",
+                    "oauth_oidc_host": "{}".format(odahu_conf['oauth_oidc_host']),
+                    "oauth_oidc_issuer_url": "{}".format(odahu_conf['oauth_oidc_issuer_url']),
+                    "oauth_oidc_jwks_url": "{}/protocol/openid-connect/certs".format(odahu_conf['oauth_oidc_issuer_url']),
+                    "oauth_oidc_port": 443,
+                    "oauth_oidc_scope": "openid profile email offline_access groups",
+                    "oauth_oidc_token_endpoint": "{}/protocol/openid-connect/token".format(odahu_conf['oauth_oidc_issuer_url']),
+                    "odahu_automation_version": "{}".format(odahu_conf['automation_version']),
+                    "odahu_infra_version": "{}".format(odahu_conf['infra_version']),
+                    "odahu_ui_version": "{}".format(odahu_conf['ui_version']),
+                    "odahuflow_connection_decrypt_token": "{}".format(odahu_conf['decrypt_token']),
+                    "odahuflow_connections": [
+                        {
+                            "id": "odahu-flow-examples",
+                            "spec": {
+                                "description": "Git repository with the Odahu-Flow examples",
+                                "keySecret": "{}".format(odahu_conf['keysecret']),
+                                "reference": "{}".format(odahu_conf['examples_version']),
+                                "type": "git",
+                                "uri": "git@github.com:odahu/odahu-examples.git",
+                                "webUILink": "https://github.com/odahu/odahu-examples"
+                            }
+                        }
+                    ],
+                    "odahuflow_version": "{}".format(odahu_conf['odahuflow_version']),
+                    "opa_policies": {},
+                    "packager_version": "{}".format(odahu_conf['packager_version']),
+                    "pods_cidr": "{}".format(odahu_conf['pods_cidr']),
+                    "service_accounts": {
+                        "airflow": {
+                            "client_id": "sa-airflow",
+                            "client_secret": "{}".format(odahu_conf['airflow_secret'])
+                        },
+                        "operator": {
+                            "client_id": "sa-operator",
+                            "client_secret": "{}".format(odahu_conf['operator_secret'])
+                        },
+                        "resource_uploader": {
+                            "client_id": "sa-resource-uploader",
+                            "client_secret": "{}".format(odahu_conf['resource-uploader_secret'])
+                        },
+                        "test": {
+                            "client_id": "sa-tester",
+                            "client_secret": "{}".format(odahu_conf['tester_secret'])
+                        },
+                        "test_data_scientist": {
+                            "client_id": "sa-tester-data-scientist",
+                            "client_secret": "{}".format(odahu_conf['tester-data-scientist_secret'])
+                        }
+                    },
+                    "service_cidr": "{}".format(odahu_conf['service_cidr']),
+                    "ssh_key": "{}".format(odahu_conf['ssh_key'].replace('\n', '')),
+                    "subnet_name": "{}".format(odahu_conf['private_subnet_name']),
+                    "tfstate_bucket": "{}-tfstate".format(odahu_conf['cluster_name']),
+                    "tls_crt": "{}".format(odahu_conf['tls_crt']),
+                    "tls_key": "{}".format(odahu_conf['tls_key']),
+                    "vpc_name": "{}".format(odahu_conf['vpc_name']),
+                    "vault": {
+                        "enabled": "true"
+                    }
+                    }
+            profile.write(json.dumps(prof))
+        local('cat /tmp/profile.json')
+        local('cp /tmp/profile.json /')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to configure parameter file.", str(err))
+        GCPActions().remove_bucket(odahu_conf['bucket_name'])
+        GCPActions().remove_static_address(odahu_conf['static_address_name'], odahu_conf['region'])
+        sys.exit(1)
+
+    try:
+        local('tf_runner create -o /tmp/result.json')
+        local("sed -i 's|name|description|g' /tmp/result.json")
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to deploy Odahu cluster.", str(err))
+        GCPActions().remove_bucket(odahu_conf['bucket_name'])
+        GCPActions().remove_static_address(odahu_conf['static_address_name'], odahu_conf['region'])
+        sys.exit(1)
+
+    # generating output information
+    print('[SUMMARY]')
+    logging.info('[SUMMARY]')
+    print('Cluster name: {}'.format(odahu_conf['cluster_name']))
+    with open('/tmp/result.json', 'r') as f:
+        output = json.load(f)
+        odahu_urls = json.dumps(output['odahu_urls']['value'], sort_keys=True, indent=4)
+    print('Odahu urls: {}'.format(odahu_urls))
+    res = dict()
+    res['odahu_urls'] = output['odahu_urls']['value']
+    res['oauth_cookie_secret'] = odahu_conf['oauth_cookie_secret']
+    res['odahuflow_connection_decrypt_token'] = odahu_conf['decrypt_token']
+    res['grafana_pass'] = odahu_conf['grafana_pass']
+    res['grafana_admin'] = odahu_conf['grafana_admin']
+    with open("/root/result.json", 'w') as result:
+        result.write(json.dumps(res))
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/general/scripts/gcp/odahu_prepare.py b/infrastructure-provisioning/src/general/scripts/gcp/odahu_prepare.py
new file mode 100644
index 0000000..1a6c3ab
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/gcp/odahu_prepare.py
@@ -0,0 +1,142 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+import requests
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['endpoint_name'] = (os.environ['endpoint_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['tag_name'] = '{}-tag'.format(odahu_conf['service_base_name'])
+    odahu_conf['endpoint_tag'] = (os.environ['endpoint_name']).lower().replace('_', '-')
+    odahu_conf['project_tag'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['region'] = os.environ['gcp_region']
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    odahu_conf['keycloak_auth_server_url'] = os.environ['keycloak_auth_server_url']
+    odahu_conf['keycloak_realm_name'] = os.environ['keycloak_realm_name']
+    odahu_conf['keycloak_client_name'] = os.environ['keycloak_client_name']
+    odahu_conf['keycloak_user'] = os.environ['keycloak_user']
+    odahu_conf['keycloak_user_password'] = os.environ['keycloak_user_password']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+
+
+    try:
+        logging.info('[CREATE STATE BUCKETS]')
+        print('[CREATE STATE BUCKETS]')
+
+        odahu_conf['bucket_tags'] = {
+            odahu_conf['tag_name']: odahu_conf['bucket_name'],
+            "endpoint_tag": odahu_conf['endpoint_tag'],
+            os.environ['conf_billing_tag_key']: os.environ['conf_billing_tag_value'],
+            "sbn": odahu_conf['service_base_name'],
+            "project_tag": odahu_conf['project_tag']}
+
+        params = "--bucket_name {} --tags '{}'".format(odahu_conf['bucket_name'], json.dumps(odahu_conf['bucket_tags']))
+
+        try:
+            local("~/scripts/{}.py {}".format('common_create_bucket', params))
+        except:
+            traceback.print_exc()
+            raise Exception
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        append_result("Unable to create bucket.", str(err))
+        sys.exit(1)
+
+    try:
+        logging.info('[CREATE NAT GATEWAY]')
+        print('[CREATE NAT GATEWAY]')
+        GCPActions().create_static_address(odahu_conf['static_address_name'], odahu_conf['region'])
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        append_result("Unable to reserve static ip.", str(err))
+        GCPActions().remove_bucket(odahu_conf['bucket_name'])
+        sys.exit(1)
+
+    try:
+        print('[CONFIGURE REDIRECT URI]')
+        logging.info('[CONFIGURE REDIRECT URI]')
+        keycloak_auth_server_url = '{}/realms/master/protocol/openid-connect/token'.format(
+            odahu_conf['keycloak_auth_server_url'])
+        keycloak_auth_data = {
+            "username": odahu_conf['keycloak_user'],
+            "password": odahu_conf['keycloak_user_password'],
+            "grant_type": "password",
+            "client_id": "admin-cli",
+        }
+        keycloak_client_create_url = '{0}/admin/realms/{1}/clients'.format(odahu_conf['keycloak_auth_server_url'],
+                                                                           odahu_conf['keycloak_realm_name'])
+        odahu_redirectUris = 'https://odahu.{0}.{1}/*,http://odahu.{0}.{1}/*'.format(odahu_conf['cluster_name'],
+                                                                                        odahu_conf['root_domain']).split(',')
+
+
+        try:
+            keycloak_token = requests.post(keycloak_auth_server_url, data=keycloak_auth_data, verify=False).json()
+            keycloak_get_Uris = requests.get(keycloak_client_create_url,
+                                            headers={"Authorization": "Bearer " + keycloak_token.get("access_token"),
+                                                     "Content-Type": "application/json"}, verify=False).json()
+            for dict in keycloak_get_Uris:
+                if dict["clientId"] == odahu_conf['keycloak_client_name']:
+                    ui_redirectUris = dict["redirectUris"]
+                    keycloak_client_id = dict["id"]
+            keycloak_redirectUris = odahu_redirectUris + ui_redirectUris
+            updated_client_data = {
+                "clientId": odahu_conf['keycloak_client_name'],
+                "id": keycloak_client_id,
+                "enabled": "true",
+                "redirectUris": keycloak_redirectUris,
+                "publicClient": "false",
+                "protocol": "openid-connect",
+            }
+            client_url = "{}/{}".format(keycloak_client_create_url, keycloak_client_id)
+            keycloak_update_Uris = requests.put(client_url, json=updated_client_data,
+                                            headers={"Authorization": "Bearer " + keycloak_token.get("access_token"),
+                                                     "Content-Type": "application/json"}, verify=False)
+        except Exception as err:
+            append_result("Failed to configure keycloak.")
+            sys.exit(1)
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        append_result("Failed to configure keycloak.", str(err))
+        GCPActions().remove_bucket(odahu_conf['bucket_name'])
+        GCPActions().remove_static_address(odahu_conf['static_address_name'], odahu_conf['region'])
+        sys.exit(1)
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/general/scripts/gcp/odahu_resume.py b/infrastructure-provisioning/src/general/scripts/gcp/odahu_resume.py
new file mode 100644
index 0000000..994828b
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/gcp/odahu_resume.py
@@ -0,0 +1,300 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+import string
+import random
+import base64
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['allowed_cidr'] = os.environ['odahu_allowed_cidr'].split(',')
+    odahu_conf['project_id'] = (os.environ['gcp_project_id'])
+    odahu_conf['region'] = (os.environ['gcp_region'])
+    odahu_conf['zone'] = (os.environ['gcp_zone'])
+    odahu_conf['edge_user_name'] = os.environ['edge_user_name']
+    odahu_conf['node_locations'] = GCPMeta().get_available_zones()
+    odahu_conf['dns_zone_name'] = os.environ['odahu_dns_zone_name']
+    odahu_conf['docker_repo'] = os.environ['odahu_docker_repo']
+    odahu_conf['cidr'] = os.environ['odahu_cidr']
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    try:
+        if os.environ['gcp_vpc_name'] == '':
+            raise KeyError
+        else:
+            odahu_conf['vpc_name'] = os.environ['gcp_vpc_name']
+    except KeyError:
+        odahu_conf['vpc_name'] = odahu_conf['service_base_name'] + '-ssn-vpc'
+    odahu_conf['vpc_cidr'] = os.environ['conf_vpc_cidr']
+    odahu_conf['private_subnet_name'] = '{0}-{1}-subnet'.format(odahu_conf['service_base_name'],
+                                                                odahu_conf['project_name'])
+    odahu_conf['grafana_admin'] = os.environ['grafana_admin']
+    odahu_conf['grafana_pass'] = os.environ['grafana_pass']
+    odahu_conf['docker_password'] = base64.b64decode(os.environ['odahu_docker_password'] + "==")
+    odahu_conf['initial_node_count'] = os.environ['odahu_initial_node_count']
+    odahu_conf['istio_helm_repo'] = os.environ['odahu_istio_helm_repo']
+    odahu_conf['helm_repo'] = os.environ['odahu_helm_repo']
+    odahu_conf['k8s_version'] = os.environ['odahu_k8s_version']
+    odahu_conf['oauth_oidc_issuer_url'] = "{}/realms/{}".format(os.environ['keycloak_auth_server_url'],
+                                                                os.environ['keycloak_realm_name'])
+    odahu_conf['oauth_oidc_host'] = os.environ['keycloak_auth_server_url'].replace('https://', '').replace('/auth', '')
+    odahu_conf['oauth_client_id'] = os.environ['keycloak_client_name']
+    odahu_conf['oauth_client_secret'] = os.environ['keycloak_client_secret']
+    odahu_conf['oauth_cookie_secret'] = os.environ['oauth_cookie_secret']
+    odahu_conf['oauth_local_jwks'] = os.environ['odahu_oauth_local_jwks']
+    odahu_conf['infra_version'] = os.environ['odahu_infra_version']
+    odahu_conf['odahuflow_version'] = os.environ['odahu_odahuflow_version']
+    odahu_conf['mlflow_toolchain_version'] = os.environ['odahu_mlflow_toolchain_version']
+    odahu_conf['jupyterlab_version'] = os.environ['odahu_jupyterlab_version']
+    odahu_conf['packager_version'] = os.environ['odahu_packager_version']
+    odahu_conf['node_version'] = os.environ['odahu_node_version']
+    odahu_conf['pods_cidr'] = os.environ['odahu_pods_cidr']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+    odahu_conf['service_cidr'] = os.environ['odahu_service_cidr']
+    odahu_conf['tls_crt'] = base64.b64decode(os.environ['odahu_tls_crt'] + "==")
+    odahu_conf['tls_key'] = base64.b64decode(os.environ['odahu_tls_key'] + "==")
+    odahu_conf['ssh_key'] = os.environ['ssh_key']
+    odahu_conf['dns_project_id'] = os.environ['odahu_dns_project_id']
+    odahu_conf['decrypt_token'] = os.environ['odahuflow_connection_decrypt_token']
+    odahu_conf['infra_vpc_peering'] = os.environ['odahu_infra_vpc_peering']
+    odahu_conf['automation_version'] = os.environ['odahu_automation_version']
+    odahu_conf['ui_version'] = os.environ['odahu_ui_version']
+    odahu_conf['examples_version'] = os.environ['odahu_examples_version']
+    odahu_conf['jupyterhub_enabled'] = os.environ['odahu_jupyterhub_enabled']
+    odahu_conf['oauth_mesh_enabled'] = os.environ['odahu_oauth_mesh_enabled']
+    odahu_conf['keysecret'] = os.environ['odahu_keysecret']
+    odahu_conf['airflow_secret'] = os.environ['odahu_airflow_secret']
+    odahu_conf['operator_secret'] = os.environ['odahu_operator_secret']
+    odahu_conf['resource-uploader_secret'] = os.environ['odahu_resource_uploader_secret']
+    odahu_conf['tester_secret'] = os.environ['odahu_tester_secret']
+    odahu_conf['tester-data-scientist_secret'] = os.environ['odahu_tester_data_scientist_secret']
+
+    print('Preparing parameters file')
+    try:
+        local("cp /root/templates/profile.json /tmp/")
+        with open("/tmp/profile.json", 'w') as profile:
+            prof = {
+                "allowed_ips": odahu_conf['allowed_cidr'],
+                "authorization_enabled": "true",
+                "authz_dry_run": "false",
+                "cloud": {
+                    "gcp": {
+                        "node_locations": odahu_conf['node_locations'],
+                        "project_id": "{}".format(odahu_conf['project_id']),
+                        "region": "{}".format(odahu_conf['region']),
+                        "zone": "{}".format(odahu_conf['zone']),
+                    },
+                    "type": "gcp"
+                },
+                "cluster_name": "{}".format(odahu_conf['cluster_name']),
+                "cluster_type": "gcp/gke",
+                "data_bucket": "{}-data-bucket".format(odahu_conf['cluster_name']),
+
+                "dns": {
+                    "domain": "odahu.{}.{}".format(odahu_conf['cluster_name'], odahu_conf['root_domain']),
+                    "gcp_project_id": "{}".format(odahu_conf['project_id']),
+                    "provider": "gcp",
+                    "zone_name": "{}".format(odahu_conf['dns_zone_name']),
+                },
+                "docker_password": "{}".format(odahu_conf['docker_password']),
+                "docker_repo": "{}".format(odahu_conf['docker_repo']),
+                "docker_username": "_json_key",
+                "gcp_cidr": "{}".format(odahu_conf['cidr']),
+                "examples_version": "{}".format(odahu_conf['examples_version']),
+                "grafana_pass": "{}".format(odahu_conf['grafana_pass']),
+                "helm_repo": "{}".format(odahu_conf['helm_repo']),
+                "jupyterhub_enabled": odahu_conf['jupyterhub_enabled'],
+                "jupyterlab_version": "{}".format(odahu_conf['jupyterlab_version']),
+                "k8s_version": "{}".format(odahu_conf['k8s_version']),
+                "mlflow_toolchain_version": "{}".format(odahu_conf['mlflow_toolchain_version']),
+                "node_pools": {
+                    "main": {
+                        "disk_size_gb": 64,
+                        "init_node_count": 8,
+                        "max_node_count": 5,
+                        "min_node_count": 1
+                    },
+                    "model_deployment": {
+                        "labels": {
+                            "mode": "odahu-flow-deployment"
+                        },
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "deployment"
+                            }
+                        ]
+                    },
+                    "packaging": {
+                        "disk_size_gb": 64,
+                        "disk_type": "pd-ssd",
+                        "labels": {
+                            "mode": "odahu-flow-packaging"
+                        },
+                        "machine_type": "n1-standard-4",
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "packaging"
+                            }
+                        ]
+                    },
+                    "training": {
+                        "disk_size_gb": 100,
+                        "labels": {
+                            "mode": "odahu-flow-training"
+                        },
+                        "machine_type": "n1-highcpu-8",
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "training"
+                            }
+                        ]
+                    },
+                    "training_gpu": {
+                        "disk_size_gb": 100,
+                        "gpu": [
+                            {
+                                "count": 2,
+                                "type": "nvidia-tesla-p100"
+                            }
+                        ],
+                        "labels": {
+                            "mode": "odahu-flow-training-gpu"
+                        },
+                        "machine_type": "n1-standard-8",
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "training-gpu"
+                            }
+                        ]
+                    }
+                },
+                "oauth_client_id": "{}".format(odahu_conf['oauth_client_id']),
+                "oauth_client_secret": "{}".format(odahu_conf['oauth_client_secret']),
+                "oauth_cookie_secret": "{}".format(odahu_conf['oauth_cookie_secret']),
+                "oauth_local_jwks": "{}".format(odahu_conf['oauth_local_jwks']),
+                "oauth_mesh_enabled": odahu_conf['oauth_mesh_enabled'],
+                "oauth_oidc_audience": "legion",
+                "oauth_oidc_host": "{}".format(odahu_conf['oauth_oidc_host']),
+                "oauth_oidc_issuer_url": "{}".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_jwks_url": "{}/protocol/openid-connect/certs".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_port": 443,
+                "oauth_oidc_scope": "openid profile email offline_access groups",
+                "oauth_oidc_token_endpoint": "{}/protocol/openid-connect/token".format(
+                    odahu_conf['oauth_oidc_issuer_url']),
+                "odahu_automation_version": "{}".format(odahu_conf['automation_version']),
+                "odahu_infra_version": "{}".format(odahu_conf['infra_version']),
+                "odahu_ui_version": "{}".format(odahu_conf['ui_version']),
+                "odahuflow_connection_decrypt_token": "{}".format(odahu_conf['decrypt_token']),
+                "odahuflow_connections": [
+                    {
+                        "id": "odahu-flow-examples",
+                        "spec": {
+                            "description": "Git repository with the Odahu-Flow examples",
+                            "keySecret": "{}".format(odahu_conf['keysecret']),
+                            "reference": "{}".format(odahu_conf['examples_version']),
+                            "type": "git",
+                            "uri": "git@github.com:odahu/odahu-examples.git",
+                            "webUILink": "https://github.com/odahu/odahu-examples"
+                        }
+                    }
+                ],
+                "odahuflow_version": "{}".format(odahu_conf['odahuflow_version']),
+                "opa_policies": {},
+                "packager_version": "{}".format(odahu_conf['packager_version']),
+                "pods_cidr": "{}".format(odahu_conf['pods_cidr']),
+                "service_accounts": {
+                    "airflow": {
+                        "client_id": "sa-airflow",
+                        "client_secret": "{}".format(odahu_conf['airflow_secret'])
+                    },
+                    "operator": {
+                        "client_id": "sa-operator",
+                        "client_secret": "{}".format(odahu_conf['operator_secret'])
+                    },
+                    "resource_uploader": {
+                        "client_id": "sa-resource-uploader",
+                        "client_secret": "{}".format(odahu_conf['resource-uploader_secret'])
+                    },
+                    "test": {
+                        "client_id": "sa-tester",
+                        "client_secret": "{}".format(odahu_conf['tester_secret'])
+                    },
+                    "test_data_scientist": {
+                        "client_id": "sa-tester-data-scientist",
+                        "client_secret": "{}".format(odahu_conf['tester-data-scientist_secret'])
+                    }
+                },
+                "service_cidr": "{}".format(odahu_conf['service_cidr']),
+                "ssh_key": "{}".format(odahu_conf['ssh_key'].replace('\n', '')),
+                "subnet_name": "{}".format(odahu_conf['private_subnet_name']),
+                "tfstate_bucket": "{}-tfstate".format(odahu_conf['cluster_name']),
+                "tls_crt": "{}".format(odahu_conf['tls_crt']),
+                "tls_key": "{}".format(odahu_conf['tls_key']),
+                "vpc_name": "{}".format(odahu_conf['vpc_name']),
+                "vault": {
+                    "enabled": "true"
+                }
+            }
+            profile.write(json.dumps(prof))
+        local('cat /tmp/profile.json')
+        local('cp /tmp/profile.json /')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to configure parameter file.", str(err))
+        sys.exit(1)
+
+    try:
+        local('tf_runner resume -v')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to deploy Odahu cluster.", str(err))
+        sys.exit(1)
diff --git a/infrastructure-provisioning/src/general/scripts/gcp/odahu_suspend.py b/infrastructure-provisioning/src/general/scripts/gcp/odahu_suspend.py
new file mode 100644
index 0000000..54aff9e
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/gcp/odahu_suspend.py
@@ -0,0 +1,298 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+import base64
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['allowed_cidr'] = os.environ['odahu_allowed_cidr'].split(',')
+    odahu_conf['project_id'] = (os.environ['gcp_project_id'])
+    odahu_conf['region'] = (os.environ['gcp_region'])
+    odahu_conf['zone'] = (os.environ['gcp_zone'])
+    odahu_conf['edge_user_name'] = os.environ['edge_user_name']
+    odahu_conf['node_locations'] = GCPMeta().get_available_zones()
+    odahu_conf['dns_zone_name'] = os.environ['odahu_dns_zone_name']
+    odahu_conf['docker_repo'] = os.environ['odahu_docker_repo']
+    odahu_conf['cidr'] = os.environ['odahu_cidr']
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    try:
+        if os.environ['gcp_vpc_name'] == '':
+            raise KeyError
+        else:
+            odahu_conf['vpc_name'] = os.environ['gcp_vpc_name']
+    except KeyError:
+        odahu_conf['vpc_name'] = odahu_conf['service_base_name'] + '-ssn-vpc'
+    odahu_conf['vpc_cidr'] = os.environ['conf_vpc_cidr']
+    odahu_conf['private_subnet_name'] = '{0}-{1}-subnet'.format(odahu_conf['service_base_name'],
+                                                                odahu_conf['project_name'])
+    odahu_conf['grafana_admin'] = os.environ['grafana_admin']
+    odahu_conf['grafana_pass'] = os.environ['grafana_pass']
+    odahu_conf['docker_password'] = base64.b64decode(os.environ['odahu_docker_password'] + "==")
+    odahu_conf['initial_node_count'] = os.environ['odahu_initial_node_count']
+    odahu_conf['istio_helm_repo'] = os.environ['odahu_istio_helm_repo']
+    odahu_conf['helm_repo'] = os.environ['odahu_helm_repo']
+    odahu_conf['k8s_version'] = os.environ['odahu_k8s_version']
+    odahu_conf['oauth_oidc_issuer_url'] = "{}/realms/{}".format(os.environ['keycloak_auth_server_url'],
+                                                                os.environ['keycloak_realm_name'])
+    odahu_conf['oauth_oidc_host'] = os.environ['keycloak_auth_server_url'].replace('https://', '').replace('/auth', '')
+    odahu_conf['oauth_client_id'] = os.environ['keycloak_client_name']
+    odahu_conf['oauth_client_secret'] = os.environ['keycloak_client_secret']
+    odahu_conf['oauth_cookie_secret'] = os.environ['oauth_cookie_secret']
+    odahu_conf['oauth_local_jwks'] = os.environ['odahu_oauth_local_jwks']
+    odahu_conf['infra_version'] = os.environ['odahu_infra_version']
+    odahu_conf['odahuflow_version'] = os.environ['odahu_odahuflow_version']
+    odahu_conf['mlflow_toolchain_version'] = os.environ['odahu_mlflow_toolchain_version']
+    odahu_conf['jupyterlab_version'] = os.environ['odahu_jupyterlab_version']
+    odahu_conf['packager_version'] = os.environ['odahu_packager_version']
+    odahu_conf['node_version'] = os.environ['odahu_node_version']
+    odahu_conf['pods_cidr'] = os.environ['odahu_pods_cidr']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+    odahu_conf['service_cidr'] = os.environ['odahu_service_cidr']
+    odahu_conf['tls_crt'] = base64.b64decode(os.environ['odahu_tls_crt'] + "==")
+    odahu_conf['tls_key'] = base64.b64decode(os.environ['odahu_tls_key'] + "==")
+    odahu_conf['ssh_key'] = os.environ['ssh_key']
+    odahu_conf['dns_project_id'] = os.environ['odahu_dns_project_id']
+    odahu_conf['decrypt_token'] = os.environ['odahuflow_connection_decrypt_token']
+    odahu_conf['infra_vpc_peering'] = os.environ['odahu_infra_vpc_peering']
+    odahu_conf['automation_version'] = os.environ['odahu_automation_version']
+    odahu_conf['ui_version'] = os.environ['odahu_ui_version']
+    odahu_conf['examples_version'] = os.environ['odahu_examples_version']
+    odahu_conf['jupyterhub_enabled'] = os.environ['odahu_jupyterhub_enabled']
+    odahu_conf['oauth_mesh_enabled'] = os.environ['odahu_oauth_mesh_enabled']
+    odahu_conf['keysecret'] = os.environ['odahu_keysecret']
+    odahu_conf['airflow_secret'] = os.environ['odahu_airflow_secret']
+    odahu_conf['operator_secret'] = os.environ['odahu_operator_secret']
+    odahu_conf['resource-uploader_secret'] = os.environ['odahu_resource_uploader_secret']
+    odahu_conf['tester_secret'] = os.environ['odahu_tester_secret']
+    odahu_conf['tester-data-scientist_secret'] = os.environ['odahu_tester_data_scientist_secret']
+
+    print('Preparing parameters file')
+    try:
+        local("cp /root/templates/profile.json /tmp/")
+        with open("/tmp/profile.json", 'w') as profile:
+            prof = {
+                    "allowed_ips": odahu_conf['allowed_cidr'],
+                    "authorization_enabled": "true",
+                    "authz_dry_run": "false",
+                    "cloud": {
+                        "gcp": {
+                            "node_locations": odahu_conf['node_locations'],
+                            "project_id": "{}".format(odahu_conf['project_id']),
+                            "region": "{}".format(odahu_conf['region']),
+                            "zone": "{}".format(odahu_conf['zone']),
+                        },
+                        "type": "gcp"
+                    },
+                    "cluster_name": "{}".format(odahu_conf['cluster_name']),
+                    "cluster_type": "gcp/gke",
+                    "data_bucket": "{}-data-bucket".format(odahu_conf['cluster_name']),
+
+                    "dns": {
+                        "domain": "odahu.{}.{}".format(odahu_conf['cluster_name'], odahu_conf['root_domain']),
+                        "gcp_project_id": "{}".format(odahu_conf['project_id']),
+                        "provider": "gcp",
+                        "zone_name": "{}".format(odahu_conf['dns_zone_name']),
+                    },
+                    "docker_password": "{}".format(odahu_conf['docker_password']),
+                    "docker_repo": "{}".format(odahu_conf['docker_repo']),
+                    "docker_username": "_json_key",
+                    "gcp_cidr": "{}".format(odahu_conf['cidr']),
+                    "examples_version": "{}".format(odahu_conf['examples_version']),
+                    "grafana_pass": "{}".format(odahu_conf['grafana_pass']),
+                    "helm_repo": "{}".format(odahu_conf['helm_repo']),
+                    "jupyterhub_enabled": odahu_conf['jupyterhub_enabled'],
+                    "jupyterlab_version": "{}".format(odahu_conf['jupyterlab_version']),
+                    "k8s_version": "{}".format(odahu_conf['k8s_version']),
+                    "mlflow_toolchain_version": "{}".format(odahu_conf['mlflow_toolchain_version']),
+                    "node_pools": {
+                        "main": {
+                            "disk_size_gb": 64,
+                            "init_node_count": 3,
+                            "max_node_count": 5,
+                            "min_node_count": 1
+                        },
+                        "model_deployment": {
+                            "labels": {
+                                "mode": "odahu-flow-deployment"
+                            },
+                            "max_node_count": 3,
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "deployment"
+                                }
+                            ]
+                        },
+                        "packaging": {
+                            "disk_size_gb": 64,
+                            "disk_type": "pd-ssd",
+                            "labels": {
+                                "mode": "odahu-flow-packaging"
+                            },
+                            "machine_type": "n1-standard-4",
+                            "max_node_count": 3,
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "packaging"
+                                }
+                            ]
+                        },
+                        "training": {
+                            "disk_size_gb": 100,
+                            "labels": {
+                                "mode": "odahu-flow-training"
+                            },
+                            "machine_type": "n1-highcpu-8",
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "training"
+                                }
+                            ]
+                        },
+                        "training_gpu": {
+                            "disk_size_gb": 100,
+                            "gpu": [
+                                {
+                                    "count": 2,
+                                    "type": "nvidia-tesla-p100"
+                                }
+                            ],
+                            "labels": {
+                                "mode": "odahu-flow-training-gpu"
+                            },
+                            "machine_type": "n1-standard-8",
+                            "taints": [
+                                {
+                                    "effect": "NO_SCHEDULE",
+                                    "key": "dedicated",
+                                    "value": "training-gpu"
+                                }
+                            ]
+                        }
+                    },
+                    "oauth_client_id": "{}".format(odahu_conf['oauth_client_id']),
+                    "oauth_client_secret": "{}".format(odahu_conf['oauth_client_secret']),
+                    "oauth_cookie_secret": "{}".format(odahu_conf['oauth_cookie_secret']),
+                    "oauth_local_jwks": "{}".format(odahu_conf['oauth_local_jwks']),
+                    "oauth_mesh_enabled": odahu_conf['oauth_mesh_enabled'],
+                    "oauth_oidc_audience": "legion",
+                    "oauth_oidc_host": "{}".format(odahu_conf['oauth_oidc_host']),
+                    "oauth_oidc_issuer_url": "{}".format(odahu_conf['oauth_oidc_issuer_url']),
+                    "oauth_oidc_jwks_url": "{}/protocol/openid-connect/certs".format(odahu_conf['oauth_oidc_issuer_url']),
+                    "oauth_oidc_port": 443,
+                    "oauth_oidc_scope": "openid profile email offline_access groups",
+                    "oauth_oidc_token_endpoint": "{}/protocol/openid-connect/token".format(
+                        odahu_conf['oauth_oidc_issuer_url']),
+                    "odahu_automation_version": "{}".format(odahu_conf['automation_version']),
+                    "odahu_infra_version": "{}".format(odahu_conf['infra_version']),
+                    "odahu_ui_version": "{}".format(odahu_conf['ui_version']),
+                    "odahuflow_connection_decrypt_token": "{}".format(odahu_conf['decrypt_token']),
+                    "odahuflow_connections": [
+                        {
+                            "id": "odahu-flow-examples",
+                            "spec": {
+                                "description": "Git repository with the Odahu-Flow examples",
+                                "keySecret": "{}".format(odahu_conf['keysecret']),
+                                "reference": "{}".format(odahu_conf['examples_version']),
+                                "type": "git",
+                                "uri": "git@github.com:odahu/odahu-examples.git",
+                                "webUILink": "https://github.com/odahu/odahu-examples"
+                            }
+                        }
+                    ],
+                    "odahuflow_version": "{}".format(odahu_conf['odahuflow_version']),
+                    "opa_policies": {},
+                    "packager_version": "{}".format(odahu_conf['packager_version']),
+                    "pods_cidr": "{}".format(odahu_conf['pods_cidr']),
+                    "service_accounts": {
+                        "airflow": {
+                            "client_id": "sa-airflow",
+                            "client_secret": "{}".format(odahu_conf['airflow_secret'])
+                        },
+                        "operator": {
+                            "client_id": "sa-operator",
+                            "client_secret": "{}".format(odahu_conf['operator_secret'])
+                        },
+                        "resource_uploader": {
+                            "client_id": "sa-resource-uploader",
+                            "client_secret": "{}".format(odahu_conf['resource-uploader_secret'])
+                        },
+                        "test": {
+                            "client_id": "sa-tester",
+                            "client_secret": "{}".format(odahu_conf['tester_secret'])
+                        },
+                        "test_data_scientist": {
+                            "client_id": "sa-tester-data-scientist",
+                            "client_secret": "{}".format(odahu_conf['tester-data-scientist_secret'])
+                        }
+                    },
+                    "service_cidr": "{}".format(odahu_conf['service_cidr']),
+                    "ssh_key": "{}".format(odahu_conf['ssh_key'].replace('\n', '')),
+                    "subnet_name": "{}".format(odahu_conf['private_subnet_name']),
+                    "tfstate_bucket": "{}-tfstate".format(odahu_conf['cluster_name']),
+                    "tls_crt": "{}".format(odahu_conf['tls_crt']),
+                    "tls_key": "{}".format(odahu_conf['tls_key']),
+                    "vpc_name": "{}".format(odahu_conf['vpc_name']),
+                    "vault": {
+                        "enabled": "true"
+                    }
+            }
+            profile.write(json.dumps(prof))
+        local('cat /tmp/profile.json')
+        local('cp /tmp/profile.json /')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to configure parameter file.", str(err))
+        sys.exit(1)
+
+    try:
+        local('tf_runner suspend -v')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to suspend Odahu cluster.", str(err))
+        sys.exit(1)
diff --git a/infrastructure-provisioning/src/general/scripts/gcp/odahu_terminate.py b/infrastructure-provisioning/src/general/scripts/gcp/odahu_terminate.py
new file mode 100644
index 0000000..c964ce3
--- /dev/null
+++ b/infrastructure-provisioning/src/general/scripts/gcp/odahu_terminate.py
@@ -0,0 +1,318 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+import base64
+
+
+if __name__ == "__main__":
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['project_name'],
+                                               os.environ['request_id'])
+    local_log_filepath = "/logs/project/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    print('Generating infrastructure names and tags')
+    odahu_conf = dict()
+    odahu_conf['allowed_cidr'] = os.environ['odahu_allowed_cidr'].split(',')
+    odahu_conf['project_id'] = (os.environ['gcp_project_id'])
+    odahu_conf['region'] = (os.environ['gcp_region'])
+    odahu_conf['zone'] = (os.environ['gcp_zone'])
+    odahu_conf['edge_user_name'] = os.environ['edge_user_name']
+    odahu_conf['node_locations'] = GCPMeta().get_available_zones()
+    odahu_conf['dns_zone_name'] = os.environ['odahu_dns_zone_name']
+    odahu_conf['docker_repo'] = os.environ['odahu_docker_repo']
+    odahu_conf['cidr'] = os.environ['odahu_cidr']
+    odahu_conf['service_base_name'] = (os.environ['conf_service_base_name']).lower().replace('_', '-')
+    odahu_conf['project_name'] = (os.environ['project_name']).lower().replace('_', '-')
+    odahu_conf['cluster_name'] = "{}-{}".format((os.environ['conf_service_base_name']).lower().replace('_', '-'),
+                                                (os.environ['odahu_cluster_name']).lower().replace('_', '-'))
+    odahu_conf['bucket_name'] = "{}-tfstate".format(odahu_conf['cluster_name'])
+    odahu_conf['static_address_name'] = "{}-nat-gw".format(odahu_conf['cluster_name'])
+    try:
+        if os.environ['gcp_vpc_name'] == '':
+            raise KeyError
+        else:
+            odahu_conf['vpc_name'] = os.environ['gcp_vpc_name']
+    except KeyError:
+        odahu_conf['vpc_name'] = odahu_conf['service_base_name'] + '-ssn-vpc'
+    odahu_conf['vpc_cidr'] = os.environ['conf_vpc_cidr']
+    odahu_conf['private_subnet_name'] = '{0}-{1}-subnet'.format(odahu_conf['service_base_name'],
+                                                                odahu_conf['project_name'])
+    odahu_conf['grafana_admin'] = os.environ['grafana_admin']
+    odahu_conf['grafana_pass'] = os.environ['grafana_pass']
+    odahu_conf['docker_password'] = base64.b64decode(os.environ['odahu_docker_password'] + "==")
+    odahu_conf['initial_node_count'] = os.environ['odahu_initial_node_count']
+    odahu_conf['istio_helm_repo'] = os.environ['odahu_istio_helm_repo']
+    odahu_conf['helm_repo'] = os.environ['odahu_helm_repo']
+    odahu_conf['k8s_version'] = os.environ['odahu_k8s_version']
+    odahu_conf['oauth_oidc_issuer_url'] = "{}/realms/{}".format(os.environ['keycloak_auth_server_url'],
+                                                                os.environ['keycloak_realm_name'])
+    odahu_conf['oauth_oidc_host'] = os.environ['keycloak_auth_server_url'].replace('https://', '').replace('/auth', '')
+    odahu_conf['oauth_client_id'] = os.environ['keycloak_client_name']
+    odahu_conf['oauth_client_secret'] = os.environ['keycloak_client_secret']
+    odahu_conf['oauth_cookie_secret'] = os.environ['oauth_cookie_secret']
+    odahu_conf['oauth_local_jwks'] = os.environ['odahu_oauth_local_jwks']
+    odahu_conf['infra_version'] = os.environ['odahu_infra_version']
+    odahu_conf['odahuflow_version'] = os.environ['odahu_odahuflow_version']
+    odahu_conf['mlflow_toolchain_version'] = os.environ['odahu_mlflow_toolchain_version']
+    odahu_conf['jupyterlab_version'] = os.environ['odahu_jupyterlab_version']
+    odahu_conf['packager_version'] = os.environ['odahu_packager_version']
+    odahu_conf['node_version'] = os.environ['odahu_node_version']
+    odahu_conf['pods_cidr'] = os.environ['odahu_pods_cidr']
+    odahu_conf['root_domain'] = os.environ['odahu_root_domain']
+    odahu_conf['service_cidr'] = os.environ['odahu_service_cidr']
+    odahu_conf['tls_crt'] = base64.b64decode(os.environ['odahu_tls_crt'] + "==")
+    odahu_conf['tls_key'] = base64.b64decode(os.environ['odahu_tls_key'] + "==")
+    odahu_conf['ssh_key'] = os.environ['ssh_key']
+    odahu_conf['dns_project_id'] = os.environ['odahu_dns_project_id']
+    odahu_conf['decrypt_token'] = os.environ['odahuflow_connection_decrypt_token']
+    odahu_conf['infra_vpc_peering'] = os.environ['odahu_infra_vpc_peering']
+    odahu_conf['automation_version'] = os.environ['odahu_automation_version']
+    odahu_conf['ui_version'] = os.environ['odahu_ui_version']
+    odahu_conf['examples_version'] = os.environ['odahu_examples_version']
+    odahu_conf['jupyterhub_enabled'] = os.environ['odahu_jupyterhub_enabled']
+    odahu_conf['oauth_mesh_enabled'] = os.environ['odahu_oauth_mesh_enabled']
+    odahu_conf['keysecret'] = os.environ['odahu_keysecret']
+    odahu_conf['airflow_secret'] = os.environ['odahu_airflow_secret']
+    odahu_conf['operator_secret'] = os.environ['odahu_operator_secret']
+    odahu_conf['resource-uploader_secret'] = os.environ['odahu_resource_uploader_secret']
+    odahu_conf['tester_secret'] = os.environ['odahu_tester_secret']
+    odahu_conf['tester-data-scientist_secret'] = os.environ['odahu_tester_data_scientist_secret']
+
+    print('Preparing parameters file')
+    try:
+        local("cp /root/templates/profile.json /tmp/")
+        with open("/tmp/profile.json", 'w') as profile:
+            prof = {
+                "allowed_ips": odahu_conf['allowed_cidr'],
+                "authorization_enabled": "true",
+                "authz_dry_run": "false",
+                "cloud": {
+                    "gcp": {
+                        "node_locations": odahu_conf['node_locations'],
+                        "project_id": "{}".format(odahu_conf['project_id']),
+                        "region": "{}".format(odahu_conf['region']),
+                        "zone": "{}".format(odahu_conf['zone']),
+                    },
+                    "type": "gcp"
+                },
+                "cluster_name": "{}".format(odahu_conf['cluster_name']),
+                "cluster_type": "gcp/gke",
+                "data_bucket": "{}-data-bucket".format(odahu_conf['cluster_name']),
+
+                "dns": {
+                    "domain": "odahu.{}.{}".format(odahu_conf['cluster_name'], odahu_conf['root_domain']),
+                    "gcp_project_id": "{}".format(odahu_conf['project_id']),
+                    "provider": "gcp",
+                    "zone_name": "{}".format(odahu_conf['dns_zone_name']),
+                },
+                "docker_password": "{}".format(odahu_conf['docker_password']),
+                "docker_repo": "{}".format(odahu_conf['docker_repo']),
+                "docker_username": "_json_key",
+                "gcp_cidr": "{}".format(odahu_conf['cidr']),
+                "examples_version": "{}".format(odahu_conf['examples_version']),
+                "grafana_pass": "{}".format(odahu_conf['grafana_pass']),
+                "helm_repo": "{}".format(odahu_conf['helm_repo']),
+                "jupyterhub_enabled": odahu_conf['jupyterhub_enabled'],
+                "jupyterlab_version": "{}".format(odahu_conf['jupyterlab_version']),
+                "k8s_version": "{}".format(odahu_conf['k8s_version']),
+                "mlflow_toolchain_version": "{}".format(odahu_conf['mlflow_toolchain_version']),
+                "node_pools": {
+                    "main": {
+                        "disk_size_gb": 64,
+                        "init_node_count": 3,
+                        "max_node_count": 5,
+                        "min_node_count": 1
+                    },
+                    "model_deployment": {
+                        "labels": {
+                            "mode": "odahu-flow-deployment"
+                        },
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "deployment"
+                            }
+                        ]
+                    },
+                    "packaging": {
+                        "disk_size_gb": 64,
+                        "disk_type": "pd-ssd",
+                        "labels": {
+                            "mode": "odahu-flow-packaging"
+                        },
+                        "machine_type": "n1-standard-4",
+                        "max_node_count": 3,
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "packaging"
+                            }
+                        ]
+                    },
+                    "training": {
+                        "disk_size_gb": 100,
+                        "labels": {
+                            "mode": "odahu-flow-training"
+                        },
+                        "machine_type": "n1-highcpu-8",
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "training"
+                            }
+                        ]
+                    },
+                    "training_gpu": {
+                        "disk_size_gb": 100,
+                        "gpu": [
+                            {
+                                "count": 2,
+                                "type": "nvidia-tesla-p100"
+                            }
+                        ],
+                        "labels": {
+                            "mode": "odahu-flow-training-gpu"
+                        },
+                        "machine_type": "n1-standard-8",
+                        "taints": [
+                            {
+                                "effect": "NO_SCHEDULE",
+                                "key": "dedicated",
+                                "value": "training-gpu"
+                            }
+                        ]
+                    }
+                },
+                "oauth_client_id": "{}".format(odahu_conf['oauth_client_id']),
+                "oauth_client_secret": "{}".format(odahu_conf['oauth_client_secret']),
+                "oauth_cookie_secret": "{}".format(odahu_conf['oauth_cookie_secret']),
+                "oauth_local_jwks": "{}".format(odahu_conf['oauth_local_jwks']),
+                "oauth_mesh_enabled": odahu_conf['oauth_mesh_enabled'],
+                "oauth_oidc_audience": "legion",
+                "oauth_oidc_host": "{}".format(odahu_conf['oauth_oidc_host']),
+                "oauth_oidc_issuer_url": "{}".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_jwks_url": "{}/protocol/openid-connect/certs".format(odahu_conf['oauth_oidc_issuer_url']),
+                "oauth_oidc_port": 443,
+                "oauth_oidc_scope": "openid profile email offline_access groups",
+                "oauth_oidc_token_endpoint": "{}/protocol/openid-connect/token".format(
+                    odahu_conf['oauth_oidc_issuer_url']),
+                "odahu_automation_version": "{}".format(odahu_conf['automation_version']),
+                "odahu_infra_version": "{}".format(odahu_conf['infra_version']),
+                "odahu_ui_version": "{}".format(odahu_conf['ui_version']),
+                "odahuflow_connection_decrypt_token": "{}".format(odahu_conf['decrypt_token']),
+                "odahuflow_connections": [
+                    {
+                        "id": "odahu-flow-examples",
+                        "spec": {
+                            "description": "Git repository with the Odahu-Flow examples",
+                            "keySecret": "{}".format(odahu_conf['keysecret']),
+                            "reference": "{}".format(odahu_conf['examples_version']),
+                            "type": "git",
+                            "uri": "git@github.com:odahu/odahu-examples.git",
+                            "webUILink": "https://github.com/odahu/odahu-examples"
+                        }
+                    }
+                ],
+                "odahuflow_version": "{}".format(odahu_conf['odahuflow_version']),
+                "opa_policies": {},
+                "packager_version": "{}".format(odahu_conf['packager_version']),
+                "pods_cidr": "{}".format(odahu_conf['pods_cidr']),
+                "service_accounts": {
+                    "airflow": {
+                        "client_id": "sa-airflow",
+                        "client_secret": "{}".format(odahu_conf['airflow_secret'])
+                    },
+                    "operator": {
+                        "client_id": "sa-operator",
+                        "client_secret": "{}".format(odahu_conf['operator_secret'])
+                    },
+                    "resource_uploader": {
+                        "client_id": "sa-resource-uploader",
+                        "client_secret": "{}".format(odahu_conf['resource-uploader_secret'])
+                    },
+                    "test": {
+                        "client_id": "sa-tester",
+                        "client_secret": "{}".format(odahu_conf['tester_secret'])
+                    },
+                    "test_data_scientist": {
+                        "client_id": "sa-tester-data-scientist",
+                        "client_secret": "{}".format(odahu_conf['tester-data-scientist_secret'])
+                    }
+                },
+                "service_cidr": "{}".format(odahu_conf['service_cidr']),
+                "ssh_key": "{}".format(odahu_conf['ssh_key'].replace('\n', '')),
+                "subnet_name": "{}".format(odahu_conf['private_subnet_name']),
+                "tfstate_bucket": "{}-tfstate".format(odahu_conf['cluster_name']),
+                "tls_crt": "{}".format(odahu_conf['tls_crt']),
+                "tls_key": "{}".format(odahu_conf['tls_key']),
+                "vpc_name": "{}".format(odahu_conf['vpc_name']),
+                "vault": {
+                    "enabled": "true"
+                }
+            }
+            profile.write(json.dumps(prof))
+        local('cat /tmp/profile.json')
+        local('cp /tmp/profile.json /')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to configure parameter file.", str(err))
+        sys.exit(1)
+
+    print('Removing Odahu cluster')
+    try:
+        local('tf_runner destroy')
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to terminate Odahu cluster.", str(err))
+        sys.exit(1)
+
+    try:
+        buckets = GCPMeta().get_list_buckets(odahu_conf['cluster_name'])
+        if 'items' in buckets:
+            for i in buckets['items']:
+                GCPActions().remove_bucket(i['name'])
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        sys.exit(1)
+
+    try:
+        static_addresses = GCPMeta().get_list_static_addresses(odahu_conf['region'], odahu_conf['cluster_name'])
+        if 'items' in static_addresses:
+            for i in static_addresses['items']:
+                GCPActions().remove_static_address(i['name'], odahu_conf['region'])
+    except Exception as err:
+        print('Error: {0}'.format(err))
+        sys.exit(1)
diff --git a/infrastructure-provisioning/src/general/scripts/gcp/ssn_configure.py b/infrastructure-provisioning/src/general/scripts/gcp/ssn_configure.py
index 4a10115..a47e53b 100644
--- a/infrastructure-provisioning/src/general/scripts/gcp/ssn_configure.py
+++ b/infrastructure-provisioning/src/general/scripts/gcp/ssn_configure.py
@@ -202,6 +202,7 @@
         additional_config = [{"name": "base", "tag": "latest"},
                              {"name": "project", "tag": "latest"},
                              {"name": "edge", "tag": "latest"},
+                             {"name": "odahu", "tag": "latest"},
                              {"name": "jupyter", "tag": "latest"},
                              {"name": "jupyterlab", "tag": "latest"},
                              {"name": "rstudio", "tag": "latest"},
@@ -213,11 +214,10 @@
                              {"name": "dataengine", "tag": "latest"},
                              {"name": "dataengine-service", "tag": "latest"}]
         params = "--hostname {} --keyfile {} --additional_config '{}' --os_family {} --os_user {} --dlab_path {} " \
-                 "--cloud_provider {} --region {}". \
+                 "--cloud_provider {} --region {} --gcr_creds {} --odahu_image {}". \
             format(ssn_conf['instance_hostname'], ssn_conf['ssh_key_path'], json.dumps(additional_config),
                    os.environ['conf_os_family'], ssn_conf['dlab_ssh_user'], os.environ['ssn_dlab_path'],
-                   os.environ['conf_cloud_provider'], ssn_conf['region'])
-
+                   os.environ['conf_cloud_provider'], ssn_conf['region'], os.environ['ssn_gcr_creds'], os.environ['odahu_deploy_image'])
         try:
             local("~/scripts/{}.py {}".format('configure_docker', params))
         except:
diff --git a/infrastructure-provisioning/src/odahu/fabfile.py b/infrastructure-provisioning/src/odahu/fabfile.py
new file mode 100644
index 0000000..85b09fb
--- /dev/null
+++ b/infrastructure-provisioning/src/odahu/fabfile.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+
+# *****************************************************************************
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ******************************************************************************
+
+import logging
+import json
+import sys
+from dlab.fab import *
+from dlab.meta_lib import *
+from dlab.actions_lib import *
+import os
+import uuid
+
+
+# Main function for provisioning Odahuflow cluster
+def run():
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['edge_user_name'], os.environ['request_id'])
+    local_log_filepath = "/logs/" + os.environ['conf_resource'] + "/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+
+    try:
+        local("~/scripts/{}.py".format('odahu_prepare'))
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed preparing Notebook node.", str(err))
+        sys.exit(1)
+
+    try:
+        local("~/scripts/{}.py".format('odahu_deploy'))
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to deploy Odahuflow cluster.", str(err))
+        sys.exit(1)
+
+# Main function for Odahuflow cluster termination
+def terminate():
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['edge_user_name'], os.environ['request_id'])
+    local_log_filepath = "/logs/" + os.environ['conf_resource'] + "/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+    try:
+        local("~/scripts/{}.py".format('odahu_terminate'))
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to terminate Odahuflow cluster.", str(err))
+        sys.exit(1)
+
+
+# Main function for suspending Odahuflow cluster
+def stop():
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['edge_user_name'], os.environ['request_id'])
+    local_log_filepath = "/logs/" + os.environ['conf_resource'] +  "/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+    try:
+        local("~/scripts/{}.py".format('odahu_suspend'))
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to suspend Odahuflow cluster.", str(err))
+        sys.exit(1)
+
+
+# Main function for resuming Odahuflow cluster
+def start():
+    local_log_filename = "{}_{}_{}.log".format(os.environ['conf_resource'], os.environ['edge_user_name'], os.environ['request_id'])
+    local_log_filepath = "/logs/" + os.environ['conf_resource'] +  "/" + local_log_filename
+    logging.basicConfig(format='%(levelname)-8s [%(asctime)s]  %(message)s',
+                        level=logging.DEBUG,
+                        filename=local_log_filepath)
+    try:
+        local("~/scripts/{}.py".format('odahu_resume'))
+    except Exception as err:
+        traceback.print_exc()
+        append_result("Failed to resume Odahuflow cluster.", str(err))
+        sys.exit(1)
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/odahu/templates/profile.json b/infrastructure-provisioning/src/odahu/templates/profile.json
new file mode 100644
index 0000000..a4b2232
--- /dev/null
+++ b/infrastructure-provisioning/src/odahu/templates/profile.json
@@ -0,0 +1,73 @@
+{
+    "alert_slack_url": "",
+    "allowed_ips": <ALLOWED_IP_CIDR>,
+    "aws_cidr": "",
+    "aws_profile": "",
+    "aws_route_table_id": "",
+    "aws_sg": "",
+    "aws_vpc_id": "",
+    "bastion_tag": "<BASTION_TAG>",
+    "cloud_type": "gcp",
+    "cluster_context": "gke_<PROJECT_ID>_<REGION>_<CLUSTER_NAME>",
+    "cluster_name": "<CLUSTER_NAME>",
+    "cluster_type": "gcp/gke",
+    "config_context_auth_info": "gke_<PROJECT_ID>_<REGION>_<CLUSTER_NAME>",
+    "config_context_cluster": "gke_<PROJECT_ID>_<REGION>_<CLUSTER_NAME>",
+    "dns_zone_name": "<DNS_ZONE_NAME>",
+    "docker_password": "",
+    "docker_repo": "<DOCKER_REPO>",
+    "docker_user": "",
+    "enclave_jwt_secret": "",
+    "gcp_cidr": "<ODAHU_CIDR>",
+    "git_examples_description": "",
+    "git_examples_key": "",
+    "git_examples_reference": "",
+    "git_examples_uri": "",
+    "git_examples_web_ui_link": "",
+    "github_org_name": "",
+    "gke_node_tag": "<CLUSTER_NAME>-gke-node",
+    "grafana_admin": "<GRAFANA_ADMIN>",
+    "grafana_pass": "<GRAFANA_PASS>",
+    "infra_cidr": "",
+    "initial_node_count": "<INITIAL_NODE_COUNT>",
+    "istio_helm_repo": "<ISTIO_HELM_REPO>",
+    "k8s_version": "<K8S_VERSION>",
+    "location": "<REGION>",
+    "oauth_oidc_audience": "legion",
+    "oauth_oidc_issuer_url": "<OAUTH_OIDC_ISSUER_URL>",
+    "data_bucket": "<CLUSTER_NAME>-data-bucket",
+    "helm_repo": "<HELM_REPO>",
+    "odahu_infra_version": "<ODAHU_INFRA_VERSION>",
+    "odahuflow_version": "<ODAHUFLOW_VERSION>",
+    "mlflow_toolchain_version": "<MLFLOW_TOOLCHAIN_VERSION>",
+    "jupyterlab_version": "<JUPYTERLAB_VERSION>",
+    "packager_version": "<PACKAGER_VERSION>",
+    "vpc_name": "<VPC_NAME>",
+    "network_name": "<VPC_NAME>",
+    "subnet_name": "<SUBNET_NAME>",
+    "node_locations": <NODE_LOCATIONS>,
+    "node_version": "<NODE_VERSION>",
+    "oauth_client_id": "<OAUTH_CLIENT_ID>",
+    "oauth_client_secret": "<OAUTH_CLIENT_SECRET>",
+    "oauth_cookie_secret": "<OAUTH_COOCKIE_SECRET>",
+    "oauth_oidc_scope": "openid profile email offline_access groups",
+    "pods_cidr": "<PODS_CIDR>",
+    "project_id": "<PROJECT_ID>",
+    "region": "<REGION>",
+    "root_domain": "<ROOT_DOMAIN>",
+    "service_cidr": "<SERVICE_CIDR>",
+    "ssh_key": "<SSH_KEY>",
+    "test_oauth_auth_url": "",
+    "test_oauth_client_id": "",
+    "test_oauth_client_secret": "",
+    "test_oauth_scope": "",
+    "test_user_email": "",
+    "test_user_password": "",
+    "tfstate_bucket": "<CLUSTER_NAME>-tfstate",
+    "tls_crt": "<TLS_CRT>",
+    "tls_key": "<TLS_KEY>",
+    "zone": "<ZONE>",
+    "dns_project_id": "<DNS_PROJECT_ID>",
+    "odahuflow_connection_decrypt_token": "<DECRYPT_TOKEN>",
+    "infra_vpc_peering": "<INFRA_VPC_PEERING>"
+}
\ No newline at end of file
diff --git a/infrastructure-provisioning/src/ssn/files/aws/mongo_roles.json b/infrastructure-provisioning/src/ssn/files/aws/mongo_roles.json
index 54d2cd6..00d4821 100644
--- a/infrastructure-provisioning/src/ssn/files/aws/mongo_roles.json
+++ b/infrastructure-provisioning/src/ssn/files/aws/mongo_roles.json
@@ -279,7 +279,8 @@
       "/api/settings",
       "/user/settings",
       "/api/project",
-      "/api/endpoint"
+      "/api/endpoint",
+      "/api/odahu"
     ],
     "groups": [
       "$anyuser"
diff --git a/infrastructure-provisioning/src/ssn/files/azure/mongo_roles.json b/infrastructure-provisioning/src/ssn/files/azure/mongo_roles.json
index 305c46b..a001da2 100644
--- a/infrastructure-provisioning/src/ssn/files/azure/mongo_roles.json
+++ b/infrastructure-provisioning/src/ssn/files/azure/mongo_roles.json
@@ -229,7 +229,8 @@
       "/api/settings",
       "/user/settings",
       "/api/project",
-      "/api/endpoint"
+      "/api/endpoint",
+      "/api/odahu"
     ],
     "groups": [
       "$anyuser"
diff --git a/infrastructure-provisioning/src/ssn/files/gcp/mongo_roles.json b/infrastructure-provisioning/src/ssn/files/gcp/mongo_roles.json
index 43d12e3..67548bf 100644
--- a/infrastructure-provisioning/src/ssn/files/gcp/mongo_roles.json
+++ b/infrastructure-provisioning/src/ssn/files/gcp/mongo_roles.json
@@ -259,7 +259,8 @@
       "/api/settings",
       "/user/settings",
       "/api/project",
-      "/api/endpoint"
+      "/api/endpoint",
+      "/api/odahu"
     ],
     "groups": [
       "$anyuser"
diff --git a/infrastructure-provisioning/src/ssn/scripts/configure_docker.py b/infrastructure-provisioning/src/ssn/scripts/configure_docker.py
index 727f97e..86e9e65 100644
--- a/infrastructure-provisioning/src/ssn/scripts/configure_docker.py
+++ b/infrastructure-provisioning/src/ssn/scripts/configure_docker.py
@@ -28,6 +28,7 @@
 from dlab.ssn_lib import *
 import os
 import time
+import base64
 
 parser = argparse.ArgumentParser()
 parser.add_argument('--hostname', type=str, default='')
@@ -39,6 +40,8 @@
 parser.add_argument('--cloud_provider', type=str, default='')
 parser.add_argument('--resource', type=str, default='')
 parser.add_argument('--region', type=str, default='')
+parser.add_argument('--gcr_creds', type=str, default='')
+parser.add_argument('--odahu_image', type=str, default='')
 args = parser.parse_args()
 
 
@@ -78,6 +81,31 @@
         sudo('sed -i "/pip install/s/jupyter/ipython==5.0.0 jupyter==1.0.0/g" Dockerfile')
         sudo('sed -i "22i COPY general/files/os/debian/sources.list /etc/apt/sources.list" Dockerfile')
 
+def login_in_gcr(os_user, gcr_creds, odahu_image, dlab_path, os_family):
+    if os.environ['conf_cloud_provider'] != 'gcp':
+        try:
+            sudo('echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt '
+                  'cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list')
+            sudo('apt-get -y install apt-transport-https ca-certificates gnupg')
+            sudo('curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -')
+            sudo('apt-get update')
+            sudo('apt-get -y install google-cloud-sdk')
+        except Exception as err:
+            traceback.print_exc()
+            print('Failed to install gcloud: ', str(err))
+            sys.exit(1)
+    try:
+        with open('/tmp/config', 'w') as f:
+            f.write(base64.b64decode(gcr_creds))
+        local('scp -i {} /tmp/config {}:/tmp/config'.format(args.keyfile, env.host_string, os_user))
+        sudo('mkdir /home/{}/.docker'.format(os_user))
+        sudo('cp /tmp/config /home/{}/.docker/config.json'.format(os_user))
+        sudo('sed -i "s|ODAHU_IMAGE|{}|" {}sources/infrastructure-provisioning/src/general/files/{}/odahu_Dockerfile'
+             .format(odahu_image, dlab_path, os_family))
+    except Exception as err:
+        traceback.print_exc()
+        print('Failed to prepare odahu image: ', str(err))
+        sys.exit(1)
 
 def build_docker_images(image_list, region, dlab_path):
     try:
@@ -176,6 +204,9 @@
     if not ensure_docker_daemon(args.dlab_path, args.os_user, args.region):
         sys.exit(1)
 
+    print("Login in Google Container Registry")
+    login_in_gcr(args.os_user, args.gcr_creds, args.odahu_image, args.dlab_path, args.os_family)
+
     print("Building dlab images")
     count = 0
     while not build_docker_images(deeper_config, args.region, args.dlab_path) and count < 5:
diff --git a/infrastructure-provisioning/src/ssn/scripts/configure_ssn_node.py b/infrastructure-provisioning/src/ssn/scripts/configure_ssn_node.py
index 42a6a09..1316542 100644
--- a/infrastructure-provisioning/src/ssn/scripts/configure_ssn_node.py
+++ b/infrastructure-provisioning/src/ssn/scripts/configure_ssn_node.py
@@ -199,6 +199,7 @@
         print('Failed to configure docker_build script: ', str(err))
         sys.exit(1)
 
+
 ##############
 # Run script #
 ##############
@@ -262,4 +263,4 @@
     ensure_ciphers()
 
     print("Configuring docker_build script")
-    docker_build_script()
\ No newline at end of file
+    docker_build_script()
diff --git a/infrastructure-provisioning/src/ssn/templates/daemon.json b/infrastructure-provisioning/src/ssn/templates/daemon.json
new file mode 100644
index 0000000..e2257ae
--- /dev/null
+++ b/infrastructure-provisioning/src/ssn/templates/daemon.json
@@ -0,0 +1,5 @@
+{
+  "insecure-registries":["<NEXUS_URL>"],
+  "registry-mirrors": ["http://<NEXUS_URL>"],
+  "disable-legacy-registry": true
+}
\ No newline at end of file
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/ResourceURL.java b/services/dlab-model/src/main/java/com/epam/dlab/dto/ResourceURL.java
index fac4891..44c07e3 100644
--- a/services/dlab-model/src/main/java/com/epam/dlab/dto/ResourceURL.java
+++ b/services/dlab-model/src/main/java/com/epam/dlab/dto/ResourceURL.java
@@ -19,6 +19,7 @@
 
 package com.epam.dlab.dto;
 
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.AllArgsConstructor;
 import lombok.Data;
@@ -30,6 +31,7 @@
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
 public class ResourceURL {
 	@JsonProperty("description")
 	private String description;
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/base/odahu/OdahuResult.java b/services/dlab-model/src/main/java/com/epam/dlab/dto/base/odahu/OdahuResult.java
new file mode 100644
index 0000000..88349ed
--- /dev/null
+++ b/services/dlab-model/src/main/java/com/epam/dlab/dto/base/odahu/OdahuResult.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.dto.base.odahu;
+
+import com.epam.dlab.dto.ResourceURL;
+import com.epam.dlab.dto.StatusBaseDTO;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OdahuResult extends StatusBaseDTO<OdahuResult> {
+    private String name;
+    @JsonProperty("project_name")
+    private String projectName;
+    @JsonProperty("endpoint_name")
+    private String endpointName;
+    @JsonProperty("grafana_admin")
+    private String grafanaAdmin;
+    @JsonProperty("grafana_pass")
+    private String grafanaPassword;
+    @JsonProperty("oauth_cookie_secret")
+    private String oauthCookieSecret;
+    @JsonProperty("odahuflow_connection_decrypt_token")
+    private String decryptToken;
+    @JsonProperty("odahu_urls")
+    private List<ResourceURL> resourceUrls;
+}
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/odahu/ActionOdahuDTO.java b/services/dlab-model/src/main/java/com/epam/dlab/dto/odahu/ActionOdahuDTO.java
new file mode 100644
index 0000000..544ba82
--- /dev/null
+++ b/services/dlab-model/src/main/java/com/epam/dlab/dto/odahu/ActionOdahuDTO.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.dto.odahu;
+
+import com.epam.dlab.dto.ResourceBaseDTO;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class ActionOdahuDTO extends ResourceBaseDTO<ActionOdahuDTO> {
+    @JsonProperty("odahu_cluster_name")
+    private final String name;
+    @JsonProperty("project_name")
+    private final String project;
+    @JsonProperty("endpoint_name")
+    private final String endpoint;
+    @JsonProperty("ssh_key")
+    private final String key;
+    @JsonProperty("grafana_admin")
+    private String grafanaAdmin;
+    @JsonProperty("grafana_pass")
+    private String grafanaPassword;
+    @JsonProperty("oauth_cookie_secret")
+    private String oauthCookieSecret;
+    @JsonProperty("odahuflow_connection_decrypt_token")
+    private String decryptToken;
+}
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/odahu/CreateOdahuDTO.java b/services/dlab-model/src/main/java/com/epam/dlab/dto/odahu/CreateOdahuDTO.java
new file mode 100644
index 0000000..3fea2d4
--- /dev/null
+++ b/services/dlab-model/src/main/java/com/epam/dlab/dto/odahu/CreateOdahuDTO.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.dto.odahu;
+
+import com.epam.dlab.dto.ResourceBaseDTO;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class CreateOdahuDTO extends ResourceBaseDTO<CreateOdahuDTO> {
+    @JsonProperty("odahu_cluster_name")
+    private final String name;
+    @JsonProperty("project_name")
+    private final String project;
+    @JsonProperty("endpoint_name")
+    private final String endpoint;
+    @JsonProperty("ssh_key")
+    private final String key;
+}
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/model/ResourceEnum.java b/services/dlab-model/src/main/java/com/epam/dlab/model/ResourceEnum.java
index 4ee7d2c..dd96c8f 100644
--- a/services/dlab-model/src/main/java/com/epam/dlab/model/ResourceEnum.java
+++ b/services/dlab-model/src/main/java/com/epam/dlab/model/ResourceEnum.java
@@ -22,7 +22,8 @@
 
 public enum ResourceEnum {
 	EDGE_NODE("edge node"),
-	NOTEBOOK("notebook");
+	NOTEBOOK("notebook"),
+	ODAHU("odahu");
 
 	private String name;
 
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/ProvisioningServiceApplication.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/ProvisioningServiceApplication.java
index 6f1047b..0808934 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/ProvisioningServiceApplication.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/ProvisioningServiceApplication.java
@@ -144,6 +144,7 @@
 		jersey.register(injector.getInstance(KeyResource.class));
 		jersey.register(injector.getInstance(CallbackHandlerResource.class));
 		jersey.register(injector.getInstance(ProjectResource.class));
+		jersey.register(injector.getInstance(OdahuResource.class));
 		jersey.register(injector.getInstance(ProvisioningHealthCheckResource.class));
 	}
 }
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/commands/CommandExecutorMockAsync.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/commands/CommandExecutorMockAsync.java
index 7277961..dae3b78 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/commands/CommandExecutorMockAsync.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/commands/CommandExecutorMockAsync.java
@@ -294,7 +294,7 @@
 	private void action(String user, DockerAction action) {
 		String resourceType = parser.getResourceType();
 
-		String prefixFileName = (Lists.newArrayList("project", "edge", "dataengine", "dataengine-service")
+		String prefixFileName = (Lists.newArrayList("project", "edge", "odahu", "dataengine", "dataengine-service")
 				.contains(resourceType) ? resourceType : "notebook") + "_";
 		String templateFileName = "mock_response/" + cloudProvider.getName() + '/' + prefixFileName +
 				action.toString() + JSON_FILE_ENDING;
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/response/handlers/OdahuCallbackHandler.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/response/handlers/OdahuCallbackHandler.java
new file mode 100644
index 0000000..1c63819
--- /dev/null
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/response/handlers/OdahuCallbackHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.core.response.handlers;
+
+import com.epam.dlab.backendapi.core.commands.DockerAction;
+import com.epam.dlab.dto.ResourceURL;
+import com.epam.dlab.dto.base.odahu.OdahuResult;
+import com.epam.dlab.rest.client.RESTService;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+@Slf4j
+public class OdahuCallbackHandler extends ResourceCallbackHandler<OdahuResult> {
+
+    private static final String ODAHU_URLS_FIELD = "odahu_urls";
+    private static final String GRAFANA_ADMIN = "grafana_admin";
+    private static final String GRAFANA_PASSWORD = "grafana_pass";
+    private static final String OAUTH_COOKIE_SECRET = "oauth_cookie_secret";
+    private static final String DECRYPT_TOKEN = "odahuflow_connection_decrypt_token";
+    private final String callbackUri;
+    private final String name;
+    private final String projectName;
+    private final String endpointName;
+
+    public OdahuCallbackHandler(RESTService selfService, String user, String uuid, DockerAction action,
+                                String callbackUri, String name, String projectName, String endpointName) {
+        super(selfService, user, uuid, action);
+        this.callbackUri = callbackUri;
+        this.name = name;
+        this.projectName = projectName;
+        this.endpointName = endpointName;
+    }
+
+    @Override
+    protected String getCallbackURI() {
+        return callbackUri;
+    }
+
+    @Override
+    protected OdahuResult parseOutResponse(JsonNode resultNode, OdahuResult result) {
+        result.setName(name);
+        result.setProjectName(projectName);
+        result.setEndpointName(endpointName);
+
+        if (resultNode == null) {
+            return result;
+        }
+        result.setGrafanaAdmin(getTextValue(resultNode.get(GRAFANA_ADMIN)));
+        result.setGrafanaPassword(getTextValue(resultNode.get(GRAFANA_PASSWORD)));
+        result.setOauthCookieSecret(getTextValue(resultNode.get(OAUTH_COOKIE_SECRET)));
+        result.setDecryptToken(getTextValue(resultNode.get(DECRYPT_TOKEN)));
+
+        final JsonNode odahuUrls = resultNode.get(ODAHU_URLS_FIELD);
+        List<ResourceURL> urls = null;
+        if (odahuUrls != null) {
+            try {
+                urls = mapper.readValue(odahuUrls.toString(), new TypeReference<List<ResourceURL>>() {
+                });
+                result.setResourceUrls(urls);
+            } catch (IOException e) {
+                log.warn("Cannot parse field {} for UUID {} in JSON",
+                        RESPONSE_NODE + "." + RESULT_NODE + "." + ODAHU_URLS_FIELD, getUUID(), e);
+            }
+        }
+
+        if (getAction() == DockerAction.CREATE && Objects.isNull(urls)) {
+            log.warn("There are no odahu urls in response file while creating {}", result);
+        }
+
+        return result;
+    }
+}
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
index 40744fa..f510ec8 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
@@ -28,9 +28,11 @@
 import com.epam.dlab.backendapi.core.response.handlers.dao.CallbackHandlerDao;
 import com.epam.dlab.backendapi.core.response.handlers.dao.FileSystemCallbackHandlerDao;
 import com.epam.dlab.backendapi.service.CheckInactivityService;
+import com.epam.dlab.backendapi.service.OdahuService;
 import com.epam.dlab.backendapi.service.ProjectService;
 import com.epam.dlab.backendapi.service.RestoreCallbackHandlerService;
 import com.epam.dlab.backendapi.service.impl.CheckInactivityServiceImpl;
+import com.epam.dlab.backendapi.service.impl.OdahuServiceImpl;
 import com.epam.dlab.backendapi.service.impl.ProjectServiceImpl;
 import com.epam.dlab.backendapi.service.impl.RestoreCallbackHandlerServiceImpl;
 import com.epam.dlab.constants.ServiceConsts;
@@ -73,5 +75,6 @@
 		bind(RestoreCallbackHandlerService.class).to(RestoreCallbackHandlerServiceImpl.class);
 		bind(CheckInactivityService.class).to(CheckInactivityServiceImpl.class);
 		bind(ProjectService.class).to(ProjectServiceImpl.class);
+		bind(OdahuService.class).to(OdahuServiceImpl.class);
 	}
 }
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProvisioningDevModule.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProvisioningDevModule.java
index 73d333f..8329c71 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProvisioningDevModule.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProvisioningDevModule.java
@@ -30,11 +30,12 @@
 import com.epam.dlab.backendapi.core.commands.ICommandExecutor;
 import com.epam.dlab.backendapi.core.response.handlers.dao.CallbackHandlerDao;
 import com.epam.dlab.backendapi.core.response.handlers.dao.FileSystemCallbackHandlerDao;
+import com.epam.dlab.backendapi.service.CheckInactivityService;
+import com.epam.dlab.backendapi.service.OdahuService;
 import com.epam.dlab.backendapi.service.ProjectService;
 import com.epam.dlab.backendapi.service.RestoreCallbackHandlerService;
-import com.epam.dlab.backendapi.service.CheckInactivityService;
-import com.epam.dlab.backendapi.service.RestoreCallbackHandlerService;
 import com.epam.dlab.backendapi.service.impl.CheckInactivityServiceImpl;
+import com.epam.dlab.backendapi.service.impl.OdahuServiceImpl;
 import com.epam.dlab.backendapi.service.impl.ProjectServiceImpl;
 import com.epam.dlab.backendapi.service.impl.RestoreCallbackHandlerServiceImpl;
 import com.epam.dlab.constants.ServiceConsts;
@@ -80,6 +81,7 @@
 		bind(RestoreCallbackHandlerService.class).to(RestoreCallbackHandlerServiceImpl.class);
 		bind(CheckInactivityService.class).to(CheckInactivityServiceImpl.class);
 		bind(ProjectService.class).to(ProjectServiceImpl.class);
+		bind(OdahuService.class).to(OdahuServiceImpl.class);
 	}
 
 	/**
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java
new file mode 100644
index 0000000..670a653
--- /dev/null
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.resources;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.epam.dlab.dto.odahu.ActionOdahuDTO;
+import com.epam.dlab.dto.odahu.CreateOdahuDTO;
+import com.google.inject.Inject;
+import io.dropwizard.auth.Auth;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+@Path("infrastructure/odahu")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public class OdahuResource {
+
+    private final OdahuService odahuService;
+
+    @Inject
+    public OdahuResource(OdahuService odahuService) {
+        this.odahuService = odahuService;
+    }
+
+    @POST
+    public Response createProject(@Auth UserInfo userInfo, CreateOdahuDTO dto) {
+        return Response.ok(odahuService.create(userInfo, dto)).build();
+    }
+
+    @Path("start")
+    @POST
+    public Response startProject(@Auth UserInfo userInfo, ActionOdahuDTO dto) {
+        return Response.ok(odahuService.start(userInfo, dto)).build();
+    }
+
+    @Path("stop")
+    @POST
+    public Response stopProject(@Auth UserInfo userInfo, ActionOdahuDTO dto) {
+        return Response.ok(odahuService.stop(userInfo, dto)).build();
+    }
+
+    @Path("terminate")
+    @POST
+    public Response terminateProject(@Auth UserInfo userInfo, ActionOdahuDTO dto) {
+        return Response.ok(odahuService.terminate(userInfo, dto)).build();
+    }
+}
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java
new file mode 100644
index 0000000..286e2be
--- /dev/null
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.service;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.dto.odahu.ActionOdahuDTO;
+import com.epam.dlab.dto.odahu.CreateOdahuDTO;
+
+public interface OdahuService {
+    String create(UserInfo userInfo, CreateOdahuDTO odahuCreateDTO);
+
+    String start(UserInfo userInfo, ActionOdahuDTO dto);
+
+    String stop(UserInfo userInfo, ActionOdahuDTO dto);
+
+    String terminate(UserInfo userInfo, ActionOdahuDTO dto);
+}
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java
new file mode 100644
index 0000000..5d032e0
--- /dev/null
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.service.impl;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.ProvisioningServiceApplicationConfiguration;
+import com.epam.dlab.backendapi.core.commands.CommandBuilder;
+import com.epam.dlab.backendapi.core.commands.DockerAction;
+import com.epam.dlab.backendapi.core.commands.DockerCommands;
+import com.epam.dlab.backendapi.core.commands.ICommandExecutor;
+import com.epam.dlab.backendapi.core.commands.RunDockerCommand;
+import com.epam.dlab.backendapi.core.response.folderlistener.FolderListenerExecutor;
+import com.epam.dlab.backendapi.core.response.handlers.OdahuCallbackHandler;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.epam.dlab.dto.ResourceBaseDTO;
+import com.epam.dlab.dto.odahu.ActionOdahuDTO;
+import com.epam.dlab.dto.odahu.CreateOdahuDTO;
+import com.epam.dlab.rest.client.RESTService;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.inject.Inject;
+
+public class OdahuServiceImpl implements OdahuService {
+
+    private static final String CALLBACK_URI = "/api/odahu/status";
+    private static final String ODAHU_RESOURCE_TYPE = "odahu";
+    private static final String ODAHU_IMAGE = "docker.dlab-odahu";
+
+    private final ProvisioningServiceApplicationConfiguration configuration;
+    private final FolderListenerExecutor folderListenerExecutor;
+    private final CommandBuilder commandBuilder;
+    private final ICommandExecutor commandExecutor;
+    private final RESTService selfService;
+
+    @Inject
+    public OdahuServiceImpl(ProvisioningServiceApplicationConfiguration configuration,
+                            FolderListenerExecutor folderListenerExecutor, CommandBuilder commandBuilder,
+                            ICommandExecutor commandExecutor, RESTService selfService) {
+        this.configuration = configuration;
+        this.folderListenerExecutor = folderListenerExecutor;
+        this.commandBuilder = commandBuilder;
+        this.commandExecutor = commandExecutor;
+        this.selfService = selfService;
+    }
+
+    @Override
+    public String create(UserInfo userInfo, CreateOdahuDTO dto) {
+        return executeDocker(userInfo, dto, DockerAction.CREATE, ODAHU_RESOURCE_TYPE, ODAHU_IMAGE, dto.getName(),
+                dto.getProject(), dto.getEndpoint());
+    }
+
+    @Override
+    public String start(UserInfo userInfo, ActionOdahuDTO dto) {
+        return executeDocker(userInfo, dto, DockerAction.START, ODAHU_RESOURCE_TYPE, ODAHU_IMAGE, dto.getName(),
+                dto.getProject(), dto.getEndpoint());
+    }
+
+    @Override
+    public String stop(UserInfo userInfo, ActionOdahuDTO dto) {
+        return executeDocker(userInfo, dto, DockerAction.STOP, ODAHU_RESOURCE_TYPE, ODAHU_IMAGE, dto.getName(),
+                dto.getProject(), dto.getEndpoint());
+    }
+
+    @Override
+    public String terminate(UserInfo userInfo, ActionOdahuDTO dto) {
+        return executeDocker(userInfo, dto, DockerAction.TERMINATE, ODAHU_RESOURCE_TYPE, ODAHU_IMAGE, dto.getName(),
+                dto.getProject(), dto.getEndpoint());
+    }
+
+
+    private String executeDocker(UserInfo userInfo, ResourceBaseDTO dto, DockerAction action, String resourceType,
+                                 String image, String name, String project, String endpoint) {
+        String uuid = DockerCommands.generateUUID();
+
+        folderListenerExecutor.start(configuration.getKeyLoaderDirectory(),
+                configuration.getKeyLoaderPollTimeout(),
+                new OdahuCallbackHandler(selfService, userInfo.getName(), uuid, action, CALLBACK_URI, name, project, endpoint));
+
+        RunDockerCommand runDockerCommand = new RunDockerCommand()
+                .withInteractive()
+                .withName(String.join("_", userInfo.getSimpleName(), name, resourceType, action.toString(),
+                        Long.toString(System.currentTimeMillis())))
+                .withVolumeForRootKeys(configuration.getKeyDirectory())
+                .withVolumeForResponse(configuration.getKeyLoaderDirectory())
+                .withVolumeForLog(configuration.getDockerLogDirectory(), resourceType)
+                .withResource(resourceType)
+                .withRequestId(uuid)
+                .withConfKeyName(configuration.getAdminKey())
+                .withImage(image)
+                .withAction(action);
+
+        try {
+            commandExecutor.executeAsync(userInfo.getName(), uuid, commandBuilder.buildCommand(runDockerCommand, dto));
+        } catch (JsonProcessingException e) {
+            e.printStackTrace();
+        }
+        return uuid;
+    }
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/aws/odahu_create.json b/services/provisioning-service/src/main/resources/mock_response/aws/odahu_create.json
new file mode 100644
index 0000000..955d08b
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/aws/odahu_create.json
@@ -0,0 +1,23 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+      "odahu_urls": [
+        {
+          "url": "http://10.10.16.2:8888/",
+          "description": "API Gateway"
+        },
+        {
+          "url": "http://10.10.16.2:8085/",
+          "description": "Documentation"
+        }
+      ],
+      "oauth_cookie_secret": "WlFRZzhhZTJxbTFNa0JpUw==",
+      "odahuflow_connection_decrypt_token": "qfY2k5bA2M",
+      "grafana_pass": "7h0uum1EGe",
+      "grafana_admin": "grafana_admin"
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/aws/odahu_start.json b/services/provisioning-service/src/main/resources/mock_response/aws/odahu_start.json
new file mode 100644
index 0000000..99433bd
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/aws/odahu_start.json
@@ -0,0 +1,9 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/aws/odahu_stop.json b/services/provisioning-service/src/main/resources/mock_response/aws/odahu_stop.json
new file mode 100644
index 0000000..99433bd
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/aws/odahu_stop.json
@@ -0,0 +1,9 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/aws/odahu_terminate.json b/services/provisioning-service/src/main/resources/mock_response/aws/odahu_terminate.json
new file mode 100644
index 0000000..99433bd
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/aws/odahu_terminate.json
@@ -0,0 +1,9 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/azure/odahu_create.json b/services/provisioning-service/src/main/resources/mock_response/azure/odahu_create.json
new file mode 100644
index 0000000..955d08b
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/azure/odahu_create.json
@@ -0,0 +1,23 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+      "odahu_urls": [
+        {
+          "url": "http://10.10.16.2:8888/",
+          "description": "API Gateway"
+        },
+        {
+          "url": "http://10.10.16.2:8085/",
+          "description": "Documentation"
+        }
+      ],
+      "oauth_cookie_secret": "WlFRZzhhZTJxbTFNa0JpUw==",
+      "odahuflow_connection_decrypt_token": "qfY2k5bA2M",
+      "grafana_pass": "7h0uum1EGe",
+      "grafana_admin": "grafana_admin"
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/azure/odahu_start.json b/services/provisioning-service/src/main/resources/mock_response/azure/odahu_start.json
new file mode 100644
index 0000000..99433bd
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/azure/odahu_start.json
@@ -0,0 +1,9 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/azure/odahu_stop.json b/services/provisioning-service/src/main/resources/mock_response/azure/odahu_stop.json
new file mode 100644
index 0000000..99433bd
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/azure/odahu_stop.json
@@ -0,0 +1,9 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/azure/odahu_terminate.json b/services/provisioning-service/src/main/resources/mock_response/azure/odahu_terminate.json
new file mode 100644
index 0000000..99433bd
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/azure/odahu_terminate.json
@@ -0,0 +1,9 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_create.json b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_create.json
new file mode 100644
index 0000000..955d08b
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_create.json
@@ -0,0 +1,23 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+      "odahu_urls": [
+        {
+          "url": "http://10.10.16.2:8888/",
+          "description": "API Gateway"
+        },
+        {
+          "url": "http://10.10.16.2:8085/",
+          "description": "Documentation"
+        }
+      ],
+      "oauth_cookie_secret": "WlFRZzhhZTJxbTFNa0JpUw==",
+      "odahuflow_connection_decrypt_token": "qfY2k5bA2M",
+      "grafana_pass": "7h0uum1EGe",
+      "grafana_admin": "grafana_admin"
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_start.json b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_start.json
new file mode 100644
index 0000000..99433bd
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_start.json
@@ -0,0 +1,9 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_stop.json b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_stop.json
new file mode 100644
index 0000000..99433bd
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_stop.json
@@ -0,0 +1,9 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_terminate.json b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_terminate.json
new file mode 100644
index 0000000..99433bd
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_terminate.json
@@ -0,0 +1,9 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java
index 6a12ea5..3b268b9 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java
@@ -161,6 +161,8 @@
 		jersey.register(injector.getInstance(EndpointResource.class));
 		jersey.register(injector.getInstance(ProjectResource.class));
 		jersey.register(injector.getInstance(ProjectCallback.class));
+		jersey.register(injector.getInstance(OdahuResource.class));
+		jersey.register(injector.getInstance(OdahuCallback.class));
 	}
 
 	private void disableGzipHandlerForGuacamoleServlet(Server server) {
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/ExploratoryDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/ExploratoryDAO.java
index ebe81c5..1adade7 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/ExploratoryDAO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/ExploratoryDAO.java
@@ -113,8 +113,8 @@
 	 * @param user name
 	 * @return list of user resources
 	 */
-	public Iterable<Document> findExploratory(String user) {
-		return find(USER_INSTANCES, eq(USER, user),
+	public Iterable<Document> findExploratory(String user, String project) {
+		return find(USER_INSTANCES, and(eq(USER, user), eq(PROJECT, project)),
 				fields(exclude(ExploratoryLibDAO.EXPLORATORY_LIBS, ExploratoryLibDAO.COMPUTATIONAL_LIBS, SCHEDULER_DATA,
 						EXPLORATORY_USER, EXPLORATORY_PASS)));
 	}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAO.java
new file mode 100644
index 0000000..f323dbc
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAO.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.dao;
+
+import com.epam.dlab.backendapi.domain.OdahuDTO;
+import com.epam.dlab.backendapi.domain.OdahuFieldsDTO;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.epam.dlab.dto.base.odahu.OdahuResult;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface OdahuDAO {
+    Optional<OdahuDTO> getByProjectEndpoint(String project, String endpoint);
+
+    OdahuFieldsDTO getFields(String name, String project, String endpoint);
+
+    List<OdahuDTO> findOdahuClusters();
+
+    boolean create(OdahuDTO odahuDTO);
+
+    void updateStatus(String name, String project, String endpoint, UserInstanceStatus status);
+
+    void updateStatusAndUrls(OdahuResult result, UserInstanceStatus status);
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAOImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAOImpl.java
new file mode 100644
index 0000000..5ba4c80
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAOImpl.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.dao;
+
+import com.epam.dlab.backendapi.domain.OdahuDTO;
+import com.epam.dlab.backendapi.domain.OdahuFieldsDTO;
+import com.epam.dlab.backendapi.domain.ProjectDTO;
+import com.epam.dlab.dto.ResourceURL;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.epam.dlab.dto.base.odahu.OdahuResult;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.mongodb.BasicDBObject;
+import com.mongodb.client.result.UpdateResult;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.mongodb.client.model.Filters.and;
+import static com.mongodb.client.model.Filters.eq;
+import static com.mongodb.client.model.Projections.elemMatch;
+import static com.mongodb.client.model.Projections.excludeId;
+import static com.mongodb.client.model.Projections.fields;
+import static com.mongodb.client.model.Projections.include;
+import static com.mongodb.client.model.Updates.push;
+import static java.util.stream.Collectors.toList;
+
+public class OdahuDAOImpl extends BaseDAO implements OdahuDAO {
+
+    private static final String PROJECTS_COLLECTION = "Projects";
+    private static final String ENDPOINTS = "endpoints";
+    private static final String ODAHU_FIELD = "odahu";
+    private static final String NAME_FIELD = "name";
+    private static final String ENDPOINT_FIELD = "endpoint";
+    private static final String PROJECT_FIELD = "project";
+    private static final String STATUS_FIELD = "status";
+    private static final String GRAFANA_ADMIN_FIELD = "grafana_admin";
+    private static final String GRAFANA_PASSWORD_FIELD = "grafana_pass";
+    private static final String OAUTH_COOKIE_SECRET_FIELD = "oauth_cookie_secret";
+    private static final String DECRYPT_TOKEN_FIELD = "odahuflow_connection_decrypt_token";
+    private static final String URLS_FIELD = "urls";
+    private static final String COMPUTATIONAL_URL_DESC = "description";
+    private static final String COMPUTATIONAL_URL_URL = "url";
+
+    @Override
+    public Optional<OdahuDTO> getByProjectEndpoint(String project, String endpoint) {
+        Optional<ProjectDTO> projectDTO = findOne(PROJECTS_COLLECTION, odahuProjectEndpointCondition(project, endpoint),
+                fields(include(ODAHU_FIELD), excludeId()),
+                ProjectDTO.class);
+
+        return projectDTO.flatMap(p -> p.getOdahu()
+                .stream()
+                .filter(odahu -> project.equals(odahu.getProject()) && endpoint.equals(odahu.getEndpoint()))
+                .findAny());
+    }
+
+    @Override
+    public OdahuFieldsDTO getFields(String name, String project, String endpoint) {
+        Optional<Document> one = findOne(PROJECTS_COLLECTION, odahuProjectEndpointCondition(name, project, endpoint),
+                fields(include(ODAHU_FIELD), excludeId()));
+        OdahuFieldsDTO odahuFields = null;
+        if (one.isPresent()) {
+            List<OdahuFieldsDTO> list = convertFromDocument(one.get().get(ODAHU_FIELD, ArrayList.class), new TypeReference<List<OdahuFieldsDTO>>() {
+            });
+            odahuFields = list.get(0);
+        }
+        return odahuFields;
+    }
+
+    @Override
+    public List<OdahuDTO> findOdahuClusters() {
+        List<ProjectDTO> projectDTOS = find(PROJECTS_COLLECTION, ProjectDTO.class);
+        return projectDTOS.stream()
+                .map(ProjectDTO::getOdahu)
+                .flatMap(List::stream)
+                .collect(toList());
+    }
+
+    @Override
+    public boolean create(OdahuDTO odahuDTO) {
+        UpdateResult updateResult = updateOne(PROJECTS_COLLECTION, projectEndpointCondition(odahuDTO.getProject(),
+                odahuDTO.getEndpoint()),
+                push(ODAHU_FIELD, convertToBson(odahuDTO)));
+        return updateResult.getModifiedCount() > 0;
+    }
+
+    @Override
+    public void updateStatus(String name, String project, String endpoint, UserInstanceStatus status) {
+        BasicDBObject dbObject = new BasicDBObject();
+        dbObject.put(ODAHU_FIELD + ".$." + STATUS_FIELD, status.name());
+        updateOne(PROJECTS_COLLECTION, and(elemMatch(ODAHU_FIELD, eq(NAME_FIELD, name)),
+                odahuProjectEndpointCondition(project, endpoint)), new Document(SET, dbObject));
+    }
+
+    @Override
+    public void updateStatusAndUrls(OdahuResult result, UserInstanceStatus status) {
+        BasicDBObject dbObject = new BasicDBObject();
+        dbObject.put(ODAHU_FIELD + ".$." + STATUS_FIELD, status.name());
+        dbObject.put(ODAHU_FIELD + ".$." + URLS_FIELD, getResourceUrlData(result.getResourceUrls()));
+        dbObject.put(ODAHU_FIELD + ".$." + GRAFANA_ADMIN_FIELD, result.getGrafanaAdmin());
+        dbObject.put(ODAHU_FIELD + ".$." + GRAFANA_PASSWORD_FIELD, result.getGrafanaPassword());
+        dbObject.put(ODAHU_FIELD + ".$." + OAUTH_COOKIE_SECRET_FIELD, result.getOauthCookieSecret());
+        dbObject.put(ODAHU_FIELD + ".$." + DECRYPT_TOKEN_FIELD, result.getDecryptToken());
+        updateOne(PROJECTS_COLLECTION, odahuProjectEndpointCondition(result.getName(), result.getProjectName(), result.getEndpointName()),
+                new Document(SET, dbObject));
+    }
+
+    private Bson odahuProjectEndpointCondition(String name, String projectName, String endpointName) {
+        return and(elemMatch(ODAHU_FIELD, eq(NAME_FIELD, name)), odahuProjectEndpointCondition(projectName, endpointName));
+    }
+
+    private Bson odahuProjectEndpointCondition(String projectName, String endpointName) {
+        return elemMatch(ODAHU_FIELD, and(eq(ENDPOINT_FIELD, endpointName), eq(PROJECT_FIELD, projectName)));
+    }
+
+    private Bson projectEndpointCondition(String projectName, String endpointName) {
+        return and(eq(NAME_FIELD, projectName), and(elemMatch(ENDPOINTS, eq(NAME_FIELD, endpointName))));
+    }
+
+    private List<Map<String, String>> getResourceUrlData(List<ResourceURL> urls) {
+        return urls.stream()
+                .map(this::toUrlDocument)
+                .collect(toList());
+    }
+
+    private LinkedHashMap<String, String> toUrlDocument(ResourceURL url) {
+        LinkedHashMap<String, String> map = new LinkedHashMap<>();
+        map.put(COMPUTATIONAL_URL_URL, url.getUrl());
+        map.put(COMPUTATIONAL_URL_DESC, url.getDescription());
+        return map;
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuActionDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuActionDTO.java
new file mode 100644
index 0000000..fa86750
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuActionDTO.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OdahuActionDTO {
+    @NotNull
+    private final String name;
+    @NotNull
+    private final String project;
+    @NotNull
+    private final String endpoint;
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuCreateDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuCreateDTO.java
new file mode 100644
index 0000000..6c6cb27
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuCreateDTO.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OdahuCreateDTO {
+    @NotNull
+    private final String name;
+    @NotNull
+    private final String project;
+    @NotNull
+    private final String endpoint;
+    @JsonProperty("custom_tag")
+    private final String customTag;
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuDTO.java
new file mode 100644
index 0000000..6ecf35c
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuDTO.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.domain;
+
+import com.epam.dlab.dto.ResourceURL;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OdahuDTO {
+    private final String name;
+    private final String project;
+    private final String endpoint;
+    @JsonProperty("grafana_admin")
+    private String grafanaAdmin;
+    @JsonProperty("grafana_pass")
+    private String grafanaPassword;
+    private final List<ResourceURL> urls = new ArrayList<>();
+    private final UserInstanceStatus status;
+    private final Map<String, String> tags;
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuFieldsDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuFieldsDTO.java
new file mode 100644
index 0000000..7a71795
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuFieldsDTO.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OdahuFieldsDTO {
+    @JsonProperty("grafana_admin")
+    private String grafanaAdmin;
+    @JsonProperty("grafana_pass")
+    private String grafanaPassword;
+    @JsonProperty("oauth_cookie_secret")
+    private String oauthCookieSecret;
+    @JsonProperty("odahuflow_connection_decrypt_token")
+    private String decryptToken;
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/ProjectDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/ProjectDTO.java
index 9cd0a35..6bd8a1e 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/ProjectDTO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/ProjectDTO.java
@@ -8,6 +8,7 @@
 
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Pattern;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
@@ -28,6 +29,7 @@
 	private final Integer budget;
 	private final List<ProjectEndpointDTO> endpoints;
 	private final boolean sharedImageEnabled;
+	private final List<OdahuDTO> odahu = new ArrayList<>();
 
 
 	public enum Status {
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
index cf08d12..f1a9a3e 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
@@ -106,6 +106,8 @@
 		bind(EndpointDAO.class).to(EndpointDAOImpl.class);
 		bind(ProjectService.class).to(ProjectServiceImpl.class);
 		bind(ProjectDAO.class).to(ProjectDAOImpl.class);
+		bind(OdahuDAO.class).to(OdahuDAOImpl.class);
+		bind(OdahuService.class).to(OdahuServiceImpl.class);
 	}
 
 	private void configureCors(Environment environment) {
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
index 0d0ae1d..b67584e 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
@@ -99,5 +99,7 @@
 		bind(SecurityService.class).to(SecurityServiceImpl.class);
 		bind(KeycloakService.class).to(KeycloakServiceImpl.class);
 		bind(Client.class).toInstance(httpClient);
+		bind(OdahuDAO.class).to(OdahuDAOImpl.class);
+		bind(OdahuService.class).to(OdahuServiceImpl.class);
 	}
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/InfrastructureInfoResource.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/InfrastructureInfoResource.java
index db8197f..b9dee92 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/InfrastructureInfoResource.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/InfrastructureInfoResource.java
@@ -78,7 +78,7 @@
 	@GET
 	@Path("/info")
 	public List<ProjectInfrastructureInfo> getUserResources(@Auth UserInfo userInfo) {
-		return infrastructureInfoService.getUserResources(userInfo.getName());
+		return infrastructureInfoService.getUserResources(userInfo);
 
 	}
 
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java
new file mode 100644
index 0000000..a254b9b
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.resources;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.domain.OdahuActionDTO;
+import com.epam.dlab.backendapi.domain.OdahuCreateDTO;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.google.inject.Inject;
+import io.dropwizard.auth.Auth;
+import io.swagger.v3.oas.annotations.Parameter;
+
+import javax.annotation.security.RolesAllowed;
+import javax.validation.Valid;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+
+@Path("odahu")
+@Consumes(MediaType.APPLICATION_JSON)
+public class OdahuResource {
+
+    private final OdahuService odahuService;
+
+    @Inject
+    public OdahuResource(OdahuService odahuService) {
+        this.odahuService = odahuService;
+    }
+
+    @GET
+    @RolesAllowed("/api/odahu")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getOdahuClusters(@Parameter(hidden = true) @Auth UserInfo userInfo) {
+        return Response.ok(odahuService.findOdahu()).build();
+    }
+
+    @POST
+    @RolesAllowed("/api/odahu")
+    public Response createOdahuCluster(@Parameter(hidden = true) @Auth UserInfo userInfo,
+                                       @Parameter(hidden = true) @Context UriInfo uriInfo,
+                                       @Valid OdahuCreateDTO odahuCreateDTO) {
+        odahuService.create(odahuCreateDTO.getProject(), odahuCreateDTO, userInfo);
+        final URI uri = uriInfo.getRequestUriBuilder().path(odahuCreateDTO.getName()).build();
+        return Response.created(uri).build();
+    }
+
+    @Path("start")
+    @POST
+    @RolesAllowed("/api/odahu")
+    public Response startOdahuCluster(@Parameter(hidden = true) @Auth UserInfo userInfo,
+                                      @Valid OdahuActionDTO startOdahuDTO) {
+        odahuService.start(startOdahuDTO.getName(), startOdahuDTO.getProject(), startOdahuDTO.getEndpoint(), userInfo);
+        return Response.accepted().build();
+    }
+
+    @Path("stop")
+    @POST
+    @RolesAllowed("/api/odahu")
+    public Response stopOdahuCluster(@Parameter(hidden = true) @Auth UserInfo userInfo,
+                                     @Valid OdahuActionDTO stopOdahuDTO) {
+        odahuService.stop(stopOdahuDTO.getName(), stopOdahuDTO.getProject(), stopOdahuDTO.getEndpoint(), userInfo);
+        return Response.accepted().build();
+    }
+
+    @Path("terminate")
+    @POST
+    @RolesAllowed("/api/odahu")
+    public Response terminateOdahuCluster(@Parameter(hidden = true) @Auth UserInfo userInfo,
+                                          @Valid OdahuActionDTO terminateOdahuDTO) {
+        odahuService.terminate(terminateOdahuDTO.getName(), terminateOdahuDTO.getProject(), terminateOdahuDTO.getEndpoint(), userInfo);
+        return Response.accepted().build();
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/callback/OdahuCallback.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/callback/OdahuCallback.java
new file mode 100644
index 0000000..6501ff7
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/callback/OdahuCallback.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.resources.callback;
+
+import com.epam.dlab.backendapi.domain.RequestId;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.epam.dlab.dto.base.odahu.OdahuResult;
+import com.epam.dlab.exceptions.DlabException;
+import com.google.inject.Inject;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.Optional;
+
+@Path("odahu/status")
+@Consumes(MediaType.APPLICATION_JSON)
+public class OdahuCallback {
+
+    private final OdahuService odahuService;
+    private final RequestId requestId;
+
+    @Inject
+    public OdahuCallback(OdahuService odahuService, RequestId requestId) {
+        this.odahuService = odahuService;
+        this.requestId = requestId;
+    }
+
+    @POST
+    public Response updateOdahuStatus(OdahuResult result) {
+        requestId.checkAndRemove(result.getRequestId());
+        final UserInstanceStatus status = UserInstanceStatus.of(result.getStatus());
+        Optional.ofNullable(status)
+                .orElseThrow(() -> new DlabException(String.format("Cannot convert %s to UserInstanceStatus", status)));
+
+        odahuService.updateStatus(result, status);
+        return Response.ok().build();
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/ProjectInfrastructureInfo.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/ProjectInfrastructureInfo.java
index 44837a2..5a436ea 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/ProjectInfrastructureInfo.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/ProjectInfrastructureInfo.java
@@ -20,6 +20,7 @@
 package com.epam.dlab.backendapi.resources.dto;
 
 import com.epam.dlab.backendapi.domain.EndpointDTO;
+import com.epam.dlab.backendapi.domain.OdahuDTO;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.AllArgsConstructor;
 import lombok.ToString;
@@ -40,5 +41,7 @@
 	@JsonProperty
 	private Iterable<Document> exploratory;
 	@JsonProperty
+	private List<OdahuDTO> odahu;
+	@JsonProperty
 	private List<EndpointDTO> endpoints;
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/InfrastructureInfoService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/InfrastructureInfoService.java
index aa23d1d..a385925 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/InfrastructureInfoService.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/InfrastructureInfoService.java
@@ -27,7 +27,7 @@
 import java.util.List;
 
 public interface InfrastructureInfoService {
-	List<ProjectInfrastructureInfo> getUserResources(String user);
+	List<ProjectInfrastructureInfo> getUserResources(UserInfo user);
 
 	HealthStatusPageDTO getHeathStatus(UserInfo user, boolean fullReport, boolean isAdmin);
 
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java
new file mode 100644
index 0000000..e787991
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.service;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.domain.OdahuCreateDTO;
+import com.epam.dlab.backendapi.domain.OdahuDTO;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.epam.dlab.dto.base.odahu.OdahuResult;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface OdahuService {
+    List<OdahuDTO> findOdahu();
+
+    Optional<OdahuDTO> get(String project, String endpoint);
+
+    void create(String project, OdahuCreateDTO createOdahuDTO, UserInfo userInfo);
+
+    void start(String name, String project, String endpoint, UserInfo user);
+
+    void stop(String name, String project, String endpoint, UserInfo user);
+
+    void terminate(String name, String project, String endpoint, UserInfo user);
+
+    void updateStatus(OdahuResult odahuResult, UserInstanceStatus status);
+
+    boolean inProgress(String project, String endpoint);
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EndpointServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EndpointServiceImpl.java
index 9c71f76..4a8956d 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EndpointServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EndpointServiceImpl.java
@@ -8,6 +8,7 @@
 import com.epam.dlab.backendapi.domain.EndpointResourcesDTO;
 import com.epam.dlab.backendapi.domain.ProjectDTO;
 import com.epam.dlab.backendapi.service.EndpointService;
+import com.epam.dlab.backendapi.service.OdahuService;
 import com.epam.dlab.backendapi.service.ProjectService;
 import com.epam.dlab.cloud.CloudProvider;
 import com.epam.dlab.constants.ServiceConsts;
@@ -37,17 +38,19 @@
 	private final ProjectService projectService;
 	private final ExploratoryDAO exploratoryDAO;
 	private final RESTService provisioningService;
+	private final OdahuService odahuService;
 	private final UserRoleDao userRoleDao;
 
 	@Inject
 	public EndpointServiceImpl(EndpointDAO endpointDAO, ProjectService projectService, ExploratoryDAO exploratoryDAO,
 							   @Named(ServiceConsts.PROVISIONING_SERVICE_NAME) RESTService provisioningService,
-							   UserRoleDao userRoleDao) {
+							   OdahuService odahuService, UserRoleDao userRoleDao) {
 
 		this.endpointDAO = endpointDAO;
 		this.projectService = projectService;
 		this.exploratoryDAO = exploratoryDAO;
 		this.provisioningService = provisioningService;
+		this.odahuService = odahuService;
 		this.userRoleDao = userRoleDao;
 	}
 
@@ -149,7 +152,8 @@
 
 	private void checkProjectEndpointResourcesStatuses(List<ProjectDTO> projects, String endpoint) {
 		boolean isTerminationEnabled = projects.stream().anyMatch(p ->
-				!projectService.checkExploratoriesAndComputationalProgress(p.getName(), Collections.singletonList(endpoint)) ||
+				odahuService.inProgress(p.getName(), endpoint) ||
+						!projectService.checkExploratoriesAndComputationalProgress(p.getName(), Collections.singletonList(endpoint)) ||
 						p.getEndpoints().stream().anyMatch(e -> e.getName().equals(endpoint) &&
 								Arrays.asList(UserInstanceStatus.CREATING, UserInstanceStatus.STARTING, UserInstanceStatus.STOPPING,
 										UserInstanceStatus.TERMINATING).contains(e.getStatus())));
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EnvironmentServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EnvironmentServiceImpl.java
index 3dfaaae..f86fd72 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EnvironmentServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/EnvironmentServiceImpl.java
@@ -23,6 +23,7 @@
 import com.epam.dlab.backendapi.dao.EnvDAO;
 import com.epam.dlab.backendapi.dao.ExploratoryDAO;
 import com.epam.dlab.backendapi.dao.UserSettingsDAO;
+import com.epam.dlab.backendapi.domain.OdahuDTO;
 import com.epam.dlab.backendapi.domain.ProjectDTO;
 import com.epam.dlab.backendapi.resources.dto.UserDTO;
 import com.epam.dlab.backendapi.resources.dto.UserResourceInfo;
@@ -98,7 +99,8 @@
 		List<UserInstanceDTO> expList = exploratoryDAO.getInstances();
 		return projectService.getProjects()
 				.stream()
-				.map(projectDTO -> getProjectEnv(projectDTO, expList)).flatMap(Collection::stream)
+				.map(projectDTO -> getProjectEnv(projectDTO, expList))
+				.flatMap(Collection::stream)
 				.collect(toList());
 	}
 
@@ -186,7 +188,12 @@
 
 	private List<UserResourceInfo> getProjectEnv(ProjectDTO projectDTO, List<UserInstanceDTO> allInstances) {
 		final Stream<UserResourceInfo> userResources = allInstances.stream()
-				.filter(instance -> instance.getProject().equals(projectDTO.getName())).map(this::toUserResourceInfo);
+				.filter(instance -> instance.getProject().equals(projectDTO.getName()))
+				.map(this::toUserResourceInfo);
+
+		Stream<UserResourceInfo> odahuResources = projectDTO.getOdahu().stream()
+				.map(this::toUserResourceInfo);
+
 		if (projectDTO.getEndpoints() != null) {
 			final Stream<UserResourceInfo> edges = projectDTO.getEndpoints()
 					.stream()
@@ -194,7 +201,7 @@
 							.withResourceStatus(e.getStatus().toString())
 							.withProject(projectDTO.getName())
 							.withIp(e.getEdgeInfo() != null ? e.getEdgeInfo().getPublicIp() : null));
-			return Stream.concat(edges, userResources)
+			return Stream.concat(edges, Stream.concat(odahuResources, userResources))
 					.collect(toList());
 		} else {
 			return userResources.collect(toList());
@@ -212,6 +219,14 @@
 				.withCloudProvider(userInstance.getCloudProvider());
 	}
 
+	private UserResourceInfo toUserResourceInfo(OdahuDTO odahuDTO) {
+		return new UserResourceInfo()
+				.withResourceType(ResourceEnum.ODAHU)
+				.withResourceName(odahuDTO.getName())
+				.withResourceStatus(odahuDTO.getStatus().toString())
+				.withProject(odahuDTO.getProject());
+	}
+
 	private void checkProjectResourceConditions(String project, String action) {
 		final List<UserInstanceDTO> userInstances = exploratoryDAO
 				.fetchProjectExploratoriesWhereStatusIn(project,
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
index ecfe6cf..953da3f 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
@@ -25,6 +25,7 @@
 import com.epam.dlab.backendapi.dao.EnvDAO;
 import com.epam.dlab.backendapi.dao.ExploratoryDAO;
 import com.epam.dlab.backendapi.domain.EndpointDTO;
+import com.epam.dlab.backendapi.domain.ProjectDTO;
 import com.epam.dlab.backendapi.domain.ProjectEndpointDTO;
 import com.epam.dlab.backendapi.resources.dto.HealthStatusPageDTO;
 import com.epam.dlab.backendapi.resources.dto.ProjectInfrastructureInfo;
@@ -40,14 +41,12 @@
 import com.google.inject.Inject;
 import com.jcabi.manifests.Manifests;
 import lombok.extern.slf4j.Slf4j;
-import org.bson.Document;
 
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
 
 @Slf4j
 public class InfrastructureInfoServiceImpl implements InfrastructureInfoService {
@@ -69,29 +68,14 @@
 
 
 	@Override
-	public List<ProjectInfrastructureInfo> getUserResources(String user) {
+	public List<ProjectInfrastructureInfo> getUserResources(UserInfo user) {
 		log.debug("Loading list of provisioned resources for user {}", user);
 		try {
-			Iterable<Document> documents = expDAO.findExploratory(user);
 			List<EndpointDTO> allEndpoints = endpointService.getEndpoints();
-			return StreamSupport.stream(documents.spliterator(),
-					false)
-					.collect(Collectors.groupingBy(d -> d.getString("project")))
-					.entrySet()
-					.stream()
-					.map(e -> {
-						List<ProjectEndpointDTO> endpoints = projectService.get(e.getKey()).getEndpoints();
-						List<EndpointDTO> endpointResult = allEndpoints.stream()
-								.filter(endpoint -> endpoints.stream()
-										.anyMatch(endpoint1 -> endpoint1.getName().equals(endpoint.getName())))
-								.collect(Collectors.toList());
-						final Map<String, Map<String, String>> projectEdges =
-								endpoints.stream()
-										.collect(Collectors.toMap(ProjectEndpointDTO::getName,
-												endpointDTO -> getSharedInfo(endpointDTO.getEdgeInfo())));
-						return new ProjectInfrastructureInfo(e.getKey(),
-								billingDAO.getBillingProjectQuoteUsed(e.getKey()), projectEdges, e.getValue(), endpointResult);
-					})
+			return projectService.getUserProjects(user, false).stream()
+					.map(p -> new ProjectInfrastructureInfo(p.getName(), billingDAO.getBillingProjectQuoteUsed(p.getName()),
+							getSharedInfo(p.getName()), expDAO.findExploratory(user.getName(), p.getName()), p.getOdahu(),
+							getEndpoints(allEndpoints, p)))
 					.collect(Collectors.toList());
 		} catch (Exception e) {
 			log.error("Could not load list of provisioned resources for user: {}", user, e);
@@ -128,6 +112,18 @@
 				.build();
 	}
 
+	private List<EndpointDTO> getEndpoints(List<EndpointDTO> allEndpoints, ProjectDTO projectDTO) {
+		return allEndpoints.stream().filter(endpoint -> projectDTO.getEndpoints().stream()
+				.anyMatch(endpoint1 -> endpoint1.getName().equals(endpoint.getName())))
+				.collect(Collectors.toList());
+	}
+
+	private Map<String, Map<String, String>> getSharedInfo(String name) {
+		return projectService.get(name).getEndpoints().stream()
+				.collect(Collectors.toMap(ProjectEndpointDTO::getName,
+						endpointDTO -> getSharedInfo(endpointDTO.getEdgeInfo())));
+	}
+
 	private Map<String, String> getSharedInfo(EdgeInfo edgeInfo) {
 		Map<String, String> shared = new HashMap<>();
 		if (Objects.isNull(edgeInfo)) {
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java
new file mode 100644
index 0000000..3f5671b
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.epam.dlab.backendapi.service.impl;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.annotation.BudgetLimited;
+import com.epam.dlab.backendapi.annotation.Project;
+import com.epam.dlab.backendapi.dao.OdahuDAO;
+import com.epam.dlab.backendapi.domain.EndpointDTO;
+import com.epam.dlab.backendapi.domain.OdahuCreateDTO;
+import com.epam.dlab.backendapi.domain.OdahuDTO;
+import com.epam.dlab.backendapi.domain.OdahuFieldsDTO;
+import com.epam.dlab.backendapi.domain.ProjectDTO;
+import com.epam.dlab.backendapi.domain.RequestId;
+import com.epam.dlab.backendapi.service.EndpointService;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.epam.dlab.backendapi.service.ProjectService;
+import com.epam.dlab.backendapi.util.RequestBuilder;
+import com.epam.dlab.constants.ServiceConsts;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.epam.dlab.dto.base.odahu.OdahuResult;
+import com.epam.dlab.exceptions.DlabException;
+import com.epam.dlab.exceptions.ResourceConflictException;
+import com.epam.dlab.rest.client.RESTService;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+@Slf4j
+public class OdahuServiceImpl implements OdahuService {
+
+    private static final String CREATE_ODAHU_API = "infrastructure/odahu";
+    private static final String START_ODAHU_API = "infrastructure/odahu/start";
+    private static final String STOP_ODAHU_API = "infrastructure/odahu/stop";
+    private static final String TERMINATE_ODAHU_API = "infrastructure/odahu/terminate";
+
+    private final ProjectService projectService;
+    private final EndpointService endpointService;
+    private final OdahuDAO odahuDAO;
+    private final RESTService provisioningService;
+    private final RequestBuilder requestBuilder;
+    private final RequestId requestId;
+
+    @Inject
+    public OdahuServiceImpl(ProjectService projectService, EndpointService endpointService, OdahuDAO odahuDAO,
+                            @Named(ServiceConsts.PROVISIONING_SERVICE_NAME) RESTService provisioningService,
+                            RequestBuilder requestBuilder, RequestId requestId) {
+        this.projectService = projectService;
+        this.endpointService = endpointService;
+        this.odahuDAO = odahuDAO;
+        this.provisioningService = provisioningService;
+        this.requestBuilder = requestBuilder;
+        this.requestId = requestId;
+    }
+
+
+    @Override
+    public List<OdahuDTO> findOdahu() {
+        return odahuDAO.findOdahuClusters();
+    }
+
+    @Override
+    public Optional<OdahuDTO> get(String project, String endpoint) {
+        return odahuDAO.getByProjectEndpoint(project, endpoint);
+    }
+
+    @BudgetLimited
+    @Override
+    public void create(@Project String project, OdahuCreateDTO odahuCreateDTO, UserInfo user) {
+        Optional<OdahuDTO> odahuDTO = odahuDAO.getByProjectEndpoint(odahuCreateDTO.getProject(), odahuCreateDTO.getEndpoint());
+        if (odahuDTO.isPresent()) {
+            throw new ResourceConflictException(String.format("Odahu cluster already exist in system for project %s " +
+                    "and endpoint %s", odahuCreateDTO.getProject(), odahuCreateDTO.getEndpoint()));
+        }
+        ProjectDTO projectDTO = projectService.get(project);
+        boolean isAdded = odahuDAO.create(new OdahuDTO(odahuCreateDTO.getName(), odahuCreateDTO.getProject(),
+                        odahuCreateDTO.getEndpoint(), UserInstanceStatus.CREATING, getTags(odahuCreateDTO)));
+
+        if (isAdded) {
+            String url = null;
+            EndpointDTO endpointDTO = endpointService.get(odahuCreateDTO.getEndpoint());
+            try {
+                url = endpointDTO.getUrl() + CREATE_ODAHU_API;
+                String uuid =
+                        provisioningService.post(url, user.getAccessToken(),
+                                requestBuilder.newOdahuCreate(user, odahuCreateDTO, projectDTO, endpointDTO), String.class);
+                requestId.put(user.getName(), uuid);
+            } catch (Exception e) {
+                log.error("Can not perform {} due to: {}, {}", url, e.getMessage(), e);
+                odahuDAO.updateStatus(odahuCreateDTO.getName(), odahuCreateDTO.getProject(), odahuCreateDTO.getEndpoint(),
+                        UserInstanceStatus.FAILED);
+            }
+        }
+    }
+
+    @BudgetLimited
+    @Override
+    public void start(String name, @Project String project, String endpoint, UserInfo user) {
+        odahuDAO.updateStatus(name, project, endpoint, UserInstanceStatus.STARTING);
+        actionOnCloud(user, START_ODAHU_API, name, project, endpoint);
+    }
+
+    @Override
+    public void stop(String name, String project, String endpoint, UserInfo user) {
+        odahuDAO.updateStatus(name, project, endpoint, UserInstanceStatus.STOPPING);
+        actionOnCloud(user, STOP_ODAHU_API, name, project, endpoint);
+    }
+
+    @Override
+    public void terminate(String name, String project, String endpoint, UserInfo user) {
+        Optional<OdahuDTO> odahuDTO = get(project, endpoint);
+        if (odahuDTO.isPresent() && UserInstanceStatus.RUNNING == odahuDTO.get().getStatus()) {
+            odahuDAO.updateStatus(name, project, endpoint, UserInstanceStatus.TERMINATING);
+            actionOnCloud(user, TERMINATE_ODAHU_API, name, project, endpoint);
+        } else {
+            log.error("Cannot terminate odahu cluster {}", odahuDTO);
+            throw new DlabException(String.format("Cannot terminate odahu cluster %s", odahuDTO));
+        }
+    }
+
+    @Override
+    public void updateStatus(OdahuResult result, UserInstanceStatus status) {
+        if (Objects.nonNull(result.getResourceUrls()) && !result.getResourceUrls().isEmpty()) {
+            odahuDAO.updateStatusAndUrls(result, status);
+        } else {
+            odahuDAO.updateStatus(result.getName(), result.getProjectName(), result.getEndpointName(), status);
+        }
+    }
+
+    @Override
+    public boolean inProgress(String project, String endpoint) {
+        return get(project, endpoint)
+                .filter(odahu -> Arrays.asList(UserInstanceStatus.CREATING, UserInstanceStatus.STARTING,
+                        UserInstanceStatus.STOPPING, UserInstanceStatus.TERMINATING).contains(odahu.getStatus()))
+                .isPresent();
+    }
+
+    private void actionOnCloud(UserInfo user, String uri, String name, String project, String endpoint) {
+        String url = null;
+        EndpointDTO endpointDTO = endpointService.get(endpoint);
+        ProjectDTO projectDTO = projectService.get(project);
+        try {
+            OdahuFieldsDTO fields = odahuDAO.getFields(name, project, endpoint);
+            url = endpointDTO.getUrl() + uri;
+            String uuid =
+                    provisioningService.post(url, user.getAccessToken(),
+                            requestBuilder.newOdahuAction(user, name, projectDTO, endpointDTO, fields), String.class);
+            requestId.put(user.getName(), uuid);
+        } catch (Exception e) {
+            log.error("Can not perform {} due to: {}, {}", url, e.getMessage(), e);
+            odahuDAO.updateStatus(name, project, project, UserInstanceStatus.FAILED);
+        }
+    }
+
+    private Map<String, String> getTags(OdahuCreateDTO odahuCreateDTO) {
+        Map<String, String> tags = new HashMap<>();
+        tags.put("custom_tag", odahuCreateDTO.getCustomTag());
+        tags.put("project_tag", odahuCreateDTO.getProject());
+        tags.put("endpoint_tag", odahuCreateDTO.getEndpoint());
+        return tags;
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/ProjectServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/ProjectServiceImpl.java
index 0e5a2d5..ca4effa 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/ProjectServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/ProjectServiceImpl.java
@@ -14,8 +14,8 @@
 import com.epam.dlab.backendapi.domain.UpdateProjectDTO;
 import com.epam.dlab.backendapi.service.EndpointService;
 import com.epam.dlab.backendapi.service.ExploratoryService;
+import com.epam.dlab.backendapi.service.OdahuService;
 import com.epam.dlab.backendapi.service.ProjectService;
-import com.epam.dlab.backendapi.service.SecurityService;
 import com.epam.dlab.backendapi.util.RequestBuilder;
 import com.epam.dlab.constants.ServiceConsts;
 import com.epam.dlab.dto.UserInstanceStatus;
@@ -55,14 +55,14 @@
 	private final RequestBuilder requestBuilder;
 	private final EndpointService endpointService;
 	private final ExploratoryDAO exploratoryDAO;
-	private final SecurityService securityService;
+	private final OdahuService odahuService;
 
 	@Inject
 	public ProjectServiceImpl(ProjectDAO projectDAO, ExploratoryService exploratoryService,
 							  UserGroupDao userGroupDao,
 							  @Named(ServiceConsts.PROVISIONING_SERVICE_NAME) RESTService provisioningService,
 							  RequestId requestId, RequestBuilder requestBuilder, EndpointService endpointService,
-							  ExploratoryDAO exploratoryDAO, SecurityService securityService) {
+							  ExploratoryDAO exploratoryDAO, OdahuService odahuService) {
 		this.projectDAO = projectDAO;
 		this.exploratoryService = exploratoryService;
 		this.userGroupDao = userGroupDao;
@@ -71,7 +71,7 @@
 		this.requestBuilder = requestBuilder;
 		this.endpointService = endpointService;
 		this.exploratoryDAO = exploratoryDAO;
-		this.securityService = securityService;
+		this.odahuService = odahuService;
 	}
 
 	@Override
@@ -124,10 +124,18 @@
 		projectActionOnCloud(userInfo, name, TERMINATE_PRJ_API, endpoint);
 		projectDAO.updateEdgeStatus(name, endpoint, UserInstanceStatus.TERMINATING);
 		exploratoryService.updateProjectExploratoryStatuses(name, endpoint, UserInstanceStatus.TERMINATING);
+		odahuService.get(name, endpoint)
+				.filter(o -> UserInstanceStatus.RUNNING == o.getStatus())
+				.ifPresent(odahu -> odahuService.terminate(odahu.getName(), name, endpoint, userInfo));
 	}
 
 	@Override
 	public void terminateEndpoint(UserInfo userInfo, List<String> endpoints, String name) {
+		List<ProjectEndpointDTO> projectEndpoint = get(name).getEndpoints().stream()
+				.filter(e -> endpoints.contains(e.getName()))
+				.collect(Collectors.toList());
+		checkProjectRelatedResourcesInProgress(name, projectEndpoint, TERMINATE_ACTION);
+
 		endpoints.forEach(endpoint -> terminateEndpoint(userInfo, endpoint, name));
 	}
 
@@ -262,12 +270,13 @@
 	}
 
 	private void checkProjectRelatedResourcesInProgress(String projectName, List<ProjectEndpointDTO> endpoints, String action) {
-        boolean edgeProgress = endpoints.stream().anyMatch(e ->
-                Arrays.asList(UserInstanceStatus.CREATING, UserInstanceStatus.STARTING, UserInstanceStatus.STOPPING,
-                        UserInstanceStatus.TERMINATING).contains(e.getStatus()));
+		boolean edgeAndOdahuProgress = endpoints.stream().anyMatch(e ->
+				Arrays.asList(UserInstanceStatus.CREATING, UserInstanceStatus.STARTING, UserInstanceStatus.STOPPING,
+						UserInstanceStatus.TERMINATING).contains(e.getStatus())
+						|| odahuService.inProgress(projectName, e.getName()));
 
 		List<String> endpointsName = endpoints.stream().map(ProjectEndpointDTO::getName).collect(Collectors.toList());
-		if (edgeProgress || !checkExploratoriesAndComputationalProgress(projectName, endpointsName)) {
+		if (edgeAndOdahuProgress || !checkExploratoriesAndComputationalProgress(projectName, endpointsName)) {
 			throw new ResourceConflictException((String.format("Can not %s environment because one of project " +
 					"resource is in processing stage", action)));
 		}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/util/RequestBuilder.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/util/RequestBuilder.java
index afe06cd..8e9f3d8 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/util/RequestBuilder.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/util/RequestBuilder.java
@@ -24,6 +24,8 @@
 import com.epam.dlab.backendapi.dao.SettingsDAO;
 import com.epam.dlab.backendapi.domain.EndpointDTO;
 import com.epam.dlab.backendapi.domain.ExploratoryLibCache;
+import com.epam.dlab.backendapi.domain.OdahuCreateDTO;
+import com.epam.dlab.backendapi.domain.OdahuFieldsDTO;
 import com.epam.dlab.backendapi.domain.ProjectDTO;
 import com.epam.dlab.backendapi.resources.dto.BackupFormDTO;
 import com.epam.dlab.backendapi.resources.dto.ComputationalCreateFormDTO;
@@ -70,6 +72,8 @@
 import com.epam.dlab.dto.gcp.computational.GcpComputationalTerminateDTO;
 import com.epam.dlab.dto.gcp.computational.SparkComputationalCreateGcp;
 import com.epam.dlab.dto.gcp.exploratory.ExploratoryCreateGcp;
+import com.epam.dlab.dto.odahu.ActionOdahuDTO;
+import com.epam.dlab.dto.odahu.CreateOdahuDTO;
 import com.epam.dlab.dto.project.ProjectActionDTO;
 import com.epam.dlab.dto.project.ProjectCreateDTO;
 import com.epam.dlab.exceptions.DlabException;
@@ -626,6 +630,33 @@
 				.withCloudSettings(cloudSettings(userInfo, endpointDTO.getCloudProvider()));
 	}
 
+	public CreateOdahuDTO newOdahuCreate(UserInfo user, OdahuCreateDTO odahuCreateDTO, ProjectDTO projectDTO, EndpointDTO endpointDTO) {
+		return CreateOdahuDTO.builder()
+				.name(odahuCreateDTO.getName())
+				.project(projectDTO.getName())
+				.endpoint(odahuCreateDTO.getEndpoint())
+				.key(projectDTO.getKey().replace("\n", ""))
+				.build()
+				.withEdgeUserName(getEdgeUserName(user, endpointDTO.getCloudProvider()))
+				.withCloudSettings(cloudSettings(user, endpointDTO.getCloudProvider()));
+	}
+
+	public ActionOdahuDTO newOdahuAction(UserInfo user, String name, ProjectDTO projectDTO, EndpointDTO endpointDTO,
+										 OdahuFieldsDTO odahuFields) {
+		return ActionOdahuDTO.builder()
+				.name(name)
+				.project(projectDTO.getName())
+				.key(projectDTO.getKey().replace("\n", ""))
+				.endpoint(endpointDTO.getName())
+				.grafanaAdmin(odahuFields.getGrafanaAdmin())
+				.grafanaPassword(odahuFields.getGrafanaPassword())
+				.oauthCookieSecret(odahuFields.getOauthCookieSecret())
+				.decryptToken(odahuFields.getDecryptToken())
+				.build()
+				.withEdgeUserName(getEdgeUserName(user, endpointDTO.getCloudProvider()))
+				.withCloudSettings(cloudSettings(user, endpointDTO.getCloudProvider()));
+	}
+
 	/**
 	 * Returns application name basing on docker image
 	 *
diff --git a/services/self-service/src/main/resources/mongo/aws/mongo_roles.json b/services/self-service/src/main/resources/mongo/aws/mongo_roles.json
index 9998d84..2adb28c 100644
--- a/services/self-service/src/main/resources/mongo/aws/mongo_roles.json
+++ b/services/self-service/src/main/resources/mongo/aws/mongo_roles.json
@@ -335,7 +335,8 @@
       "/api/settings",
       "/user/settings",
       "/api/project",
-      "/api/endpoint"
+      "/api/endpoint",
+      "/api/odahu"
     ],
     "groups": [
       "$anyuser"
diff --git a/services/self-service/src/main/resources/mongo/azure/mongo_roles.json b/services/self-service/src/main/resources/mongo/azure/mongo_roles.json
index 113a705..bbd7789 100644
--- a/services/self-service/src/main/resources/mongo/azure/mongo_roles.json
+++ b/services/self-service/src/main/resources/mongo/azure/mongo_roles.json
@@ -275,7 +275,8 @@
       "/api/settings",
       "/user/settings",
       "/api/project",
-      "/api/endpoint"
+      "/api/endpoint",
+      "/api/odahu"
     ],
     "groups": [
       "$anyuser"
diff --git a/services/self-service/src/main/resources/mongo/gcp/mongo_roles.json b/services/self-service/src/main/resources/mongo/gcp/mongo_roles.json
index 8098628..d55e85f 100644
--- a/services/self-service/src/main/resources/mongo/gcp/mongo_roles.json
+++ b/services/self-service/src/main/resources/mongo/gcp/mongo_roles.json
@@ -311,7 +311,8 @@
       "/api/settings",
       "/user/settings",
       "/api/project",
-      "/api/endpoint"
+      "/api/endpoint",
+      "/api/odahu"
     ],
     "groups": [
       "$anyuser"
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts b/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
index e535bda..8c19a94 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
@@ -23,10 +23,10 @@
 import { ManagenementModule } from './management';
 import { ProjectModule } from './project';
 import { RolesModule } from './roles';
+import {LegionDeploymentModule} from "./legion-deployment";
 
 @NgModule({
-  imports: [CommonModule, ManagenementModule, ProjectModule, RolesModule],
-  declarations: [],
-  exports: [ManagenementModule, ProjectModule, RolesModule]
+  imports: [CommonModule, ManagenementModule, ProjectModule, RolesModule, LegionDeploymentModule],
+  exports: [ManagenementModule, ProjectModule, RolesModule, LegionDeploymentModule],
 })
 export class AdministrationModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.html
new file mode 100644
index 0000000..ff04e55
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.html
@@ -0,0 +1,93 @@
+<div class="create-legion-cluster" id="dialog-box">
+  <header class="dialog-header">
+    <h4 class="modal-title">Create Odahu cluster</h4>
+    <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
+  </header>
+  <div class="dialog-content selection">
+    <div id="scrolling" class="content-box mat-reset scrolling-content">
+      <form [formGroup]="createLegionClusterForm" *ngIf="createLegionClusterForm" novalidate>
+        <div class="control-group">
+          <label class="label">Select project</label>
+          <div class="control selector-wrapper">
+            <mat-form-field>
+              <mat-label>Select project</mat-label>
+              <mat-select formControlName="project" panelClass="create-resources-dialog">
+                <mat-option *ngFor="let project of projects" [value]="project.name" (click)="setEndpoints(project)">
+                  {{ project.name }}</mat-option>
+                <mat-option *ngIf="!projects.length" class="multiple-select ml-10" disabled>
+                  No projects for creating Odahu clusters
+                </mat-option>
+              </mat-select>
+              <button class="caret">
+                <i class="material-icons">keyboard_arrow_down</i>
+              </button>
+            </mat-form-field>
+          </div>
+        </div>
+
+        <div class="control-group">
+          <label class="label">Select endpoint</label>
+          <div class="control selector-wrapper" [ngClass]="{ 'not-active' : !endpoints.length }">
+            <mat-form-field>
+              <mat-label>Select endpoint</mat-label>
+              <mat-select formControlName="endpoint" disableOptionCentering [disabled]="!endpoints.length"
+                          panelClass="create-resources-dialog">
+                <mat-option *ngFor="let endpoint of endpoints" [value]="endpoint">
+                  {{ endpoint }}
+                </mat-option>
+                <mat-option *ngIf="!endpoints.length" class="multiple-select ml-10" disabled>Endpoints list is empty</mat-option>
+              </mat-select>
+              <button class="caret">
+                <i class="material-icons">keyboard_arrow_down</i>
+              </button>
+            </mat-form-field>
+          </div>
+        </div>
+
+        <div class="control-group name-control">
+          <label class="label">Name</label>
+          <div class="control">
+            <input type="text" class="form-control" placeholder="Enter Name" formControlName="name">
+            <span class="error" *ngIf="!createLegionClusterForm.controls.name.valid && createLegionClusterForm.controls.name.dirty && !createLegionClusterForm.controls.name.hasError('duplication')">
+              Odahu cluster name can only contain letters and numbers
+            </span>
+            <span class="error" *ngIf="createLegionClusterForm.controls.name.hasError('duplication')">This Odahu cluster name already exists.</span>
+          </div>
+        </div>
+
+        <div class="control-group name-control">
+          <label class="label">Custom tag</label>
+          <div class="control">
+            <input type="text" class="form-control" placeholder="Enter custom tag" formControlName="custom_tag">
+          <span class="error"
+            *ngIf="!createLegionClusterForm.controls.custom_tag.valid && createLegionClusterForm.controls.custom_tag.dirty">
+            Custom tag can only contain letters, numbers, hyphens and '_' but can not end with special characters</span>
+          </div>
+        </div>
+
+<!--        <div class="control-group">-->
+<!--          <label class="label" [ngStyle]="!createLegionClusterForm.controls.useExistingClusterUrl.value && {'width': '100%' }">-->
+<!--            <input  type="checkbox" formControlName="useExistingClusterUrl"/> Use existing k8s cluster-->
+<!--          </label>-->
+<!--          <div class="control" *ngIf="createLegionClusterForm.controls.useExistingClusterUrl.value">-->
+<!--            <input type="text" class="form-control"-->
+<!--                                   formControlName="existingClusterUrl" placeholder="Enter ODAHU k8s cluster URL"/>-->
+<!--            <span class="error url-error">-->
+<!--                  <span *ngIf="!createLegionClusterForm.controls.existingClusterUrl.valid">Please enter valid cluster URL</span>-->
+<!--                </span>-->
+<!--          </div>-->
+<!--        </div>-->
+
+        <div class="text-center m-top-30">
+          <button mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">Cancel</button>
+          <button mat-raised-button type="button" class="butt butt-success action"
+                  [disabled]="!createLegionClusterForm.valid" (click)="createOdahuCluster(createLegionClusterForm.value)">
+            Create
+          </button>
+        </div>
+
+      </form>
+    </div>
+  </div>
+</div>
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.scss
new file mode 100644
index 0000000..324f0df
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.scss
@@ -0,0 +1,7 @@
+.create-legion-cluster{
+  .error{
+    position: absolute;
+    left: 0;
+    top: 36px;
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.ts
new file mode 100644
index 0000000..7aa941d
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/create-legion-cluster.component.ts
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Component, OnInit, Inject } from '@angular/core';
+import { FormGroup, FormBuilder, Validators } from '@angular/forms';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { ToastrService } from 'ngx-toastr';
+
+import { Project } from '../../project/project.component';
+import { ProjectService, LegionDeploymentService } from '../../../core/services';
+
+import { DICTIONARY } from '../../../../dictionary/global.dictionary';
+import {CheckUtils, PATTERNS} from '../../../core/util';
+
+
+@Component({
+  selector: 'create-legion-cluster',
+  templateUrl: 'create-legion-cluster.component.html',
+  styleUrls: ['./create-legion-cluster.component.scss']
+})
+
+export class CreateLegionClusterComponent implements OnInit {
+  readonly DICTIONARY = DICTIONARY;
+  public createLegionClusterForm: FormGroup;
+
+  projects: Project[] = [];
+  endpoints: Array<String> = [];
+
+  constructor(
+    @Inject(MAT_DIALOG_DATA) public data: any,
+    public toastr: ToastrService,
+    public dialogRef: MatDialogRef<CreateLegionClusterComponent>,
+    private _fb: FormBuilder,
+    private projectService: ProjectService,
+    private legionDeploymentService: LegionDeploymentService,
+  ) {
+  }
+
+  ngOnInit() {
+    this.getUserProjects();
+    this.initFormModel();
+  }
+
+  public getUserProjects(): void {
+    this.projectService.getUserProjectsList(true).subscribe((projects: any) => {
+      this.projects = projects.filter(project => {
+        return project.endpoints.length > project.odahu.filter(od => od.status !== 'FAILED' && od.status !== 'TERMINATED').length; }
+        );
+    });
+  }
+
+  public setEndpoints(project): void {
+    this.endpoints = project.endpoints
+      .filter(e => e.status === 'RUNNING' && !this.data.some(odahu => odahu.status !== 'FAILED'
+        && odahu.status !== 'TERMINATED'
+        && odahu.endpoint === e.name
+        && odahu.project === project.name)
+      )
+      .map(e => e.name);
+  }
+
+  private initFormModel(): void {
+    this.createLegionClusterForm = this._fb.group({
+      name: ['', [Validators.required, Validators.pattern(PATTERNS.namePattern), this.checkDuplication.bind(this)]],
+      project: ['', Validators.required],
+      endpoint: ['', [Validators.required]],
+      custom_tag: ['', [Validators.pattern(PATTERNS.namePattern)]]
+    });
+  }
+
+  private createOdahuCluster(value): void {
+    this.dialogRef.close();
+    this.legionDeploymentService.createOdahuNewCluster(value).subscribe(() => {
+      this.toastr.success('Odahu cluster creation is processing!', 'Success!');
+      }, error => this.toastr.error(error.message || 'Odahu cluster creation failed!', 'Oops!')
+    );
+  }
+
+  private checkDuplication(control) {
+    if (control && control.value) {
+      for (let index = 0; index < this.data.length; index++) {
+        if (CheckUtils.delimitersFiltering(control.value) === CheckUtils.delimitersFiltering(this.data[index].name))
+          return { duplication: true };
+      }
+    }
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/index.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/index.ts
new file mode 100644
index 0000000..a3adc9c
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/create-legion-claster/index.ts
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { MaterialModule } from '../../../shared/material.module';
+import { FormControlsModule } from '../../../shared/form-controls';
+import { KeysPipeModule, UnderscorelessPipeModule } from '../../../core/pipes';
+import {CreateLegionClusterComponent} from "./create-legion-cluster.component";
+
+export * from './create-legion-cluster.component';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    FormControlsModule,
+    MaterialModule,
+    KeysPipeModule,
+    UnderscorelessPipeModule
+  ],
+  declarations: [CreateLegionClusterComponent],
+  entryComponents: [CreateLegionClusterComponent],
+  exports: [CreateLegionClusterComponent]
+})
+export class CreateLegionClusterModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/index.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/index.ts
new file mode 100644
index 0000000..fa28e36
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/index.ts
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { MaterialModule } from '../../shared/material.module';
+import { FormControlsModule } from '../../shared/form-controls';
+import { UnderscorelessPipeModule } from '../../core/pipes/underscoreless-pipe';
+
+import {BubbleModule} from "../../shared/bubble";
+import {LegionDeploymentComponent} from "./legion-deployment.component";
+import {LegionDeploymentDataService} from "./legion-deployment-data.service";
+import {LegionListComponent} from "./legion-list/legion-list.component";
+import {CreateLegionClusterModule} from "./create-legion-claster";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MaterialModule,
+    FormControlsModule,
+    UnderscorelessPipeModule,
+    BubbleModule,
+    CreateLegionClusterModule
+  ],
+  declarations: [LegionDeploymentComponent, LegionListComponent],
+  entryComponents: [],
+  providers: [LegionDeploymentDataService],
+  exports: [LegionDeploymentComponent]
+})
+export class LegionDeploymentModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment-data.service.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment-data.service.ts
new file mode 100644
index 0000000..83dfeaf
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment-data.service.ts
@@ -0,0 +1,27 @@
+import { Injectable } from '@angular/core';
+import {BehaviorSubject, Observable} from 'rxjs';
+import {LegionDeploymentService} from '../../core/services';
+
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class LegionDeploymentDataService {
+  _legionClasters = new BehaviorSubject<any>(null);
+
+  constructor(private legionDeploymentService: LegionDeploymentService) {
+    this.getClastersList();
+  }
+
+  public updateClasters(): void {
+    this.getClastersList();
+  }
+
+  private getClastersList(): void {
+   this.legionDeploymentService.getOduhuClustersList().subscribe(
+      (response: any ) => {
+        return this._legionClasters.next(response);
+      });
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.html
new file mode 100644
index 0000000..1368180
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.html
@@ -0,0 +1,42 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+
+<div class="base-retreat">
+  <div class="sub-nav">
+    <div>
+      <button mat-raised-button class="butt butt-create" (click)="createLegionCluster()">
+        <i class="material-icons">add</i>Create new
+      </button>
+    </div>
+    <div>
+      <button mat-raised-button class="butt" (click)="refreshGrid()">
+        <i class="material-icons">autorenew</i>Refresh
+      </button>
+    </div>
+  </div>
+
+  <mat-divider></mat-divider>
+
+  <div>
+    <legion-list>
+    </legion-list>
+  </div>
+</div>
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.scss
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts
new file mode 100644
index 0000000..263debf
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts
@@ -0,0 +1,59 @@
+import { Component, OnInit } from '@angular/core';
+import {LegionDeploymentDataService} from './legion-deployment-data.service';
+import {Subscription} from 'rxjs';
+import {MatDialog} from '@angular/material/dialog';
+import {ToastrService} from 'ngx-toastr';
+import {CreateLegionClusterComponent} from './create-legion-claster/create-legion-cluster.component';
+import {HealthStatusService, LegionDeploymentService} from '../../core/services';
+
+export interface OdahuCluster {
+  name: string;
+  project: string;
+  endpoint: string;
+}
+
+@Component({
+  selector: 'dlab-legion-deployment',
+  templateUrl: './legion-deployment.component.html',
+  styleUrls: ['./legion-deployment.component.scss']
+})
+export class LegionDeploymentComponent implements OnInit {
+
+  private legionClastersList: any[];
+  private subscriptions: Subscription = new Subscription();
+  private healthStatus;
+
+  constructor(
+    private legionDeploymentDataService: LegionDeploymentDataService,
+    private dialog: MatDialog,
+    public toastr: ToastrService,
+    public legionDeploymentService: LegionDeploymentService,
+    private healthStatusService: HealthStatusService,
+  ) { }
+
+  ngOnInit() {
+    this.getEnvironmentHealthStatus();
+    this.subscriptions.add(this.legionDeploymentDataService._legionClasters.subscribe(
+      (value) => {
+        if (value) this.legionClastersList = value;
+      }));
+    this.refreshGrid();
+  }
+
+  public createLegionCluster(): void {
+    this.dialog.open(CreateLegionClusterComponent, {  data: this.legionClastersList, panelClass: 'modal-lg' })
+      .afterClosed().subscribe((result) => {
+      result && this.getEnvironmentHealthStatus();
+      this.refreshGrid();
+      });
+  }
+
+  private getEnvironmentHealthStatus(): void {
+    this.healthStatusService.getEnvironmentHealthStatus()
+      .subscribe((result: any) => this.healthStatus = result);
+  }
+
+  public refreshGrid(): void {
+    this.legionDeploymentDataService.updateClasters();
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.html
new file mode 100644
index 0000000..a670b2f
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.html
@@ -0,0 +1,94 @@
+
+<table mat-table [dataSource]="dataSource" class="legion-clasters-table mat-elevation-z6 selection">
+  <ng-container matColumnDef="project">
+    <th mat-header-cell *matHeaderCellDef class="project"> Project name </th>
+    <td mat-cell *matCellDef="let element" class="project">
+      <span *ngIf="element && element.project">{{element.project}}</span>
+    </td>
+    <td mat-footer-cell *matFooterCellDef></td>
+  </ng-container>
+
+  <ng-container matColumnDef="endpoint-url">
+    <th mat-header-cell *matHeaderCellDef class="endpoint"> Endpoint </th>
+    <td mat-cell *matCellDef="let element" class="endpoint">
+      <span *ngIf="element && element.endpoint" matTooltip="{{element.endpoint}}" [matTooltipPosition]="'above'">
+        {{element.endpoint}}
+      </span>
+    </td>
+    <td mat-footer-cell *matFooterCellDef></td>
+  </ng-container>
+
+  <ng-container matColumnDef="legion-name">
+    <th mat-header-cell *matHeaderCellDef class="legion-name"> Odahu cluster name </th>
+    <td mat-cell *matCellDef="let element" class="legion-name">
+      <span *ngIf="element && element.name">{{element.name}}</span>
+    </td>
+    <td mat-footer-cell *matFooterCellDef></td>
+  </ng-container>
+
+  <ng-container matColumnDef="legion-status">
+    <th mat-header-cell *matHeaderCellDef class="legion-status"> Odahu cluster status </th>
+    <td mat-cell *matCellDef="let element" class="legion-status">
+      <span *ngIf="element && element.name" [ngClass]="element.status.toLowerCase()">{{ element.status | titlecase}}</span>
+    </td>
+    <td mat-footer-cell *matFooterCellDef></td>
+  </ng-container>
+
+  <ng-container matColumnDef="actions">
+    <th mat-header-cell *matHeaderCellDef class="legion-actions"></th>
+    <td mat-cell *matCellDef="let element" class="settings">
+      <span *ngIf="element && element.name" #settings (click)="actions.toggle($event, settings)" class="actions" [ngClass]="{'disabled': element.status !== 'RUNNING' && element.status !== 'STOPPED'}"></span>
+      <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
+        <ul class="list-unstyled">
+          <div class="active-items"></div>
+          <li class="project-seting-item" *ngIf="element.status === 'STOPPED'" (click)="odahuAction(element, 'start')">
+            <i class="material-icons">play_circle_outline</i>
+            <a class="action">
+              Start
+            </a>
+          </li>
+          <li class="project-seting-item" *ngIf="element.status === 'RUNNING'" (click)="odahuAction(element, 'stop')">
+            <i class="material-icons">pause_circle_outline</i>
+            <a class="action" >
+              Stop
+            </a>
+          </li>
+          <li class="project-seting-item" [ngClass]="{'disabled' : element.status === 'STOPPED'}" *ngIf="element.status !== 'TERMINATED' && element.status !== 'TERMINATING'" (click)="odahuAction(element, 'terminate')">
+            <i class="material-icons">phonelink_off</i>
+            <a class="action">
+              Terminate
+            </a>
+          </li>
+          <!--<li class="project-seting-item">-->
+            <!--<i class="material-icons">arrow_downward</i>-->
+            <!--<a>-->
+              <!--Scale-down-->
+            <!--</a>-->
+          <!--</li>-->
+          <!--<li class="project-seting-item">-->
+            <!--<i class="material-icons">arrow_upward</i>-->
+            <!--<a  class="action">-->
+              <!--Scale-up-->
+            <!--</a>-->
+          <!--</li>-->
+        </ul>
+      </bubble-up>
+    </td>
+    <td mat-footer-cell *matFooterCellDef></td>
+  </ng-container>
+
+    <ng-container matColumnDef="placeholder">
+      <td mat-footer-cell *matFooterCellDef
+          [colSpan]="displayedColumns.length"
+          class="info">
+          <span>No Odahu clusters</span>
+      </td>
+      <td mat-footer-cell *matFooterCellDef></td>
+    </ng-container>
+
+  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
+  <tr [hidden]="legionClustersList && legionClustersList.length" mat-footer-row *matFooterRowDef="['placeholder']"></tr>
+</table>
+
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.scss
new file mode 100644
index 0000000..21fc43e
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.scss
@@ -0,0 +1,77 @@
+.legion-clasters-table {
+  width: 100%;
+
+  .project, .endpoint, .legion-name, .legion-status{
+    width: 23%;
+  }
+
+  td.settings {
+    position: relative;
+    vertical-align: middle !important;
+    text-align: right;
+  }
+
+  .list-menu {
+    min-width: 190px;
+  }
+
+  .material-icons {
+    font-size: 18px;
+    padding-top: 1px;
+  }
+
+  td.settings .actions {
+    background-image: url(../../../../assets/svg/settings_icon.svg);
+    width: 16px;
+    height: 16px;
+    display: inline-block;
+    text-align: center;
+    cursor: pointer;
+    &.disabled {
+      opacity: 0.4;
+      cursor: not-allowed;
+      pointer-events: none;
+    }
+  }
+
+  td {
+    &.info.mat-footer-cell{
+      text-align: center;
+      padding: 40px;
+    }
+    .settings {
+      position: relative;
+      vertical-align: middle !important;
+      text-align: right;
+
+      .actions {
+        background-image: url(../../../../assets/svg/settings_icon.svg);
+        width: 16px;
+        height: 16px;
+        display: inline-block;
+        text-align: center;
+        cursor: pointer;
+      }
+    }
+
+    .project-seting-item {
+      display: flex;
+      padding: 10px;
+      align-items: center;
+      border-bottom: 1px solid #edf1f5;
+      cursor: pointer;
+      color: #577289;
+
+      &:hover {
+        color: #36afd5;
+        transition: all .45s ease-in-out;
+      }
+
+      a {
+        padding-left: 5px;
+      }
+    }
+  }
+}
+
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts
new file mode 100644
index 0000000..7e7913e
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts
@@ -0,0 +1,48 @@
+import { Component, OnInit } from '@angular/core';
+import {Subscription} from 'rxjs';
+import {LegionDeploymentDataService} from '../legion-deployment-data.service';
+import { MatTableDataSource } from '@angular/material/table';
+import {LegionDeploymentService} from '../../../core/services';
+import {ToastrService} from 'ngx-toastr';
+import {MatDialog} from '@angular/material/dialog';
+import {OdahuActionDialogComponent} from '../../../shared/modal-dialog/odahu-action-dialog';
+
+@Component({
+  selector: 'legion-list',
+  templateUrl: './legion-list.component.html',
+  styleUrls: ['./legion-list.component.scss']
+})
+export class LegionListComponent implements OnInit {
+  private legionClustersList: any[];
+  private subscriptions: Subscription = new Subscription();
+  public dataSource: MatTableDataSource<any>;
+  displayedColumns: string[] = [ 'legion-name', 'project', 'endpoint-url', 'legion-status', 'actions'];
+
+  constructor(
+    private legionDeploymentDataService: LegionDeploymentDataService,
+    private legionDeploymentService: LegionDeploymentService,
+    public toastr: ToastrService,
+    public dialog: MatDialog
+  ) { }
+
+  ngOnInit() {
+    this.subscriptions.add(this.legionDeploymentDataService._legionClasters.subscribe(
+      (value) => {
+        if (value) {
+          this.legionClustersList = value;
+          this.dataSource = new MatTableDataSource(value);
+        }
+      }));
+  }
+
+  private odahuAction(element: any, action: string) {
+    this.dialog.open(OdahuActionDialogComponent, {data: {type: action, item: element}, panelClass: 'modal-sm'})
+      .afterClosed().subscribe(result => {
+        result && this.legionDeploymentService.odahuAction(element,  action).subscribe(v =>
+          this.legionDeploymentDataService.updateClasters(),
+          error => this.toastr.error(`Odahu cluster ${action} failed!`, 'Oops!')
+        ) ;
+      }, error => this.toastr.error(error.message || `Odahu cluster ${action} failed!`, 'Oops!')
+    );
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
index 631e7ae..1945cd0 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
@@ -121,12 +121,12 @@
       </td>
     </ng-container>
 
-    <ng-container matColumnDef="actions">
+    <ng-container matColumnDef="actions"> 
       <th mat-header-cell *matHeaderCellDef class="actions">
         <span class="label"> Actions </span>
       </th>
-      <td mat-cell *matCellDef="let element" class="settings actions-col">
-        <span #settings class="actions" (click)="actions.toggle($event, settings)" *ngIf="element.type !== 'edge node'"
+      <td mat-cell *matCellDef="let element" class=" settings actions-col">
+        <span #settings class="actions" (click)="actions.toggle($event, settings)" *ngIf="element.type !== 'edge node' && element.type !== 'odahu'"
           [ngClass]="{
             'disabled' : isActiveResources(element),
             'disabled' : element.status !== 'running' && element.status !== 'stopped' && element.status !== 'stopping' && element.status !== 'failed' }"></span>
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/project/index.ts b/services/self-service/src/main/resources/webapp/src/app/administration/project/index.ts
index 609ea59..8203e55 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/project/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/project/index.ts
@@ -30,7 +30,7 @@
 
 import { ProjectComponent, EditProjectComponent } from './project.component';
 import { ProjectDataService } from './project-data.service';
-import {BubbleModule} from "../../shared/bubble";
+import {BubbleModule} from '../../shared/bubble';
 
 @NgModule({
   imports: [
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/project/project-form/project-form.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/project/project-form/project-form.component.ts
index 683e7d7..f38ab06 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/project/project-form/project-form.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/project/project-form/project-form.component.ts
@@ -29,7 +29,7 @@
 import { Project } from '../project.component';
 import { DICTIONARY } from '../../../../dictionary/global.dictionary';
 
-export interface GenerateKey { privateKey: string, publicKey: string };
+export interface GenerateKey { privateKey: string, publicKey: string }
 
 @Component({
   selector: 'project-form',
@@ -139,7 +139,7 @@
   }
 
   public selectOptions(list, key, select?) {
-    let filter = key === 'endpoints' ? list.map(el => el.name) : list;
+    const filter = key === 'endpoints' ? list.map(el => el.name) : list;
     this.projectForm.controls[key].setValue(select ? filter : []);
   }
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/project/project-list/project-list.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/project/project-list/project-list.component.ts
index 9238b7f..e917f04 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/project/project-list/project-list.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/project/project-list/project-list.component.ts
@@ -28,7 +28,7 @@
 import { CheckUtils } from '../../../core/util';
 import {ProgressBarService} from '../../../core/services/progress-bar.service';
 import {EdgeActionDialogComponent} from '../../../shared/modal-dialog/edge-action-dialog';
-import {EndpointService} from '../../../core/services';
+import {EndpointService} from "../../../core/services";
 
 
 @Component({
@@ -50,6 +50,7 @@
     public toastr: ToastrService,
     private projectDataService: ProjectDataService,
     private progressBarService: ProgressBarService,
+    private endpointService: EndpointService,
     @Inject(MAT_DIALOG_DATA) public data: any,
     public dialogRef: MatDialogRef<ProjectListComponent>,
     public dialog: MatDialog,
@@ -69,6 +70,18 @@
     setTimeout(() => {this.progressBarService.startProgressBar(); } , 0);
     this.subscriptions.add(this.projectDataService._projects.subscribe((value: Project[]) => {
       this.projectList = value;
+      this.endpointService.getEndpointsData().subscribe((endpoints: any) => {
+        if (this.projectList) {
+          this.projectList.forEach(project => project.endpoints.forEach(endpoint => {
+            const filtredEndpoints =  endpoints.filter(v => v.name === endpoint.name);
+            if (filtredEndpoints.length) {
+              endpoint.endpointStatus = endpoints.filter(v => v.name === endpoint.name)[0].status;
+            } else {
+              endpoint.endpointStatus = 'N/A';
+            }
+          }));
+        }
+       });
       if (value) this.dataSource = new MatTableDataSource(value);
       this.progressBarService.stopProgressBar();
     }, () => this.progressBarService.stopProgressBar()));
diff --git a/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts b/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
index 3c8ae3f..9afd6a5 100644
--- a/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
@@ -34,6 +34,7 @@
 import { SwaggerComponent } from './swagger/swagger.component';
 
 import { AuthorizationGuard, CheckParamsGuard, CloudProviderGuard, AdminGuard } from './core/services';
+import {LegionDeploymentComponent} from "./administration/legion-deployment/legion-deployment.component";
 
 const routes: Routes = [{
   path: 'login',
@@ -59,6 +60,11 @@
       path: 'projects',
       component: ProjectComponent,
       canActivate: [AuthorizationGuard, AdminGuard],
+    },
+     {
+      path: 'odahu',
+      component: LegionDeploymentComponent,
+      canActivate: [AuthorizationGuard,],
     }, {
       path: 'roles',
       component: RolesComponent,
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts b/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts
index f815c70..411849c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts
@@ -40,6 +40,7 @@
 import { DataengineConfigurationService } from './services/dataengineConfiguration.service';
 import { StorageService } from './services/storage.service';
 import { ProjectService } from './services/project.service';
+import { LegionDeploymentService } from './services/legion-deployment.service';
 import { EndpointService } from './services/endpoint.service';
 import { UserAccessKeyService } from './services/userAccessKey.service';
 
@@ -82,6 +83,7 @@
         StorageService,
         ProjectService,
         EndpointService,
+        LegionDeploymentService,
         UserAccessKeyService,
 
         { provide: MatDialogRef, useValue: {} },
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
index 121188e..e6583f6 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
@@ -75,6 +75,7 @@
   private static readonly DOWNLOAD_REPORT = 'download_report';
   private static readonly SETTINGS = 'settings';
   private static readonly PROJECT = 'project';
+  private static readonly ODAHU = 'odahu';
   private static readonly ENDPOINT = 'endpoint';
   private static readonly ENDPOINT_CONNECTION = 'endpoint_connection';
 
@@ -609,6 +610,24 @@
       null);
   }
 
+  public createOdahuCluster(data): Observable<any> {
+    return this.buildRequest(HTTPMethod.POST,
+      this.requestRegistry.Item(ApplicationServiceFacade.ODAHU),
+      data);
+  }
+
+   public getOdahuList(): Observable<any> {
+      return this.buildRequest(HTTPMethod.GET,
+        this.requestRegistry.Item(ApplicationServiceFacade.ODAHU),
+        null);
+    }
+
+  public odahuStartStop(data, params): Observable<any> {
+    return this.buildRequest(HTTPMethod.POST,
+      this.requestRegistry.Item(ApplicationServiceFacade.ODAHU) + `/${params}`,
+      data);
+  }
+
   private setupRegistry(): void {
     this.requestRegistry = new Dictionary<string>();
 
@@ -688,6 +707,9 @@
     this.requestRegistry.Add(ApplicationServiceFacade.PROJECT, '/api/project');
     this.requestRegistry.Add(ApplicationServiceFacade.ENDPOINT, '/api/endpoint');
     this.requestRegistry.Add(ApplicationServiceFacade.ENDPOINT_CONNECTION, '/api/endpoint/url/');
+
+    // odahu
+    this.requestRegistry.Add(ApplicationServiceFacade.ODAHU, '/api/odahu');
   }
 
   private buildRequest(method: HTTPMethod, url_path: string, body: any, opt?) {
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts
index 0068a29..395c667 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts
@@ -38,3 +38,4 @@
 export * from './storage.service';
 export * from './project.service';
 export * from './endpoint.service';
+export * from './legion-deployment.service';
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/legion-deployment.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/legion-deployment.service.ts
new file mode 100644
index 0000000..1b0872e
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/legion-deployment.service.ts
@@ -0,0 +1,35 @@
+import { Injectable } from '@angular/core';
+import {Observable} from 'rxjs';
+import { map, catchError } from 'rxjs/operators';
+import { ApplicationServiceFacade } from './applicationServiceFacade.service';
+import { ErrorUtils } from '../util';
+
+@Injectable()
+
+export class LegionDeploymentService {
+  constructor(private applicationServiceFacade: ApplicationServiceFacade) { }
+
+  public createOdahuNewCluster(data): Observable<{}> {
+    return this.applicationServiceFacade
+      .createOdahuCluster(data)
+      .pipe(
+        map(response => response),
+        catchError(ErrorUtils.handleServiceError));
+  }
+
+  public getOduhuClustersList(): Observable<{}> {
+    return this.applicationServiceFacade
+      .getOdahuList()
+      .pipe(
+        map(response => response),
+        catchError(ErrorUtils.handleServiceError));
+  }
+
+  public odahuAction(data, action) {
+    return this.applicationServiceFacade
+        .odahuStartStop(data, action)
+        .pipe(
+            map(response => response),
+            catchError(ErrorUtils.handleServiceError));
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.ts
index 1856688..29914e5 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reporting/reporting-grid/reporting-grid.component.ts
@@ -81,7 +81,7 @@
   resetFiltering(): void {
     this.filteredReportData.defaultConfigurations();
 
-    this.filter.nativeElement.value = ''
+    this.filter.nativeElement.value = '';
     this.filterReport.emit(this.filteredReportData);
     this.resetRangePicker.emit(true);
   }
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts b/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts
index 3433383..958eb23 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts
@@ -96,6 +96,11 @@
           if (this.PROVIDER) {
             this.rebuildBillingReport();
           }
+        } else {
+          this.PROVIDER = 'azure';
+          if (this.PROVIDER) {
+            this.rebuildBillingReport();
+          }
         }
       }, e => {
         this.PROVIDER = 'azure';
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.scss
index a4b825e..c47cc43 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.scss
@@ -110,7 +110,7 @@
    }
  }
  @media screen and (max-width: 1520px) {
-   .resources,
+  .resources,
    managment {
      .source {
        .resource-wrap {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
index 4977b22..de503f3 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
@@ -18,11 +18,24 @@
   -->
 
 <div class="detail-dialog" id="dialog-box">
-  <header class="dialog-header header-white">
+  <header class="dialog-header header-white" >
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </header>
   <div class="dialog-content">
-    <div *ngIf="data">
+    <div *ngIf="data.legion">
+      <table class="detail-header">
+        <tr>
+          <td>{{legion.name}}</td>
+          <td>
+            <span class="status" ngClass="{{legion.status || ''}}">
+              {{legion.status}}
+            </span>
+          </td>
+          <td>{{legion.shape}}</td>
+        </tr>
+      </table>
+    </div>
+    <div *ngIf="data.notebook">
       <table class="detail-header">
         <tr>
           <td>{{notebook.template_name}}</td>
@@ -129,5 +142,26 @@
         </div>
       </div>
     </div>
+
+    <div class="legion-info" *ngIf="data.legion">
+      <div class="content-box">
+        <div class="detail-info" *ngIf="!legion.error_message">
+        <div  class="links_block">
+          <div *ngFor="let url of legion.url" class="odahu-links">
+            <div class="odahu-link">
+              <span class="description">{{url.description }}: &nbsp;</span>
+              <a class="ellipsis" matTooltip="{{ url.url}}" matTooltipPosition="above" href="{{ url.url}}"
+                 target="_blank">{{ url.url}}
+              </a>
+            </div>
+            <div class="grafana" *ngIf="url.description === 'Grafana'">
+              <div><span>Gafana user:&nbsp;</span><span class="creds">{{legion.bucket_name}}</span></div>
+              <div><span>Gafana password:&nbsp;</span><span class="creds">{{legion.shared_bucket_name}}</span></div>
+            </div>
+          </div>
+        </div>
+    </div>
+      </div>
+    </div>
   </div>
 </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss
index 2849ce0..f4d17ef 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss
@@ -26,17 +26,42 @@
 }
 
 .links_block {
+  max-height: 300px;
+  overflow-y: auto;
 
-  >p {
-    display: flex;
+  .odahu-links:not(:last-child){
+    margin-bottom: 20px;
+  }
 
-    .description {
-      white-space: nowrap;
-      padding-left: 7px;
-      font-weight: 600;
-      color: $blue-grey-color;
+  .grafana{
+    white-space: nowrap;
+    padding-left: 30px;
+    color: $blue-grey-color;
+
+    div{
+      display: flex;
+
+      span{
+        font-size: 13px;
+        font-weight: 500;
+
+        &.creds{
+          font-weight: 600;
+        }
+      }
     }
   }
+
+  >p, .odahu-link {
+     display: flex;
+
+    .description {
+       white-space: nowrap;
+       padding-left: 7px;
+       font-weight: 600;
+       color: $blue-grey-color;
+     }
+   }
 }
 
 .checkbox-group {
@@ -73,7 +98,6 @@
   }
 
   span {
-
     .danger_color {
       position: absolute;
       bottom: -16px;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
index db097f3..c2f607a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
@@ -35,7 +35,7 @@
 
 export class DetailDialogComponent implements OnInit {
   readonly DICTIONARY = DICTIONARY;
-  readonly PROVIDER = this.data.cloud_provider;
+  public PROVIDER: string;
   notebook: any;
   upTimeInHours: number;
   upTimeSince: string = '';
@@ -43,6 +43,7 @@
   config: Array<{}> = [];
 
   public configurationForm: FormGroup;
+  private legion: any;
 
   @ViewChild('configurationNode', { static: false }) configuration;
 
@@ -53,13 +54,19 @@
     public dialogRef: MatDialogRef<DetailDialogComponent>,
     public toastr: ToastrService
   ) {
-    this.notebook = data;
+    if(data.notebook) {
+      this.notebook = data.notebook;
+      this.PROVIDER = this.data.notebook.cloud_provider;
+    }
+
+    if(data.legion) {
+      this.legion = data.legion;
+      this.PROVIDER = this.data.legion.cloud_provider || 'azure';
+    }
   }
 
   ngOnInit() {
-    this.notebook;
-
-    if (this.notebook) {
+   if (this.notebook) {
       this.tooltip = false;
 
       this.upTimeInHours = (this.notebook.time) ? DateUtils.diffBetweenDatesInHours(this.notebook.time) : 0;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html
index 965f9d8..ea0c65f 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html
@@ -105,8 +105,9 @@
         [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'" sticky>
 
         <tr *ngFor="let element of element.exploratory; let i = index" class="element-row mat-row">
-          <td class="name-col" (click)="printDetailEnvironmentModal(element)">
-            <span matTooltip="{{ element.name }}" matTooltipPosition="above">{{ element.name }}</span>
+          <td class="name-col">
+            <span *ngIf="element.shape !== 'odahu cluster'" matTooltip="{{ element.name }}" matTooltipPosition="above" (click)="printDetailEnvironmentModal(element)">{{ element.name }}</span>
+            <span *ngIf="element.shape === 'odahu cluster'" matTooltip="{{ element.name }}" matTooltipPosition="above" (click)="printDetailLegionModal(element)">{{ element.name }}</span>
           </td>
           <td class="status-col status" ngClass="{{ element.status.toLowerCase() || ''}}">
             {{element.status | underscoreless }}
@@ -122,7 +123,7 @@
                 matTooltipPosition="above">
                 {{ element.tags.custom_tag }}
               </mat-chip>
-              <mat-chip matTooltip="User tag: {{ element.tags.user_tag }}" matTooltipPosition="above">
+              <mat-chip *ngIf="element.tags.user_tag" matTooltip="User tag: {{ element.tags.user_tag }}" matTooltipPosition="above">
                 {{ element.tags.user_tag }}
               </mat-chip>
               <mat-chip matTooltip="Project tag: {{ element.tags.project_tag }}" matTooltipPosition="above">
@@ -132,14 +133,14 @@
           </td>
 
           <td class="resources-col">
-            <computational-resources-list [resources]="element.resources" [environment]="element"
+            <computational-resources-list *ngIf="element.shape !== 'odahu cluster'" [resources]="element.resources" [environment]="element"
               (buildGrid)="buildGrid($event)">
             </computational-resources-list>
           </td>
           <td *ngIf="healthStatus?.billingEnabled" class="cost-col">
             <span class="total_cost">{{ element.cost || 'N/A' }} {{ element.currency_code || '' }}</span>
             <span (click)="element.billing && printCostDetails(element)" class="currency_details"
-              [ngClass]="{ 'not-allowed' : !element.billing }">
+              [ngClass]="{ 'not-allowed' : !element.billing || element.shape === 'odahu cluster' }">
               <i class="material-icons">help_outline</i>
             </span>
           </td>
@@ -148,11 +149,12 @@
             <span #settings (click)="actions.toggle($event, settings)" class="actions"
               [ngClass]="{ 'disabled': element.status.toLowerCase() === 'creating'
               || (element.image === 'docker.dlab-superset' && element.status !== 'running' && element.status !== 'stopped')
-              || (element.image === 'docker.dlab-jupyterlab' && element.status !== 'running' && element.status !== 'stopped') }">
+              || (element.image === 'docker.dlab-jupyterlab' && element.status !== 'running' && element.status !== 'stopped')
+              }">
             </span>
 
             <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
-              <ul class="list-unstyled">
+              <ul class="list-unstyled" *ngIf="element.shape !== 'odahu cluster'">
                 <div class="active-items" *ngIf="element.status.toLowerCase() !== 'failed'
                 && element.status !== 'terminating'
                 && element.status !== 'terminated'
@@ -214,6 +216,29 @@
                   </a>
                 </li>
               </ul>
+              <ul class="list-unstyled" *ngIf="element.shape === 'odahu cluster'">
+                <li class="project-seting-item" *ngIf="element.status === 'stopped'" (click)="odahuAction(element, 'start')">
+                  <i class="material-icons">play_circle_outline</i>
+                  <a class="action">
+                    Start
+                  </a>
+                </li>
+                <li class="project-seting-item" *ngIf="element.status === 'running'" (click)="odahuAction(element, 'stop')">
+                  <i class="material-icons">pause_circle_outline</i>
+                  <a class="action" >
+                    Stop
+                  </a>
+                </li>
+                <li class="project-seting-item"
+                    [ngClass]="{'disabled': element.status === 'stopped' || element.status.toLowerCase() === 'stopping' || element.status.toLowerCase() === 'starting'}"
+                    *ngIf="element.status === element.status !== 'terminated' && element.status !== 'terminating'" (click)="odahuAction(element, 'terminate')"
+                >
+                  <i class="material-icons">phonelink_off</i>
+                  <a class="action">
+                    Terminate
+                  </a>
+                </li>
+              </ul>
             </bubble-up>
           </td>
         </tr>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss
index e09ff3e..1657a56 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss
@@ -392,6 +392,11 @@
   text-align: center;
 }
 
+.legion-icon{
+  vertical-align: middle;
+  margin-left: 10px;
+}
+
 .content-row{
   background-clip: padding-box;
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
index 3052d84..861981e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
@@ -23,7 +23,7 @@
 import { ToastrService } from 'ngx-toastr';
 import { MatDialog } from '@angular/material/dialog';
 
-import { UserResourceService } from '../../core/services';
+import {LegionDeploymentService, UserResourceService} from '../../core/services';
 
 import { ExploratoryModel, Exploratory } from './resources-grid.model';
 import { FilterConfigurationModel } from './filter-configuration.model';
@@ -40,6 +40,7 @@
 
 import { DICTIONARY } from '../../../dictionary/global.dictionary';
 import {ProgressBarService} from '../../core/services/progress-bar.service';
+import {OdahuActionDialogComponent} from '../../shared/modal-dialog/odahu-action-dialog';
 import {ComputationModel} from '../computational/computational-resource.model';
 import {NotebookModel} from '../exploratory/notebook.model';
 
@@ -95,6 +96,7 @@
     private userResourceService: UserResourceService,
     private dialog: MatDialog,
     private progressBarService: ProgressBarService,
+    private legionDeploymentService: LegionDeploymentService,
   ) { }
 
   ngOnInit(): void {
@@ -144,7 +146,7 @@
   public isResourcesInProgress(notebook) {
     const env = this.getResourceByName(notebook.name, notebook.project);
 
-    if (env && env.resources.length) {
+    if (env && env.resources && env.resources.length) {
       return env.resources.filter(item => (item.status !== 'failed' && item.status !== 'terminated'
         && item.status !== 'running' && item.status !== 'stopped')).length > 0;
     }
@@ -173,7 +175,12 @@
   }
 
   public printDetailEnvironmentModal(data): void {
-    this.dialog.open(DetailDialogComponent, { data: data, panelClass: 'modal-lg' })
+    this.dialog.open(DetailDialogComponent, { data: {notebook: data}, panelClass: 'modal-lg' })
+      .afterClosed().subscribe(() => this.buildGrid());
+  }
+
+  public printDetailLegionModal(data): void {
+    this.dialog.open(DetailDialogComponent, { data: {legion: data}, panelClass: 'modal-lg' })
       .afterClosed().subscribe(() => this.buildGrid());
   }
 
@@ -219,7 +226,7 @@
   private getResourceByName(notebook_name: string, project_name: string) {
     return this.getEnvironmentsListCopy().filter(environments => environments.project === project_name)
       .map(env => env.exploratory.find(({ name }) => name === notebook_name))
-      .filter(name => !!name)[0];
+      .filter(notebook_name => !!notebook_name)[0];
   }
 
   private getEnvironmentsListCopy() {
@@ -234,11 +241,11 @@
       if (shapes.indexOf(item.shape) === -1) shapes.push(item.shape);
       if (statuses.indexOf(item.status) === -1) statuses.push(item.status);
       statuses.sort(SortUtils.statusSort);
-
+      if (item.resources) {
       item.resources.map((resource: any) => {
         if (resources.indexOf(resource.status) === -1) resources.push(resource.status);
         resources.sort(SortUtils.statusSort);
-      });
+      }); }
     }));
 
     this.filterConfiguration = new FilterConfigurationModel('', statuses, shapes, resources, '', '');
@@ -249,7 +256,10 @@
     let filteredData = this.getEnvironmentsListCopy();
 
     const containsStatus = (list, selectedItems) => {
-      return list.filter((item: any) => { if (selectedItems.indexOf(item.status) !== -1) return item; });
+      if (list && list.length) {
+        return list.filter((item: any) => { if (selectedItems.indexOf(item.status) !== -1) return item; });
+      }
+      return list;
     };
 
     if (filteredData.length) this.filtering = true;
@@ -264,7 +274,6 @@
             const isName = item.name.toLowerCase().indexOf(config.name.toLowerCase()) !== -1;
             const isStatus = config.statuses.length > 0 ? (config.statuses.indexOf(item.status) !== -1) : (config.type !== 'active');
             const isShape = config.shapes.length > 0 ? (config.shapes.indexOf(item.shape) !== -1) : true;
-
             const modifiedResources = containsStatus(item.resources, config.resources);
             let isResources = config.resources.length > 0 ? (modifiedResources.length > 0) : true;
 
@@ -352,4 +361,15 @@
       .subscribe((result) => { },
         (error) => console.log('UPDATE USER PREFERENCES ERROR ', error));
   }
+
+  private odahuAction(element: any, action: string) {
+    this.dialog.open(OdahuActionDialogComponent, {data: {type: action, item: element}, panelClass: 'modal-sm'})
+      .afterClosed().subscribe(result => {
+        result && this.legionDeploymentService.odahuAction(element,  action).subscribe(v =>
+          this.buildGrid(),
+          error => this.toastr.error(`Odahu cluster ${action} failed!`, 'Oops!')
+        ) ;
+      }, error => this.toastr.error(error.message || `Odahu cluster ${action} failed!`, 'Oops!')
+    );
+  }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
index 14a2824..7d40063 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.model.ts
@@ -55,11 +55,12 @@
   public static loadEnvironments(data: Array<any>) {
     if (data) {
       return data.map((value) => {
-        return {
-          project: value.project,
-          exploratory: value.exploratory.map(el => {
-            const provider = el.cloud_provider.toLowerCase();
-            return new ExploratoryModel(
+        const exploratory = value.exploratory.map(el => {
+          let provider;
+          if (el.cloud_provider) {
+            provider = el.cloud_provider.toLowerCase();
+          }
+          return new ExploratoryModel(
             provider,
             el.exploratory_name,
             el.template_name,
@@ -73,13 +74,13 @@
             el.private_ip,
             el.exploratory_user,
             el.exploratory_pass,
-            value.shared[el.endpoint][DICTIONARY[provider].bucket_name],
-            value.shared[el.endpoint][DICTIONARY[provider].shared_bucket_name],
-            el.error_message,
-            el[DICTIONARY[provider].billing.cost],
-            el[DICTIONARY[provider].billing.currencyCode],
-            el.billing,
-            el.libs,
+          value.shared[el.endpoint][DICTIONARY[provider].bucket_name],
+          value.shared[el.endpoint][DICTIONARY[provider].shared_bucket_name],
+          el.error_message,
+          el[DICTIONARY[provider].billing.cost],
+          el[DICTIONARY[provider].billing.currencyCode],
+          el.billing,
+          el.libs,
             value.shared[el.endpoint][DICTIONARY[provider].user_storage_account_name],
             value.shared[el.endpoint][DICTIONARY[provider].shared_storage_account_name],
             value.shared[el.endpoint][DICTIONARY[provider].datalake_name],
@@ -87,8 +88,50 @@
             value.shared[el.endpoint][DICTIONARY[provider].datalake_shared_directory_name],
             el.project,
             el.endpoint,
-            el.tags,
-          )})
+            el.tags
+          );
+        });
+
+        const odahu = value.odahu.map(el => {
+          let provider;
+          if (el.cloud_provider) {
+            provider = el.cloud_provider.toLowerCase();
+          } else {
+            provider = 'azure';
+          }
+          return new ExploratoryModel(
+            provider,
+            el.name,
+            el.template_name,
+            el.image,
+            el.status.toLowerCase(),
+            'odahu cluster',
+            [],
+            el.up_time,
+            el.urls,
+            value.shared[el.endpoint].edge_node_ip,
+            el.private_ip,
+          el.exploratory_user,
+          el.exploratory_pass,
+          el.grafana_admin,
+          el.grafana_pass,
+          el.error_message,
+          el[DICTIONARY[provider].billing.cost],
+            el[DICTIONARY[provider].billing.currencyCode],
+            [],
+            [],
+            '',
+            '',
+            '',
+            '',
+            '',
+            el.project,
+            el.endpoint,
+            el.tags
+          )});
+        return {
+          project: value.project,
+          exploratory: [...exploratory, ...odahu]
         };
       });
     }
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/edge-action-dialog/index.ts b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/edge-action-dialog/index.ts
index fd6e98a..c91d503 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/edge-action-dialog/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/edge-action-dialog/index.ts
@@ -21,7 +21,7 @@
 import { CommonModule } from '@angular/common';
 import { EdgeActionDialogComponent } from './edge-action-dialog.component';
 import { MaterialModule } from '../../material.module';
-import {FormsModule} from "@angular/forms";
+import {FormsModule} from '@angular/forms';
 
 export * from './edge-action-dialog.component';
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/index.ts b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/index.ts
new file mode 100644
index 0000000..bfc5420
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/index.ts
@@ -0,0 +1,15 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { OdahuActionDialogComponent } from './odahu-action-dialog.component';
+import { MaterialModule } from '../../material.module';
+import {FormsModule} from '@angular/forms';
+
+export * from './odahu-action-dialog.component';
+
+@NgModule({
+  imports: [CommonModule, MaterialModule, FormsModule],
+  declarations: [OdahuActionDialogComponent],
+  entryComponents: [OdahuActionDialogComponent],
+  exports: [OdahuActionDialogComponent]
+})
+export class OdahuActionDialogModule {}
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/odahu-action-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/odahu-action-dialog.component.ts
new file mode 100644
index 0000000..921ff99
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/odahu-action-dialog/odahu-action-dialog.component.ts
@@ -0,0 +1,50 @@
+import { Component, Inject } from '@angular/core';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+
+@Component({
+  selector: 'edge-action-dialog',
+  template: `
+  <div id="dialog-box">
+    <header class="dialog-header">
+      <h4 class="modal-title"><span class="action">{{data.type | titlecase}}</span> Odahu cluster</h4>
+      <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
+    </header>
+      <div mat-dialog-content class="content message mat-dialog-content">
+          <h3>Odahu cluster <span class="strong">{{data.item.name}} </span>will be {{label[data.type]}}</h3>
+      <p class="m-top-20 action-text"><span class="strong">Do you want to proceed?</span></p>
+
+      <div class="text-center m-top-30 m-bott-30">
+        <button type="button" class="butt" mat-raised-button (click)="dialogRef.close()">No</button>
+        <button type="button" class="butt butt-success" mat-raised-button (click)="dialogRef.close(true)">Yes</button>
+      </div>
+      </div>
+  </div>
+  `,
+  styles: [`
+    .content { color: #718ba6; padding: 20px 50px; font-size: 14px; font-weight: 400; margin: 0; }
+    .info .confirm-dialog { color: #607D8B; }
+    header { display: flex; justify-content: space-between; color: #607D8B; }
+    h3 { font-weight: 300; }
+    header h4 i { vertical-align: bottom; }
+    header a i { font-size: 20px; }
+    header a:hover i { color: #35afd5; cursor: pointer; }
+    .action{text-transform: capitalize}
+    .action-text { text-align: center; }
+    label { font-size: 15px; font-weight: 500; font-family: "Open Sans",sans-serif; cursor: pointer; display: flex; align-items: center;}
+    label input {margin-top: 2px; margin-right: 5px;}
+  `]
+})
+
+export class OdahuActionDialogComponent {
+  public label = {
+    stop: 'stopped',
+    start: 'started',
+    terminate: 'terminated',
+  };
+
+  constructor(
+    public dialogRef: MatDialogRef<OdahuActionDialogComponent>,
+    @Inject(MAT_DIALOG_DATA) public data: any) {
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/index.ts b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/index.ts
index 7a6f2d4..2796b61 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/index.ts
@@ -25,7 +25,8 @@
 
 import { NavbarComponent } from './navbar.component';
 import { NotificationDialogModule } from '../modal-dialog/notification-dialog';
-import {EdgeActionDialogModule} from "../modal-dialog/edge-action-dialog";
+import {EdgeActionDialogModule} from '../modal-dialog/edge-action-dialog';
+import {OdahuActionDialogModule} from '../modal-dialog/odahu-action-dialog';
 
 export * from './navbar.component';
 
@@ -36,6 +37,7 @@
     MaterialModule,
     NotificationDialogModule,
     EdgeActionDialogModule,
+    OdahuActionDialogModule,
     ProgressDialogModule,
     BubbleModule,
   ],
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
index 9485a87..1b6dc05 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
@@ -88,6 +88,11 @@
               <span *ngIf="isExpanded; else projects">Projects</span>
               <ng-template #projects><i class="material-icons">dns</i></ng-template>
             </a>
+            <a class="sub-nav-item" [style.margin-left.px]="isExpanded ? '30' : '0'" [routerLink]="['/odahu']"
+               [routerLinkActive]="['active']" [routerLinkActiveOptions]="{exact:true}">
+              <span *ngIf="isExpanded; else legion">Odahu deployment</span>
+              <ng-template #legion><i class="material-icons">get_app</i></ng-template>
+            </a>
             <a class="sub-nav-item" [style.margin-left.px]="isExpanded ? '30' : '0'"
               [routerLink]="['/environment_management']" [routerLinkActive]="['active']"
               [routerLinkActiveOptions]="{exact:true}">
diff --git a/services/self-service/src/main/resources/webapp/src/assets/styles/_dialogs.scss b/services/self-service/src/main/resources/webapp/src/assets/styles/_dialogs.scss
index c6f8fe8..595f36c 100644
--- a/services/self-service/src/main/resources/webapp/src/assets/styles/_dialogs.scss
+++ b/services/self-service/src/main/resources/webapp/src/assets/styles/_dialogs.scss
@@ -340,6 +340,12 @@
   font-weight: 500;
   max-height: 200px;
   overflow: auto;
+
+.disabled {
+  opacity: 0.4;
+  cursor: not-allowed;
+  pointer-events: none;
+
 }
 
 @media screen and (max-width: 1280px) {
diff --git a/services/self-service/src/test/java/com/epam/dlab/backendapi/resources/InfrastructureInfoResourceTest.java b/services/self-service/src/test/java/com/epam/dlab/backendapi/resources/InfrastructureInfoResourceTest.java
index b92335e..e31b1ed 100644
--- a/services/self-service/src/test/java/com/epam/dlab/backendapi/resources/InfrastructureInfoResourceTest.java
+++ b/services/self-service/src/test/java/com/epam/dlab/backendapi/resources/InfrastructureInfoResourceTest.java
@@ -37,7 +37,16 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.refEq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
 
 public class InfrastructureInfoResourceTest extends TestBase {
 
@@ -159,7 +168,7 @@
 	@Test
 	public void getUserResourcesWithException() {
 		doThrow(new DlabException("Could not load list of provisioned resources for user"))
-				.when(infrastructureInfoService).getUserResources(anyString());
+				.when(infrastructureInfoService).getUserResources(any(UserInfo.class));
 		final Response response = resources.getJerseyTest()
 				.target("/infrastructure/info")
 				.request()
@@ -169,7 +178,7 @@
 		assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, response.getStatus());
 		assertEquals(MediaType.APPLICATION_JSON, response.getHeaderString(HttpHeaders.CONTENT_TYPE));
 
-		verify(infrastructureInfoService).getUserResources(USER.toLowerCase());
+		verify(infrastructureInfoService).getUserResources(any(UserInfo.class));
 		verifyNoMoreInteractions(infrastructureInfoService);
 	}