diff --git a/.gitignore b/.gitignore
index 81a6ed9f80ee2d3a7a930b81fa1176651f20c0ae..80fb6f6fb61ee2b16095613fdecf9f835fc68d05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,4 +36,7 @@ build/
 target/*
 */target
 /mvn
-provider/indexer-gcp/bin/
\ No newline at end of file
+provider/indexer-gcp/bin/*
+
+# Environment configuration
+*.env
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7819692f9a39083f11b262b79dff3c97a20a1db3..29aea813863e3270b5412e143f841682193e3689 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,15 +4,15 @@ variables:
   AWS_ENVIRONMENT: dev
   GCP_BUILD_SUBDIR: provider/indexer-gcp
   GCP_INT_TEST_SUBDIR: testing/indexer-test-gcp
-  GCP_APPLICATION_NAME: os-indexer
+  GCP_APPLICATION_NAME: osdu-indexer
   GCP_ENVIRONMENT: dev
   GCP_PROJECT: opendes
   GCP_TENANT_NAME: opendes
   GCP_DEPLOY_ENV: p4d
   GCP_DOMAIN: cloud.slb-ds.com
-  GCP_STORAGE_URL: https://os-indexer-dot-opendes.appspot.com/api/storage/v2/
+  GCP_STORAGE_URL: https://osdu-indexer-dot-opendes.appspot.com/api/storage/v2/
 
 include:
   - project: 'osdu/platform/ci-cd-pipelines'
     ref: 'master'
-    file: 'service.gitlab-ci.yml'
+    file: 'temp-service.gitlab-ci.yml'
diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/ElasticClientHandler.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/ElasticClientHandler.java
index fcc90699042b27fb2c4e67b7c3d4f24e97bf8267..7035abf3ecb9820e4f09b1c7f3ff89966437152a 100644
--- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/ElasticClientHandler.java
+++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/ElasticClientHandler.java
@@ -71,6 +71,22 @@ public class ElasticClientHandler {
             String basicEncoded = Base64.getEncoder().encodeToString(clusterSettings.getUserNameAndPassword().getBytes());
             String basicAuthenticationHeaderVal = String.format("Basic %s", basicEncoded);
 
+            RestClientBuilder builder = createClientBuilder(host, basicAuthenticationHeaderVal, port, protocolScheme, tls);
+
+            return new RestHighLevelClient(builder);
+        } catch (AppException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new AppException(
+                    HttpStatus.SC_INTERNAL_SERVER_ERROR,
+                    "search client error",
+                    "error creating search client",
+                    String.format("Elastic client connection params, cluster: %s, host: %s, port: %s", cluster, host, port),
+                    e);
+        }
+    }
+
+    public RestClientBuilder createClientBuilder(String host, String basicAuthenticationHeaderVal, int port, String protocolScheme, String tls) {
             RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, protocolScheme));
             builder.setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder.setConnectTimeout(REST_CLIENT_CONNECT_TIMEOUT)
                     .setSocketTimeout(REST_CLIENT_SOCKET_TIMEOUT));
@@ -80,23 +96,13 @@ public class ElasticClientHandler {
                     new BasicHeader("client.transport.nodes_sampler_interval", "30s"),
                     new BasicHeader("client.transport.ping_timeout", "30s"),
                     new BasicHeader("client.transport.sniff", "false"),
-                    new BasicHeader("request.headers.X-Found-Cluster", cluster),
-                    new BasicHeader("cluster.name", cluster),
+                new BasicHeader("request.headers.X-Found-Cluster", host),
+                new BasicHeader("cluster.name", host),
                     new BasicHeader("xpack.security.transport.ssl.enabled", tls),
                     new BasicHeader("Authorization", basicAuthenticationHeaderVal),
             };
 
             builder.setDefaultHeaders(defaultHeaders);
-            return new RestHighLevelClient(builder);
-        } catch (AppException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new AppException(
-                    HttpStatus.SC_INTERNAL_SERVER_ERROR,
-                    "search client error",
-                    "error creating search client",
-                    String.format("Elastic client connection params, cluster: %s, host: %s, port: %s", cluster, host, port),
-                    e);
-        }
+        return builder;
     }
 }
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 9e36b2746e2b28283aa939b56560511b11a43bee..0ce2c8068920277504c25f3b6557316db50eeec2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,6 +62,7 @@
         <module>provider/indexer-aws</module>
         <module>provider/indexer-azure</module>
         <module>provider/indexer-gcp</module>
+        <module>provider/indexer-ibm</module>
     </modules>
 
 </project>
diff --git a/provider/indexer-aws/.env.template b/provider/indexer-aws/.env.template
new file mode 100644
index 0000000000000000000000000000000000000000..b55ecdc872088111f1c3bb74f33a75bfb4af3253
--- /dev/null
+++ b/provider/indexer-aws/.env.template
@@ -0,0 +1,77 @@
+# Copyright © Amazon Web Services
+#
+# Licensed 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.
+
+##### Sample .env file ###########################################################
+#
+# Basic use: duplicate this file, and make sure the new copy is also in the root of the AWS
+# 'provider' folder, and name it `.env`. Note that on macOS, by default, files starting with
+# are considered hidden system files, and are not displayed by default in Finder or the file
+# selector (which you will need to use when adding the environment file(s) to the run
+# configuration(s). While you can change a setting to show hidden files and folders by
+# default, there is also a keyboard shortcut to quickly toggle between hide/show. With either
+# Finder as the active application ("Finder" appears next to the Apple logo in the Menu Bar),
+# press: command + shift + . (period). You can store configurations for multiple environments
+# by adding more duplicates following a naming scheme of your choosing, for example:
+# `staging.env`, `uat.env`, or `local.env`.
+#
+# This requires installing a plugin to your IDE that allows you to use a .env
+# file in your repository folder (does NOT get checked into source control;
+# only the sample environment configuration (sample.env) should be committed.
+#
+# Download links for .env file plugins:
+# IntelliJ - https://github.com/Ashald/EnvFile
+
+##### Authentication / Secrets #####
+# Replace placeholder text with your own AWS secret access keys
+# and rename to `.env` - do NOT check-in .env with your credentials! Leave it in .gitignore
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_KEY=
+AWS_ACCOUNT_ID=
+
+#### Urls/Ports #############
+STORAGE_HOST=
+
+APPLICATION_PORT=
+CACHE_CLUSTER_INDEX_ENDPOINT=
+CACHE_CLUSTER_INDEX_PORT=
+CACHE_CLUSTER_CURSOR_ENDPOINT=
+CACHE_CLUSTER_CURSOR_PORT=
+ELASTIC_HOST=
+ELASTIC_PORT=
+
+##### Other environment variables ##########################################################
+JAVA_HEAP_MEMORY=
+SNS_TOPIC_NAME=
+SNS_STORAGE_TOPIC_NAME=
+ENVIRONMENT=
+AWS_REGION=
+
+##### Integration test-specific - these are only used for integration tests, not the app ###
+OTHER_RELEVANT_DATA_COUNTRIES=
+LEGAL_TAG=
+DEFAULT_DATA_PARTITION_ID_TENANT1=
+DEFAULT_DATA_PARTITION_ID_TENANT2=
+ENTITLEMENTS_DOMAIN=
+AWS_COGNITO_CLIENT_ID=
+AWS_COGNITO_AUTH_FLOW=
+AWS_COGNITO_AUTH_PARAMS_PASSWORD=
+AWS_COGNITO_AUTH_PARAMS_USER=
+AWS_COGNITO_AUTH_PARAMS_USER_NO_ACCESS=
+ELASTIC_HOST=
+DEFAULT_ELASTIC_USER_NAME=
+DEFAULT_ELASTIC_PASSWORD=
+ELASTIC_PORT=
+SEARCH_HOST=
+STORAGE_HOST=
+INDEXER_HOST=
\ No newline at end of file
diff --git a/provider/indexer-aws/CloudFormation/Automated/cache.yml b/provider/indexer-aws/CloudFormation/Automated/cache.yml
index dcdc9a87f34a1e391eed3e3a178b79ada9904c0c..207f9815a887994d91731ced4215c00403587394 100644
--- a/provider/indexer-aws/CloudFormation/Automated/cache.yml
+++ b/provider/indexer-aws/CloudFormation/Automated/cache.yml
@@ -42,7 +42,7 @@ Parameters:
     MaxLength: '64'
     AllowedPattern: "^[a-zA-Z]+[0-9a-zA-Z_-]*$"
     ConstraintDescription: Must start with a letter. Only numbers, letters, -, and _ accepted. Max. length 64 characters.
-    Default: os-storage
+    Default: os-indexer
 
   CacheName:
     Description: The name of the cache cluster. Will be prefixed with the environment name.
diff --git a/provider/indexer-aws/CloudFormation/Automated/ecs-cluster.yml b/provider/indexer-aws/CloudFormation/Automated/ecs-cluster.yml
index 3e2787b66b85926b0153ad194a33191ff29b8f0b..3a357d30c1edaedc60a366f266552ee354850c38 100644
--- a/provider/indexer-aws/CloudFormation/Automated/ecs-cluster.yml
+++ b/provider/indexer-aws/CloudFormation/Automated/ecs-cluster.yml
@@ -1,3 +1,17 @@
+# Copyright © Amazon Web Services
+#
+# Licensed 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.
+
 AWSTemplateFormatVersion: 2010-09-09
 Description: >-
   CloudFormation template for creating the resources used for the ECS cluster the application will
@@ -161,6 +175,30 @@ Parameters:
     MinValue: 256
     MaxValue: 131072
 
+  DomainName:
+    Description: >-
+      The optional custom DNS name for the ECS service's load balancer. If omitted, the site will only be accessible
+      via the ECS service's Application Load Balancer DNS name. This value is used in the creation and signing of
+      the service's SSL certificate. Leave blank is not using a custom domain for this deployment.
+    Type: String
+    Default: ''
+
+  HostedZoneName:
+    Description: >-
+      The name of the hosted zone (ex: for indexer.osdu.slb.com, this would likely be osdu.slb.com).
+      Leave blank is not using a custom domain for this deployment.
+    Type: String
+    Default: ''
+
+  ElasticsearchDomainName:
+    Description: The name of the Elasticsearch domain. Will be prefixed with the environment name.
+    Type: String
+    MinLength: '1'
+    MaxLength: '64'
+    AllowedPattern: "^[a-zA-Z]+[0-9a-zA-Z_-]*$"
+    ConstraintDescription: Must start with a letter. Only numbers, letters, -, and _ accepted. Max. length 64 characters.
+    Default: osdu-indexer
+
 Mappings:
   # This mapping is for the ECS-optimized edition of the November 13-14, 2019 release of the Amazon Linux 2 AMI
   # It will need to be periodically updated as new versions are released by Amazon.
@@ -200,7 +238,45 @@ Mappings:
     sa-east-1:
       AMIID: ami-0c947c117562538ee
 
+Conditions:
+  IncludeCustomDomain: !Not [!Equals [ !Ref DomainName, '' ]]
+  IsPortStandardSSL:
+    !Or [!Equals [ !Ref ECSPort, '443' ], !Equals [ !Ref ECSPort, '8443' ]]
+  IsLoadBalancerHTTPS: !And # HTTPS for ECS requires a custom domain, but CloudFront will still have HTTPS/SSL
+    - !Condition IncludeCustomDomain
+    - !Condition IsPortStandardSSL
+
 Resources:
+  # This sets up a Route 53 record for CloudFront if a custom domain is being used,
+  # otherwise a default cloudfront.net value will be used instead
+  CloudFrontDNSName:
+    Type: AWS::Route53::RecordSetGroup
+    Condition: IncludeCustomDomain
+    Properties:
+      HostedZoneName: !Join ['', [!Ref HostedZoneName, .]] # Route 53 requires a trailing period
+      RecordSets:
+        - Name: !Ref DomainName
+          Type: A
+          AliasTarget:
+            # This hosted zone ID is for ALL CloudFront distributions, always, and should be hard-coded
+            HostedZoneId: Z2FDTNDATAQYW2
+            DNSName: !GetAtt ECSCloudFrontDistribution.DomainName
+
+  # This sets up a Route 53 record for the ECS ALB origin if a custom domain is being used
+  ECSDNSName:
+    Type: AWS::Route53::RecordSetGroup
+    Condition: IncludeCustomDomain
+    Properties:
+      HostedZoneName: !Join ['', [!Ref HostedZoneName, .]] # Route 53 requires a trailing period
+      RecordSets:
+        - Name: !Join ['.', ['origin', !Ref DomainName]] # prefix the ECS origin record with 'origin.'
+          Type: A
+          AliasTarget:
+            HostedZoneId: !GetAtt ECSALB.CanonicalHostedZoneID # this value comes from the ALB attributes
+            DNSName: !GetAtt ECSALB.DNSName
+            EvaluateTargetHealth: true # Route 53 routes traffic to ECS targets based on their health checks
+    DependsOn: ECSALB
+
   CodeDeployApplication:
     Type: AWS::CodeDeploy::Application
     Properties:
@@ -220,11 +296,11 @@ Resources:
               AWS:
                 - !Sub arn:aws:iam::${AWS::AccountId}:root
                 - Fn::ImportValue:
-                    !Sub "${Environment}-${ApplicationName}-CodeBuildRoleArn"
+                    !Sub "${Environment}-CodeBuildRoleArn"
                 - Fn::ImportValue:
-                    !Sub "${Environment}-${ApplicationName}-CFNRoleArn"
+                    !Sub "${Environment}-CFNRoleArn"
                 - Fn::ImportValue:
-                    !Sub "${Environment}-${ApplicationName}-PipelineRoleArn"
+                    !Sub "${Environment}-PipelineRoleArn"
               Service:
                 - codebuild.amazonaws.com
             Action:
@@ -278,6 +354,8 @@ Resources:
               Value: '{{resolve:secretsmanager:dev-IndexerServiceIamCredentials:SecretString:secret_key}}'
             - Name: ENVIRONMENT
               Value: !Ref Environment
+            - Name: VSTS_FEED_USER
+              Value: '{{resolve:secretsmanager:dev-VSTSFeedToken:SecretString:vsts_feed_user}}'
             - Name: VSTS_FEED_TOKEN
               Value: '{{resolve:secretsmanager:dev-VSTSFeedToken:SecretString:vsts_feed_token}}'
             - Name: CACHE_CLUSTER_SCHEMA_ENDPOINT
@@ -304,8 +382,22 @@ Resources:
               Value: !Ref 'AWS::AccountId'
             - Name: SNS_TOPIC_NAME
               Value: !Ref SNSTopicName
+            - Name: ELASTIC_HOST
+              Value:
+                Fn::ImportValue:
+                  !Sub "${Environment}-${ElasticsearchDomainName}-ElasticsearchDomainEndpoint"
+            - Name: ELASTIC_PORT
+              Value: '443' # the Elasticsearch port is not configurable on AWS, and is always 80 for HTTP and 443 for HTTPS, so there's no value in using a CFN parameter
             - Name: JAVA_HEAP_MEMORY
               Value: !Ref ECSMemoryAllocation
+            - Name: STORAGE_HOST
+              Value:
+                Fn::ImportValue:
+                  !Sub "${Environment}-os-storage-EcsCloudFrontDomainName"
+            - Name: SNS_STORAGE_TOPIC_NAME
+              Value:
+                Fn::ImportValue:
+                  !Sub "${Environment}-OSDUStorageSNSTopic"
       Volumes:
         - Name: docker-volume
 
@@ -335,7 +427,16 @@ Resources:
           TargetGroupArn: !Ref 'ECSTargetGroup'
       LoadBalancerArn: !Ref 'ECSALB'
       Port: !Ref ECSPort
-      Protocol: HTTP
+      Protocol: !If [IsLoadBalancerHTTPS, HTTPS, HTTP]
+
+  LoadBalancerALBListenerCertificate:
+    Type: AWS::ElasticLoadBalancingV2::ListenerCertificate
+    Condition: IncludeCustomDomain
+    Properties:
+      Certificates:
+        - Fn::ImportValue:
+            !Sub "${Environment}-${ApplicationName}-LoadBalancerSSLCertificateArn"
+      ListenerArn: !Ref 'ALBListener'
 
   ECSALBPrimaryListenerRule:
     Type: AWS::ElasticLoadBalancingV2::ListenerRule
@@ -356,17 +457,92 @@ Resources:
     Properties:
       HealthCheckIntervalSeconds: 120
       HealthCheckPath: /api/indexer/v2/liveness_check
-      HealthCheckProtocol: HTTP
+      HealthCheckProtocol: !If [IsLoadBalancerHTTPS, HTTPS, HTTP]
       HealthCheckTimeoutSeconds: 5
       HealthyThresholdCount: 2
-      Name: !Sub ECSTargetGroup-${ApplicationName}
+      Name: !Sub ECSTargetGroup-New-${ApplicationName}
       Port: !Ref ECSPort
-      Protocol: HTTP
+      Protocol: !If [IsLoadBalancerHTTPS, HTTPS, HTTP]
       UnhealthyThresholdCount: 2
       VpcId:
         Fn::ImportValue:
           !Sub "${Environment}-OSDU-VPC"
 
+  ECSCloudFrontDistribution:
+    Type: AWS::CloudFront::Distribution
+    DependsOn: ECSALB
+    Properties:
+      DistributionConfig:
+        Comment: 'Cloudfront Distribution pointing ALB Origin'
+        Origins:
+          - DomainName: !GetAtt 'ECSALB.DNSName'
+            Id: !Ref 'ECSALB'
+            CustomOriginConfig:
+              HTTPPort: !Ref ECSPort # The ports are the same because we'll only ever be accessing the ECS cluster over one protocol, as set in OriginProtocolPolicy below
+              HTTPSPort: !Ref ECSPort # The ports are the same because we'll only ever be accessing the ECS cluster over one protocol, as set in OriginProtocolPolicy below
+              OriginProtocolPolicy: !If [IsLoadBalancerHTTPS, https-only, http-only] # this only affects the origin, not CloudFront / the user's request
+              OriginKeepaliveTimeout: '60'
+              OriginReadTimeout: '60'
+              OriginSSLProtocols:
+                - TLSv1
+                - TLSv1.1
+                - TLSv1.2
+                - SSLv3
+        Enabled: true
+        HttpVersion: 'http2'
+        Aliases:
+          - Fn::If:
+              - IncludeCustomDomain
+              - !Ref DomainName
+              - !Ref AWS::NoValue
+        DefaultCacheBehavior:
+          AllowedMethods:
+            - GET
+            - HEAD
+            - OPTIONS
+            - PUT
+            - POST
+            - PATCH
+            - DELETE
+          Compress: true
+          TargetOriginId: !Ref 'ECSALB'
+          DefaultTTL: 5
+          MaxTTL: 30
+          ForwardedValues:
+            QueryString: true
+            Cookies:
+              Forward: all
+            Headers:
+              - Authorization
+              - Data-Partition-Id
+              - Content-Type
+              - Kind
+              - Limit
+              - Cursor
+          ViewerProtocolPolicy: redirect-to-https # CloudFront requests will always be HTTPS, regardless of the origin or the request
+        ViewerCertificate:
+          AcmCertificateArn:
+            Fn::If:
+              - IncludeCustomDomain
+              - Fn::ImportValue:
+                  !Sub "${Environment}-${ApplicationName}-LoadBalancerSSLCertificateArn"
+              - Ref: AWS::NoValue
+          CloudFrontDefaultCertificate:
+            Fn::If:
+              - IncludeCustomDomain
+              - Ref: AWS::NoValue
+              - true
+          SslSupportMethod:
+            Fn::If:
+              - IncludeCustomDomain
+              - sni-only # sni-only is free; 'vip' is the only other option, which allows viewers without Server Name Indication (SNI) support by using dedicated IP addresses, but it costs $600/mo per SSL certificate
+              - Ref: AWS::NoValue
+          MinimumProtocolVersion:
+            Fn::If:
+              - IncludeCustomDomain
+              - TLSv1
+              - Ref: AWS::NoValue # this is not used when using the default CloudFront certificate (which is always TLSv1)
+
   ECSAutoScalingGroup:
     Type: AWS::AutoScaling::AutoScalingGroup
     Properties:
@@ -555,6 +731,26 @@ Outputs:
     Export:
       Name: !Sub ${Environment}-${ApplicationName}-EcsAlbUrl
 
+  ECSALBCustomDNSName:
+    Description: The custom DNS name of the ECS service's ALB origin.
+    Condition: IncludeCustomDomain
+    Value: !Join ['.', ['origin', !Ref DomainName]]
+    Export:
+      Name: !Sub ${Environment}-${ApplicationName}-EcsAlbCustomDnsName
+
+  ECSCloudFrontCustomDNSName:
+    Description: The custom DNS name of the ECS service's CloudFront Distribution.
+    Condition: IncludeCustomDomain
+    Value: !Ref DomainName
+    Export:
+      Name: !Sub ${Environment}-${ApplicationName}-EcsCloudFrontCustomDnsName
+
+  ECSCloudFrontDomainName:
+    Description: The custom DNS name of the ECS service's CloudFront Distribution.
+    Value: !GetAtt ECSCloudFrontDistribution.DomainName
+    Export:
+      Name: !Sub ${Environment}-${ApplicationName}-EcsCloudFrontDomainName
+
   TaskDefinitionArn:
     Description: The ARN of the Indexer Service ECS task definition.
     Value: !Ref 'TaskDefinition'
diff --git a/provider/indexer-aws/CloudFormation/Automated/ecs-network.yml b/provider/indexer-aws/CloudFormation/Automated/ecs-network.yml
index cd3baa306cf870d1663747520c8823e542543214..0fa408776e7b9a4040531b58afcafc4550e8f239 100644
--- a/provider/indexer-aws/CloudFormation/Automated/ecs-network.yml
+++ b/provider/indexer-aws/CloudFormation/Automated/ecs-network.yml
@@ -1,3 +1,17 @@
+# Copyright © Amazon Web Services
+#
+# Licensed 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.
+
 AWSTemplateFormatVersion: 2010-09-09
 Description: >-
   CloudFormation template for creating the network resources used for the ECS cluster the application will
@@ -38,21 +52,59 @@ Parameters:
   ECSPort:
     Description: The port that the ECS Service will listen on.
     Type: Number
-    Default: 80
+    Default: 443
     MinValue: 1
     MaxValue: 65535
 
+  DomainName:
+    Description: >-
+      The optional custom DNS name for the service's load balancer. If omitted, the site will only be accessible
+      via the ECS service's Application Load Balancer DNS name. This value is used in the creation and signing of
+      the service's SSL certificate. Leave blank for none.
+    Type: String
+    Default: ''
+
+  AcmCertificateArn:
+    Description: >-
+      The Amazon Resource Name (ARN) of an existing AWS Certificate Manager (ACM) certificate.
+      If omitted, a new SSL certified will be requested/generated (only if the custom domain name
+      parameter is provided, otherwise the ECS service's ALB will not use SSL/HTTPS).
+    Type: String
+    AllowedPattern: "^(|arn:aws:acm:.*)$"
+    Default: ''
+
+Conditions:
+  IncludeCustomDomain: !Not [!Equals [ !Ref DomainName, '' ]]
+  UseExistingACMSSLCertificate: !And
+    - !Not [!Equals [ !Ref AcmCertificateArn, '' ]]
+    - !Condition IncludeCustomDomain
+  ShouldRequestNewSSLCertificate: !And
+    - !Not [!Condition UseExistingACMSSLCertificate]
+    - !Condition IncludeCustomDomain
+  ShouldExportSSLCertificate: !Or
+    - !Condition IncludeCustomDomain
+    - !Condition UseExistingACMSSLCertificate
+
 Resources:
+  # If an existing SSL certificate is not provided, but a custom domain is, request one
+  LoadBalancerSSLCertificate:
+    Type: 'AWS::CertificateManager::Certificate'
+    Condition: ShouldRequestNewSSLCertificate
+    Properties:
+      DomainName: !Ref DomainName
+      SubjectAlternativeNames:
+        - !Join ['.', ['origin', !Ref DomainName]] #
+
   ECSSecurityGroup:
     Type: AWS::EC2::SecurityGroup
     Properties:
       GroupName: !Sub "${Environment}-${ApplicationName}-sg"
-      GroupDescription: indexer Service ECS Security Group
+      GroupDescription: Indexer Service ECS Security Group
       VpcId:
         Fn::ImportValue:
           !Sub "${Environment}-OSDU-VPC"
 
-  # Public access to ECS Listening Port
+  # Public access to the specified ECS Listening Port
   ECSSecurityGroupECSListenerInbound:
     Type: AWS::EC2::SecurityGroupIngress
     Properties:
@@ -62,37 +114,8 @@ Resources:
       ToPort: !Ref ECSPort
       CidrIp: 0.0.0.0/0
 
-  # Public access to port 443
-  ECSSecurityGroupHTTPSInbound:
-    Type: AWS::EC2::SecurityGroupIngress
-    Properties:
-      GroupId: !Ref 'ECSSecurityGroup'
-      IpProtocol: tcp
-      FromPort: '443'
-      ToPort: '443'
-      CidrIp: 0.0.0.0/0
-
-  # Public access to port 8080
-  ECSSecurityGroupHTTPAltInbound:
-    Type: AWS::EC2::SecurityGroupIngress
-    Properties:
-      GroupId: !Ref 'ECSSecurityGroup'
-      IpProtocol: tcp
-      FromPort: '8080'
-      ToPort: '8080'
-      CidrIp: 0.0.0.0/0
-
-  # Public access to port 8443
-  ECSSecurityGroupHTTPSAltInbound:
-    Type: AWS::EC2::SecurityGroupIngress
-    Properties:
-      GroupId: !Ref 'ECSSecurityGroup'
-      IpProtocol: tcp
-      FromPort: '8443'
-      ToPort: '8443'
-      CidrIp: 0.0.0.0/0
-
-  # SSH access for instances in our VPC's jump box subnet group (coming soon – will be part of the Util CFN)
+  # SSH access for instances in our VPC's jump box subnet group
+  # TODO: Update when the jump box is created as a part of the Util CFN, for now it is public
   ECSSecurityGroupSSHInbound:
     Type: AWS::EC2::SecurityGroupIngress
     Properties:
@@ -102,7 +125,7 @@ Resources:
       ToPort: '22'
       CidrIp: 0.0.0.0/0
 
-  # Open Application Load Balancer port range to itself
+  # Open Application Load Balancer port range to self-access
   ECSSecurityGroupALBports:
     Type: AWS::EC2::SecurityGroupIngress
     Properties:
@@ -114,7 +137,14 @@ Resources:
 
 Outputs:
   EcsNetworkSecurityGroupId:
-    Description: The ID of the indexer Service ECS EC2 security group.
+    Description: The ID of the Indexer Service ECS EC2 security group.
     Value: !Ref 'ECSSecurityGroup'
     Export:
       Name: !Sub ${Environment}-${ApplicationName}-EcsNetworkSecurityGroupId
+
+  LoadBalancerSSLCertificateArn:
+    Condition: ShouldExportSSLCertificate
+    Description: The ARN of the SSL certificate to be used for both ECS and CloudFront (includes both DNS names).
+    Value: !If [UseExistingACMSSLCertificate, !Ref AcmCertificateArn, !Ref 'LoadBalancerSSLCertificate']
+    Export:
+      Name: !Sub ${Environment}-${ApplicationName}-LoadBalancerSSLCertificateArn
diff --git a/provider/indexer-aws/CloudFormation/Automated/elasticsearch.yml b/provider/indexer-aws/CloudFormation/Automated/elasticsearch.yml
index 00ef39a89b1c2496f654c32a620fe485a662569e..7a18783ae484ffcb6b248c98e131db4d28ee63a6 100644
--- a/provider/indexer-aws/CloudFormation/Automated/elasticsearch.yml
+++ b/provider/indexer-aws/CloudFormation/Automated/elasticsearch.yml
@@ -28,6 +28,28 @@ Parameters:
     ConstraintDescription: Can only be "dev/uat/prod"
     Default: dev
 
+  ApplicationName:
+    Description: >
+      The name of the application, which will be used to generate the ECS cluster name.
+      It will be prefixed with the environment name.
+    Type: String
+    MinLength: '1'
+    MaxLength: '64'
+    AllowedPattern: "^[a-zA-Z]+[0-9a-zA-Z_-]*$"
+    ConstraintDescription: Must start with a letter. Only numbers, letters, -, and _ accepted. Max. length 64 characters.
+    Default: os-indexer
+
+  SearchApplicationName:
+    Description: >
+      The name of the application, which will be used to generate the ECS cluster name.
+      It will be prefixed with the environment name.
+    Type: String
+    MinLength: '1'
+    MaxLength: '64'
+    AllowedPattern: "^[a-zA-Z]+[0-9a-zA-Z_-]*$"
+    ConstraintDescription: Must start with a letter. Only numbers, letters, -, and _ accepted. Max. length 64 characters.
+    Default: os-search
+
   Region:
     Description: The AWS region to deploy the resources to.
     Type: String
@@ -183,36 +205,26 @@ Resources:
         Enabled: false
       SnapshotOptions:
         AutomatedSnapshotStartHour: "0"
-#      AccessPolicies:
-#        - Version: "2012-10-17"
-#            Statement:
-#              - Effect: "Allow"
-#                Principal:
-#                  AWS:
-#                    - !Sub arn:aws:iam::${AWS::AccountId}:root
-#                    - Fn::ImportValue:
-#                        !Sub "${Environment}-IndexerServiceIamUserArn"
-#                      # TODO: need to create cognito user and identity pool and link it to principal for dynamic creation
-#                    - "arn:aws:iam::888733619319:role/Cognito_osduelasticsearchAuth_Role"
-#                Action:
-#                  - "es:*"
-#                  - 'es:ESHttp*'
-#                  - 'cognito-identity:*'
-#                  - 'cognito-idp:*'
-#                  - 'sts:AssumeRole'
-#                Resource: !Sub arn:aws:es:us-east-1:846973539254:domain/${Environment}-${ElasticsearchDomainName}/*
-#        - "Version": "2012-10-17"
-#          "Statement":
-#            - "Effect": "Allow"
-#              "Action":
-#                - "iam:PassRole"
-#              "Resource": "arn:aws:iam::888733619319:role/service-role/CognitoAccessForAmazonES"
+      AccessPolicies:
+        Version: 2012-10-17
+        Statement:
+          - Effect: "Allow"
+            Principal: "*"
+            Resource: '*'
+            Action: "*"
       AdvancedOptions:
         rest.action.multi.allow_explicit_index: "true"
       Tags:
         -
           Key: "Environment"
           Value: !Ref Environment
+      VPCOptions:
+        SubnetIds:
+          - Fn::ImportValue:
+              !Sub "${Environment}-OSDU-PrivateSubnet-AZ1"
+        SecurityGroupIds:
+          - Fn::ImportValue:
+              !Sub "${Environment}-${ApplicationName}-EcsNetworkSecurityGroupId"
 
 Outputs:
   # Elasticsearch domain ARN
diff --git a/provider/indexer-aws/CloudFormation/JarDeploy/CodePipeline-JarDeploy.yml b/provider/indexer-aws/CloudFormation/JarDeploy/CodePipeline-JarDeploy.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f75d29cd5944fc25edfcf213bac567194319af4e
--- /dev/null
+++ b/provider/indexer-aws/CloudFormation/JarDeploy/CodePipeline-JarDeploy.yml
@@ -0,0 +1,249 @@
+# Copyright © Amazon Web Services
+#
+# Licensed 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.
+
+AWSTemplateFormatVersion: 2010-09-09
+
+Description: >
+  This CloudFormation script creates the deployment pipeline for OSDU's indexer service. The CodePipeline
+  should automatically trigger whenever commits are made on the tracked branch. The start and end
+  of the CodePipeline should trigger a SNS alert to keep track of when the deployment has started
+  and when it finishes.
+
+Parameters:
+  Environment:
+    Description: Environment Name. Defaults to 'dev'. Can only be dev/uat/prod.
+    Type: String
+    AllowedValues:
+      - dev
+      - uat
+      - prod
+    Default: dev
+
+  DeploymentRegion:
+    Description: The AWS region to deploy the application to. The default is us-east-1.
+    Type: String
+    Default: us-east-1
+
+  SNSNotificationEmail:
+    Description: The email address to send SNS notifications about the build to.
+    Type: String
+    Default: barclay.walsh@parivedasolutions.com
+
+  CodeCommitRepositoryName:
+    Description: The name of the Code Commit Repository that the CodePipeline source is connected to.
+    Type: String
+    Default: os-indexer
+
+  JarServiceBase:
+    Description: The name of the service base path for the JAR files (e.g. 'indexer').
+    Type: String
+    Default: indexer
+
+  CodeCommitBranchName:
+    Description: The name of the Code Commit branch that the CodePipeline source is connected to.
+    Type: String
+    Default: dev
+
+Resources:
+  ArtifactStoreBucket:
+    Type: AWS::S3::Bucket
+    DeletionPolicy: Delete
+    Properties:
+      VersioningConfiguration:
+        Status: Enabled
+
+  ArtifactStoreBucketPolicy:
+    Type: AWS::S3::BucketPolicy
+    Properties:
+      Bucket: !Ref ArtifactStoreBucket
+      PolicyDocument:
+        Statement:
+          - Action:
+              - s3:*
+            Effect: Allow
+            Resource:
+              - !Sub arn:aws:s3:::${ArtifactStoreBucket}
+              - !Sub arn:aws:s3:::${ArtifactStoreBucket}/*
+            Principal:
+              AWS:
+                - !Sub arn:aws:iam::${AWS::AccountId}:root
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-CodeBuildRoleArn'
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-PipelineRoleArn'
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-CFNRoleArn'
+
+  CachingBucket:
+    Type: AWS::S3::Bucket
+    DeletionPolicy: Delete
+    Properties:
+      VersioningConfiguration:
+        Status: Enabled
+
+  CachingBucketPolicy:
+    Type: AWS::S3::BucketPolicy
+    Properties:
+      Bucket: !Ref CachingBucket
+      PolicyDocument:
+        Statement:
+          - Action:
+              - s3:*
+            Effect: Allow
+            Resource:
+              - !Sub arn:aws:s3:::${CachingBucket}
+              - !Sub arn:aws:s3:::${CachingBucket}/*
+            Principal:
+              AWS:
+                - !Sub arn:aws:iam::${AWS::AccountId}:root
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-CodeBuildRoleArn'
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-PipelineRoleArn'
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-CFNRoleArn'
+
+  SNSCodePipelineDeploymentFailed:
+    Type: AWS::SNS::Topic
+    Properties:
+      Subscription:
+        - Endpoint: !Ref SNSNotificationEmail
+          Protocol: email
+      TopicName: !Sub '${Environment}-OS-Indexer-Deployment-CodePipeline-JarDeploy-Failed'
+
+  EventRuleCodePipelineFailed:
+    Type: AWS::Events::Rule
+    Properties:
+      Description: Triggered whenever the CodePipeline deployment stage has failed.
+      EventPattern:
+        source:
+          - "aws.codepipeline"
+        detail-type:
+          - "CodePipeline Stage Execution State Change"
+        detail:
+          state:
+            - "FAILED"
+          pipeline:
+            - !Sub '${Environment}-OSDU-OS-Indexer-CodePipeline-JarDeploy'
+
+      Name: !Sub ${Environment}-CodePipelineEventRule-${CodeCommitRepositoryName}-JarDeploy
+      Targets:
+      -
+        Arn:
+          !Ref SNSCodePipelineDeploymentFailed
+        Id: "Deployment-CodePipeline-JarDeploy-Failed"
+        InputTransformer:
+          InputPathsMap:
+            pipeline : "$.detail.pipeline"
+          InputTemplate: '"The Pipeline <pipeline> has failed."'
+
+  Pipeline:
+    Type: AWS::CodePipeline::Pipeline
+    Properties:
+      ArtifactStore:
+        Location: !Ref ArtifactStoreBucket
+        Type: S3
+      Name: !Sub '${Environment}-OSDU-OS-Indexer-CodePipeline-JarDeploy'
+      RoleArn: !ImportValue
+        'Fn::Sub': '${Environment}-PipelineRoleArn'
+      Stages:
+        - Name: Source
+          Actions:
+            - Name: Source
+              ActionTypeId:
+                Category: Source
+                Owner: AWS
+                Provider: CodeCommit
+                Version: '1'
+              Configuration:
+                BranchName: !Ref CodeCommitBranchName
+                RepositoryName: !Ref CodeCommitRepositoryName
+              OutputArtifacts:
+                - Name: Source
+              RunOrder: '1'
+
+        - Name: CodeBuild
+          Actions:
+            - Name: Jar-CodeBuild
+              ActionTypeId:
+                Category: Build
+                Owner: AWS
+                Provider: CodeBuild
+                Version: '1'
+              InputArtifacts:
+                - Name: Source
+              OutputArtifacts:
+                - Name: Jar-CodeBuild
+              Configuration:
+                ProjectName: !Ref JarCodeBuild
+              RunOrder: '2'
+
+  JarCodeBuild:
+    Type: AWS::CodeBuild::Project
+    Properties:
+      Name: !Sub ${Environment}-jar-codebuild-${CodeCommitRepositoryName}
+      Description: CodeBuild commands which run after the CloudFormation deployment.
+      ServiceRole: !ImportValue
+        'Fn::Sub': '${Environment}-CodeBuildRoleArn'
+      Artifacts:
+        Type: S3
+        Location: !Ref ArtifactStoreBucket
+        Name: !Sub ${Environment}-jar-codebuild
+      Environment:
+        Type: LINUX_CONTAINER
+        ComputeType: BUILD_GENERAL1_SMALL
+        Image: aws/codebuild/standard:2.0
+        EnvironmentVariables:
+          - Name: ENVIRONMENT
+            Type: PLAINTEXT
+            Value: !Ref Environment
+          - Name: AWS_ACCOUNT_ID
+            Type: PLAINTEXT
+            Value: !Ref AWS::AccountId
+          - Name: AWS_REGION
+            Type: PLAINTEXT
+            Value: !Ref DeploymentRegion
+          - Name: APPLICATION_NAME
+            Type: PLAINTEXT
+            Value: !Ref CodeCommitRepositoryName
+          - Name: JAR_SERVICE_BASE
+            Type: PLAINTEXT
+            Value: !Ref JarServiceBase
+          - Name: M2_REPO_S3_BUCKET
+            Type: PLAINTEXT
+            Value: !Sub "${Environment}-${AWS::AccountId}-persistent-maven-m2-bucket"
+          - Name: JAR_DEPLOY_S3_BUCKET
+            Type: PLAINTEXT
+            Value: !Sub ${Environment}-${AWS::AccountId}-osdu-jar-deploy
+        PrivilegedMode: true
+      Source:
+        BuildSpec: ./provider/indexer-aws/buildspec-jar-deploy.yml
+        Location: !Sub https://git-codecommit.${AWS::Region}.amazonaws.com/v1/repos/${CodeCommitRepositoryName}
+        Type: CODECOMMIT
+      Cache:
+        Type: S3
+        Location: !Sub ${CachingBucket}/${Environment}
+      TimeoutInMinutes: 15
+      VpcConfig:
+        SecurityGroupIds:
+          - Fn::ImportValue:
+              !Sub "${Environment}-OSDU-CodeBuildSecurityGroup"
+        Subnets:
+          - Fn::ImportValue:
+              !Sub "${Environment}-OSDU-PrivateSubnet-AZ1"
+          - Fn::ImportValue:
+              !Sub "${Environment}-OSDU-PrivateSubnet-AZ2"
+        VpcId:
+          Fn::ImportValue:
+            !Sub "${Environment}-OSDU-VPC"
diff --git a/provider/indexer-aws/CloudFormation/Manual/01-CreateCodePipeline.yml b/provider/indexer-aws/CloudFormation/Manual/01-CreateCodePipeline.yml
index cfaad181934f70d70b9ce9213c9ab0f6b3175c1f..0109633f0071acd48c8520bf240dd962068ead11 100644
--- a/provider/indexer-aws/CloudFormation/Manual/01-CreateCodePipeline.yml
+++ b/provider/indexer-aws/CloudFormation/Manual/01-CreateCodePipeline.yml
@@ -84,35 +84,6 @@ Parameters:
     Default: indexerIndexCache
 
 Resources:
-  S3BucketCloudFormation:
-    Type: 'AWS::S3::Bucket'
-    DeletionPolicy: Delete
-    Properties:
-      BucketName: !Sub '${Environment}-os-indexer-cloudformation-scripts'
-  CloudFormationS3BucketPolicy:
-    Type: 'AWS::S3::BucketPolicy'
-    Properties:
-      Bucket: !Ref S3BucketCloudFormation
-      PolicyDocument:
-        Statement:
-          - Action:
-              - 's3:*'
-            Effect: Allow
-            Resource:
-              - !Sub 'arn:aws:s3:::${S3BucketCloudFormation}'
-              - !Sub 'arn:aws:s3:::${S3BucketCloudFormation}/*'
-            Principal:
-              AWS:
-                - !Sub 'arn:aws:iam::${AWS::AccountId}:root'
-                - !GetAtt
-                  - CodeBuildRole
-                  - Arn
-                - !GetAtt
-                  - PipelineRole
-                  - Arn
-                - !GetAtt
-                  - CFNRole
-                  - Arn
   ArtifactStoreBucket:
     Type: 'AWS::S3::Bucket'
     DeletionPolicy: Delete
@@ -134,15 +105,12 @@ Resources:
             Principal:
               AWS:
                 - !Sub 'arn:aws:iam::${AWS::AccountId}:root'
-                - !GetAtt
-                  - CodeBuildRole
-                  - Arn
-                - !GetAtt
-                  - PipelineRole
-                  - Arn
-                - !GetAtt
-                  - CFNRole
-                  - Arn
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-CodeBuildRoleArn'
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-PipelineRoleArn'
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-CFNRoleArn'
   CachingBucket:
     Type: AWS::S3::Bucket
     DeletionPolicy: Delete
@@ -165,9 +133,12 @@ Resources:
             Principal:
               AWS:
                 - !Sub arn:aws:iam::${AWS::AccountId}:root
-                - !GetAtt [CodeBuildRole,Arn]
-                - !GetAtt [PipelineRole,Arn]
-                - !GetAtt [CFNRole,Arn]
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-CodeBuildRoleArn'
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-PipelineRoleArn'
+                - !ImportValue
+                  'Fn::Sub': '${Environment}-CFNRoleArn'
 
   SNSCodePipelineDeploymentFailed:
     Type: 'AWS::SNS::Topic'
@@ -205,9 +176,8 @@ Resources:
         Location: !Ref ArtifactStoreBucket
         Type: S3
       Name: !Sub '${Environment}-OSDU-OS-Indexer-CodePipeline'
-      RoleArn: !GetAtt
-        - PipelineRole
-        - Arn
+      RoleArn: !ImportValue
+        'Fn::Sub': '${Environment}-PipelineRoleArn'
       Stages:
         - Name: Source
           Actions:
@@ -251,9 +221,8 @@ Resources:
               Configuration:
                 ActionMode: CREATE_UPDATE
                 Capabilities: CAPABILITY_NAMED_IAM
-                RoleArn: !GetAtt
-                  - CFNRole
-                  - Arn
+                RoleArn: !ImportValue
+                  'Fn::Sub': '${Environment}-CFNRoleArn'
                 StackName: !Sub '${Environment}-${MasterStackName}'
                 TemplatePath: !Sub 'Source::${MasterTemplateName}'
                 TemplateConfiguration: !Sub >-
@@ -280,10 +249,8 @@ Resources:
     Properties:
       Name: !Sub '${Environment}-pre-deployment-codebuild-${CodeCommitRepositoryName}'
       Description: CodeBuild commands which run prior to the CloudFormation deployment.
-      ServiceRole:
-        'Fn::GetAtt':
-          - CodeBuildRole
-          - Arn
+      ServiceRole: !ImportValue
+        'Fn::Sub': '${Environment}-CodeBuildRoleArn'
       Artifacts:
         Type: S3
         Location: !Ref ArtifactStoreBucket
@@ -303,8 +270,11 @@ Resources:
             Type: PLAINTEXT
             Value: !Ref DeploymentRegion
           - Name: CFN_S3_BUCKET
+            Value: !ImportValue
+              'Fn::Sub': '${Environment}-S3BucketCloudFormation'
+          - Name: APPLICATION_NAME
             Type: PLAINTEXT
-            Value: !Sub '${Environment}-os-indexer-cloudformation-scripts'
+            Value: !Ref CodeCommitRepositoryName
         PrivilegedMode: false
       Source:
         BuildSpec: ./provider/indexer-aws/buildspec-pre-deploy.yml
@@ -318,8 +288,8 @@ Resources:
     Properties:
       Name: !Sub ${Environment}-post-deployment-codebuild-${CodeCommitRepositoryName}
       Description: CodeBuild commands which run after the CloudFormation deployment.
-      ServiceRole:
-        Fn::GetAtt: [ CodeBuildRole, Arn ]
+      ServiceRole: !ImportValue
+        'Fn::Sub': '${Environment}-CodeBuildRoleArn'
       Artifacts:
         Type: S3
         Location: !Ref ArtifactStoreBucket
@@ -338,18 +308,12 @@ Resources:
           - Name: AWS_REGION
             Type: PLAINTEXT
             Value: !Ref DeploymentRegion
+          - Name: VSTS_FEED_USER
+            Type: PLAINTEXT
+            Value: '{{resolve:secretsmanager:dev-VSTSFeedToken:SecretString:vsts_feed_user}}'
           - Name: VSTS_FEED_TOKEN
             Type: PLAINTEXT
             Value: '{{resolve:secretsmanager:dev-VSTSFeedToken:SecretString:vsts_feed_token}}'
-          - Name: S3_MAVEN_REPOSITORY
-            Type: PLAINTEXT
-            Value: !Sub ${Environment}-osdu-local-maven-repository
-          - Name: AWS_SECRET_ACCESS_KEY_MAVEN
-            Type: PLAINTEXT
-            Value: '{{resolve:secretsmanager:dev-MavenS3RepositoryIamCredentials:SecretString:secret_key}}'
-          - Name: AWS_ACCESS_KEY_ID_MAVEN
-            Type: PLAINTEXT
-            Value: '{{resolve:secretsmanager:dev-MavenS3RepositoryIamCredentials:SecretString:access_key}}'
           - Name: IMAGE_TAG
             Type: PLAINTEXT
             Value: latest
@@ -368,6 +332,9 @@ Resources:
           - Name: APPLICATION_NAME
             Type: PLAINTEXT
             Value: !Ref CodeCommitRepositoryName
+          - Name: M2_REPO_S3_BUCKET
+            Type: PLAINTEXT
+            Value: !Sub "${Environment}-${AWS::AccountId}-persistent-maven-m2-bucket"
         PrivilegedMode: true
       Source:
         BuildSpec: ./provider/indexer-aws/buildspec-post-deploy.yml
@@ -388,293 +355,4 @@ Resources:
               !Sub "${Environment}-OSDU-PrivateSubnet-AZ2"
         VpcId:
           Fn::ImportValue:
-            !Sub "${Environment}-OSDU-VPC"
-
-  CFNRole:
-    Type: 'AWS::IAM::Role'
-    Properties:
-      AssumeRolePolicyDocument:
-        Statement:
-          - Action:
-              - 'sts:AssumeRole'
-            Effect: Allow
-            Principal:
-              Service:
-                - cloudformation.amazonaws.com
-        Version: 2012-10-17
-      Path: /
-      Policies:
-        - PolicyName: !Sub 'CloudFormationRole-${CodeCommitRepositoryName}'
-          PolicyDocument:
-            Version: 2012-10-17
-            Statement:
-              - Action:
-                  - 's3:*'
-                  - 'ec2:*'
-                  - 'apigateway:*'
-                  - 'cloudwatch:*'
-                  - 'events:*'
-                  - 'logs:*'
-                  - 'xray:*'
-                  - 'lambda:*'
-                  - 'rds:*'
-                  - 'codepipeline:*'
-                  - 'codecommit:*'
-                  - 'cloudformation:*'
-                  - 'ecr:*'
-                  - 'dynamodb:*'
-                  - 'application-autoscaling:*'
-                  - 'autoscaling:*'
-                  - 'states:*'
-                  - 'iam:CreateUser'
-                  - 'iam:UpdateUser'
-                  - 'iam:DeleteUser'
-                  - 'iam:CreateAccessKey'
-                  - 'iam:UpdateAccessKey'
-                  - 'iam:DeleteAccessKey'
-                  - 'iam:Delete*'
-                  - 'iam:List*'
-                  - 'iam:Get*'
-                  - 'iam:Put*'
-                  - 'iam:CreateServiceSpecificCredential'
-                  - 'iam:DeactivateMFADevice'
-                  - 'iam:GenerateServiceLastAccessedDetails'
-                  - 'iam:UpdateOpenIDConnectProviderThumbprint'
-                  - 'iam:PutRolePolicy'
-                  - 'iam:AddRoleToInstanceProfile'
-                  - 'iam:SimulateCustomPolicy'
-                  - 'iam:UploadSSHPublicKey'
-                  - 'iam:UpdateServiceSpecificCredential'
-                  - 'iam:RemoveClientIDFromOpenIDConnectProvider'
-                  - 'iam:UpdateRoleDescription'
-                  - 'iam:UpdateServerCertificate'
-                  - 'iam:CreateInstanceProfile'
-                  - 'iam:GenerateCredentialReport'
-                  - 'iam:UntagRole'
-                  - 'iam:PutRolePermissionsBoundary'
-                  - 'iam:TagRole'
-                  - 'iam:ResetServiceSpecificCredential'
-                  - 'iam:PassRole'
-                  - 'iam:EnableMFADevice'
-                  - 'iam:ResyncMFADevice'
-                  - 'iam:UpdateSAMLProvider'
-                  - 'iam:CreatePolicy'
-                  - 'iam:CreateServiceLinkedRole'
-                  - 'iam:UpdateRole'
-                  - 'iam:AddClientIDToOpenIDConnectProvider'
-                  - 'iam:SetDefaultPolicyVersion'
-                  - 'iam:UpdateAssumeRolePolicy'
-                  - 'iam:RemoveRoleFromInstanceProfile'
-                  - 'iam:CreateRole'
-                  - 'iam:AttachRolePolicy'
-                  - 'iam:CreateLoginProfile'
-                  - 'iam:DetachRolePolicy'
-                  - 'iam:AttachUserPolicy'
-                  - 'iam:DetachUserPolicy'
-                  - 'iam:SimulatePrincipalPolicy'
-                  - 'iam:CreateAccountAlias'
-                  - 'iam:ChangePassword'
-                  - 'iam:UpdateLoginProfile'
-                  - 'iam:UpdateAccessKey'
-                  - 'iam:UpdateSSHPublicKey'
-                  - 'iam:UpdateAccountPasswordPolicy'
-                  - 'iam:CreateSAMLProvider'
-                  - 'iam:CreateVirtualMFADevice'
-                  - 'iam:CreateAccessKey'
-                  - 'iam:AddUserToGroup'
-                  - 'iam:RemoveUserFromGroup'
-                  - 'iam:CreatePolicyVersion'
-                  - 'iam:UploadSigningCertificate'
-                  - 'iam:TagUser'
-                  - 'iam:CreateOpenIDConnectProvider'
-                  - 'iam:UploadServerCertificate'
-                  - 'iam:UntagUser'
-                  - 'iam:UpdateSigningCertificate'
-                  - 'sns:*'
-                  - 'sqs:*'
-                  - 'secretsmanager:*'
-                  - 'acm:*'
-                  - 'kms:*'
-                  - 'cloudfront:*'
-                  - 'route53:*'
-                  - 'route53domains:*'
-                  - 'elasticache:*'
-                  - 'es:*'
-                  - 'ecr:*'
-                  - 'codedeploy:*'
-                  - 'elasticloadbalancing:*'
-                  - 'ecs:*'
-                  - 'servicediscovery:CreatePrivateDnsNamespace'
-                  - 'servicediscovery:CreateService'
-                  - 'servicediscovery:GetNamespace'
-                  - 'servicediscovery:GetOperation'
-                  - 'servicediscovery:GetService'
-                  - 'servicediscovery:ListNamespaces'
-                  - 'servicediscovery:ListServices'
-                  - 'servicediscovery:UpdateService'
-                  - 'servicediscovery:DeleteService'
-                Effect: Allow
-                Resource: '*'
-  CodeBuildRole:
-    Type: 'AWS::IAM::Role'
-    Properties:
-      RoleName: !Sub 'CodeBuildRole-${CodeCommitRepositoryName}'
-      AssumeRolePolicyDocument:
-        Version: 2012-10-17
-        Statement:
-          - Effect: Allow
-            Principal:
-              Service:
-                - codebuild.amazonaws.com
-            Action:
-              - 'sts:AssumeRole'
-      Path: /service-role/
-      Policies:
-        - PolicyName: !Sub 'CodeBuildNestedCFNAccessPolicy-${CodeCommitRepositoryName}'
-          PolicyDocument:
-            Version: 2012-10-17
-            Statement:
-              - Effect: Allow
-                Action:
-                  - 'cloudformation:Get*'
-                  - 'cloudformation:Describe*'
-                  - 'cloudformation:List*'
-                Resource:
-                  - '*'
-              - Effect: Allow
-                Action:
-                  - 'codebuild:StartBuild'
-                Resource:
-                  - 'Fn::Sub': >-
-                      arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:project/*
-              - Effect: Allow
-                Action:
-                  - 'codecommit:ListBranches'
-                  - 'codecommit:ListRepositories'
-                  - 'codecommit:BatchGetRepositories'
-                  - 'codecommit:Get*'
-                  - 'codecommit:GitPull'
-                Resource:
-                  - !Sub >-
-                    arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${CodeCommitRepositoryName}
-              - Effect: Allow
-                Action:
-                  - 'ec2:*'
-                  - 'cloudformation:ValidateTemplate'
-                  - 'elasticloadbalancing:Describe*'
-                  - 'autoscaling:Describe*'
-                  - 'iam:Get*'
-                  - 'iam:List*'
-                  - 'logs:Describe*'
-                  - 'logs:Get*'
-                  - 'tag:Get*'
-                  - "ecr:*"
-                  - "codedeploy:*"
-                  - "ecs:*"
-                Resource:
-                  - '*'
-              - Effect: Allow
-                Action:
-                  - 'logs:CreateLogGroup'
-                  - 'logs:CreateLogStream'
-                  - 'logs:PutLogEvents'
-                Resource:
-                  - 'Fn::Sub': >-
-                      arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*
-              - Effect: Allow
-                Action:
-                  - 's3:*'
-                Resource: '*'
-              - Effect: Allow
-                Action:
-                  - 'lambda:UpdateFunctionCode'
-                  - 'lambda:UpdateFunctionConfiguration'
-                  - 'lambda:PublishLayerVersion'
-                  - 'lambda:GetLayerVersion'
-                Resource: '*'
-              - Effect: Allow
-                Action:
-                  - 'apigateway:GET'
-                  - 'apigateway:POST'
-                Resource: '*'
-  PipelineRole:
-    Type: 'AWS::IAM::Role'
-    Properties:
-      AssumeRolePolicyDocument:
-        Statement:
-          - Action:
-              - 'sts:AssumeRole'
-            Effect: Allow
-            Principal:
-              Service:
-                - codepipeline.amazonaws.com
-        Version: 2012-10-17
-      Path: /
-      Policies:
-        - PolicyName: !Sub 'CodePipelineAccess-${CodeCommitRepositoryName}'
-          PolicyDocument:
-            Version: 2012-10-17
-            Statement:
-              - Action:
-                  - 's3:*'
-                  - 'cloudformation:CreateStack'
-                  - 'cloudformation:DescribeStacks'
-                  - 'cloudformation:DeleteStack'
-                  - 'cloudformation:UpdateStack'
-                  - 'cloudformation:CreateChangeSet'
-                  - 'cloudformation:ExecuteChangeSet'
-                  - 'cloudformation:DeleteChangeSet'
-                  - 'cloudformation:DescribeChangeSet'
-                  - 'cloudformation:SetStackPolicy'
-                  - 'cloudformation:ValidateTemplate'
-                  - 'iam:PassRole'
-                  - 'sns:Publish'
-                  - 'lambda:ListFunctions'
-                  - 'lambda:InvokeFunction'
-                  - 'ec2:Describe*'
-                  - 'ec2:Get*'
-                  - 'ec2:Search*'
-                  - 'ec2:*Vpc*'
-                  - 'ec2:*Gateway'
-                  - 'ec2:*Tags'
-                  - 'ec2:*Subnet*'
-                  - 'ec2:*Route*'
-                  - 'ec2:*SecurityGroup'
-                  - 'ec2:allocate*'
-                  - 'ec2:release*'
-                Effect: Allow
-                Resource: '*'
-              - Action:
-                  - 'codecommit:GetUploadArchiveStatus'
-                  - 'codecommit:CancelUploadArchive'
-                  - 'codecommit:GetBranch'
-                  - 'codecommit:GetCommit'
-                  - 'codecommit:GetUploadStatus'
-                  - 'codecommit:UploadArchive'
-                Effect: Allow
-                Resource: '*'
-              - Effect: Allow
-                Action:
-                  - 'codebuild:*'
-                Resource:
-                  - 'Fn::Sub': >-
-                      arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:project/*
-
-Outputs:
-  CodeBuildRoleArn:
-    Description: The ARN of the role used by the CodeBuild projects.
-    Value: !GetAtt CodeBuildRole.Arn
-    Export:
-      Name: !Sub ${Environment}-${CodeCommitRepositoryName}-CodeBuildRoleArn
-
-  CFNRoleArn:
-    Description: The ARN of the role used by CloudFormation templates run from the automated pipeline.
-    Value: !GetAtt CFNRole.Arn
-    Export:
-      Name: !Sub ${Environment}-${CodeCommitRepositoryName}-CFNRoleArn
-
-  PipelineRoleArn:
-    Description: The ARN of the role used by the application's CodePipeline.
-    Value: !GetAtt PipelineRole.Arn
-    Export:
-      Name: !Sub ${Environment}-${CodeCommitRepositoryName}-PipelineRoleArn
+            !Sub "${Environment}-OSDU-VPC"
\ No newline at end of file
diff --git a/provider/indexer-aws/CloudFormation/Master/os-indexer-master.yml b/provider/indexer-aws/CloudFormation/Master/os-indexer-master.yml
index 17dd9c69dfcd27cc4c4ab2249d49028251770815..c4a49e0b6ab7580627d96052056f23df5e745680 100644
--- a/provider/indexer-aws/CloudFormation/Master/os-indexer-master.yml
+++ b/provider/indexer-aws/CloudFormation/Master/os-indexer-master.yml
@@ -15,6 +15,18 @@
 AWSTemplateFormatVersion: 2010-09-09
 Description: Creates all AWS resources used by OSDU's Indexer Service. Requires having previously setup the CodeCommit repository, as well as the CodePipeline (manual template).
 Parameters:
+
+  VersionNumber:
+    Description: Version Number for the pom to deploy the jar and Docker Image deployment in the Dockerfile
+    Type: String
+    Default: '0.0.1'
+
+  ServiceName:
+    Description: >-
+      Service name for jar deployment in the Dockerfile
+    Type: String
+    Default: 'indexer'
+
   Environment:
     Description: The name of the environment.
     Type: String
@@ -30,17 +42,9 @@ Parameters:
     Type: String
     Default: us-east-1
 
-  ChildTemplateBasePath:
-    Description: >-
-      The base path for where child CloudFormation templates are located – can be relative or absolute, e.g.
-      https://s3.amazonaws.com/dev-osdu-cloudformation-scripts/Automated/
-    Type: String
-    AllowedPattern: '^https:\/\/s3.amazonaws.com\/.*\/$'
-    Default: https://s3.amazonaws.com/dev-osdu-cloudformation-scripts/Automated/
-
   ApplicationName:
     Description: >
-      The name of the application, should be equal to the repository name.
+      The name of the indexer application, should be equal to the repository name.
     Type: String
     MinLength: '1'
     MaxLength: '64'
@@ -48,15 +52,15 @@ Parameters:
     ConstraintDescription: Must start with a letter. Only numbers, letters, -, and _ accepted. Max. length 64 characters.
     Default: os-indexer
 
-  StorageApplicationName:
+  SearchApplicationName:
     Description: >
-      The name of the storage application
+      The name of the Search Service application (ex: os-search). Should be the same as the Search Service repo name.
     Type: String
     MinLength: '1'
     MaxLength: '64'
     AllowedPattern: "^[a-zA-Z]+[0-9a-zA-Z_-]*$"
     ConstraintDescription: Must start with a letter. Only numbers, letters, -, and _ accepted. Max. length 64 characters.
-    Default: osdu-storage
+    Default: os-search
 
   KeyName:
     Description: >
@@ -443,6 +447,30 @@ Parameters:
     MinValue: 256
     MaxValue: 131072
 
+  DomainName:
+    Description: >-
+      The optional custom DNS name for the ECS service's load balancer. If omitted, the site will only be accessible
+      via the ECS service's Application Load Balancer DNS name. This value is used in the creation and signing of
+      the service's SSL certificate. Leave blank is not using a custom domain for this deployment.
+    Type: String
+    Default: ''
+
+  HostedZoneName:
+    Description: >-
+      The name of the hosted zone (ex: for indexer.osdu.slb.com, this would likely be osdu.slb.com).
+      Leave blank is not using a custom domain for this deployment.
+    Type: String
+    Default: ''
+
+  AcmCertificateArn:
+    Description: >-
+      The Amazon Resource Name (ARN) of an existing AWS Certificate Manager (ACM) certificate.
+      If omitted, a new SSL certified will be requested/generated (only if the custom domain name
+      parameter is provided, otherwise the ECS service's ALB will not use SSL/HTTPS).
+    Type: String
+    AllowedPattern: "^(|arn:aws:acm:.*)$"
+    Default: ''
+
 Resources:
 
   #### Shared Resources ################################################################
@@ -450,7 +478,11 @@ Resources:
   IAMCredentialsStack:
     Type: 'AWS::CloudFormation::Stack'
     Properties:
-      TemplateURL: !Join [ '', [ !Ref ChildTemplateBasePath, iam-credentials.yml ] ]
+      TemplateURL: !Sub
+        - https://s3.amazonaws.com/${CloudFormationS3Bucket}/${ApplicationName}/Automated/${CFNTemplateFilename}
+        - CloudFormationS3Bucket: !ImportValue
+            'Fn::Sub': '${Environment}-S3BucketCloudFormation'
+          CFNTemplateFilename: iam-credentials.yml
       Parameters:
         Environment: !Ref Environment
         Region: !Ref DeploymentRegion
@@ -460,7 +492,11 @@ Resources:
   MessageBusSNSStack:
     Type: 'AWS::CloudFormation::Stack'
     Properties:
-      TemplateURL: !Join ['', [!Ref ChildTemplateBasePath, sns-topic.yml ] ]
+      TemplateURL: !Sub
+        - https://s3.amazonaws.com/${CloudFormationS3Bucket}/${ApplicationName}/Automated/${CFNTemplateFilename}
+        - CloudFormationS3Bucket: !ImportValue
+            'Fn::Sub': '${Environment}-S3BucketCloudFormation'
+          CFNTemplateFilename: sns-topic.yml
       Parameters:
         Environment: !Ref Environment
         Region: !Ref DeploymentRegion
@@ -473,18 +509,28 @@ Resources:
     Type: 'AWS::CloudFormation::Stack'
     DependsOn: IAMCredentialsStack
     Properties:
-      TemplateURL: !Join [ '', [ !Ref ChildTemplateBasePath, ecs-network.yml ] ]
+      TemplateURL: !Sub
+        - https://s3.amazonaws.com/${CloudFormationS3Bucket}/${ApplicationName}/Automated/${CFNTemplateFilename}
+        - CloudFormationS3Bucket: !ImportValue
+            'Fn::Sub': '${Environment}-S3BucketCloudFormation'
+          CFNTemplateFilename: ecs-network.yml
       Parameters:
         Environment: !Ref Environment
         Region: !Ref DeploymentRegion
         ApplicationName: !Ref ApplicationName
         ECSPort: !Ref ECSPort
+        DomainName: !Ref DomainName
+        AcmCertificateArn: !Ref AcmCertificateArn
 
   ECSClusterStack:
     Type: 'AWS::CloudFormation::Stack'
     DependsOn: [SchemaCacheStack, IndexCacheStack]
     Properties:
-      TemplateURL: !Join [ '', [ !Ref ChildTemplateBasePath, ecs-cluster.yml ] ]
+      TemplateURL: !Sub
+        - https://s3.amazonaws.com/${CloudFormationS3Bucket}/${ApplicationName}/Automated/${CFNTemplateFilename}
+        - CloudFormationS3Bucket: !ImportValue
+            'Fn::Sub': '${Environment}-S3BucketCloudFormation'
+          CFNTemplateFilename: ecs-cluster.yml
       Parameters:
         Environment: !Ref Environment
         Region: !Ref DeploymentRegion
@@ -498,6 +544,9 @@ Resources:
         ECSPort: !Ref ECSPort
         SNSTopicName: !Ref SNSTopicName
         ECSMemoryAllocation: !Ref ECSMemoryAllocation
+        DomainName: !Ref DomainName
+        HostedZoneName: !Ref HostedZoneName
+        ElasticsearchDomainName: !Ref ElasticsearchDomainName
 
   #### Caching Resources ###############################################################
 
@@ -505,7 +554,11 @@ Resources:
     Type: 'AWS::CloudFormation::Stack'
     DependsOn: ECSNetworkStack
     Properties:
-      TemplateURL: !Join [ '', [ !Ref ChildTemplateBasePath, cache.yml ] ]
+      TemplateURL: !Sub
+        - https://s3.amazonaws.com/${CloudFormationS3Bucket}/${ApplicationName}/Automated/${CFNTemplateFilename}
+        - CloudFormationS3Bucket: !ImportValue
+            'Fn::Sub': '${Environment}-S3BucketCloudFormation'
+          CFNTemplateFilename: cache.yml
       Parameters:
         Environment: !Ref Environment
         Region: !Ref DeploymentRegion
@@ -519,7 +572,11 @@ Resources:
     Type: 'AWS::CloudFormation::Stack'
     DependsOn: ECSNetworkStack
     Properties:
-      TemplateURL: !Join [ '', [ !Ref ChildTemplateBasePath, cache.yml ] ]
+      TemplateURL: !Sub
+        - https://s3.amazonaws.com/${CloudFormationS3Bucket}/${ApplicationName}/Automated/${CFNTemplateFilename}
+        - CloudFormationS3Bucket: !ImportValue
+            'Fn::Sub': '${Environment}-S3BucketCloudFormation'
+          CFNTemplateFilename: cache.yml
       Parameters:
         Environment: !Ref Environment
         Region: !Ref DeploymentRegion
@@ -530,13 +587,16 @@ Resources:
         NumberOfCacheNodes: !Ref SchemaCacheNumberOfCacheNodes
 
   #### Elasticsearch Resources #########################################################
-  # TODO: Add VPCs to Elasticsearch
 
   ElasticsearchStack:
     Type: 'AWS::CloudFormation::Stack'
-    DependsOn: IAMCredentialsStack
+    DependsOn: [IAMCredentialsStack, ECSNetworkStack]
     Properties:
-      TemplateURL: !Join [ '', [ !Ref ChildTemplateBasePath, elasticsearch.yml ] ]
+      TemplateURL: !Sub
+        - https://s3.amazonaws.com/${CloudFormationS3Bucket}/${ApplicationName}/Automated/${CFNTemplateFilename}
+        - CloudFormationS3Bucket: !ImportValue
+            'Fn::Sub': '${Environment}-S3BucketCloudFormation'
+          CFNTemplateFilename: elasticsearch.yml
       Parameters:
         Environment: !Ref Environment
         Region: !Ref DeploymentRegion
@@ -548,3 +608,18 @@ Resources:
         ZoneAwarenessEnabled: !Ref ZoneAwarenessEnabled
         ElasticsearchVersion: !Ref ElasticsearchVersion
         EBSVolumeSize: !Ref EBSVolumeSize
+        ApplicationName: !Ref ApplicationName
+        SearchApplicationName: !Ref SearchApplicationName
+
+Outputs:
+  JarVersionNumber:
+    Description: The service name associated with the JAR package for the Dockerfile.
+    Value: !Ref 'VersionNumber'
+    Export:
+      Name: !Sub ${Environment}-${ApplicationName}-JarVersionNumber
+
+  JarServiceName:
+    Description: The service name associated with the JAR package for the Dockerfile.
+    Value: !Ref 'ServiceName'
+    Export:
+      Name: !Sub ${Environment}-${ApplicationName}-JarServiceName
diff --git a/provider/indexer-aws/CloudFormation/Params/dev.template_configuration.json b/provider/indexer-aws/CloudFormation/Params/dev.template_configuration.json
index c24b72c65cdf1c366e0c85096177c12f30107a3c..f33fd69a62180cc5c08e9962a46aae62705cabf5 100644
--- a/provider/indexer-aws/CloudFormation/Params/dev.template_configuration.json
+++ b/provider/indexer-aws/CloudFormation/Params/dev.template_configuration.json
@@ -2,8 +2,8 @@
   "Parameters" : {
     "Environment" : "dev",
     "DeploymentRegion" : "us-east-1",
-    "ChildTemplateBasePath" : "https://s3.amazonaws.com/dev-os-indexer-cloudformation-scripts/Automated/",
     "ApplicationName" : "os-indexer",
+    "SearchApplicationName" : "os-search",
     "KeyName": "indexer-ecs-keypair",
     "DesiredCapacity": "2",
     "MinSize": "0",
@@ -24,14 +24,19 @@
     "ElasticsearchDomainName": "osdu-indexer",
     "ElasticsearchNodeInstanceType": "t2.medium.elasticsearch",
     "DedicatedMasterInstanceType": "t2.medium.elasticsearch",
-    "NumberOfElasticsearchNodes": "2",
-    "NumberOfDedicatedMasterNodes": "2",
+    "NumberOfElasticsearchNodes": "4",
+    "NumberOfDedicatedMasterNodes": "3",
     "ZoneAwarenessEnabled": "false",
     "ElasticsearchVersion": "6.8",
     "EBSVolumeSize": "10",
-    "ECSPort": "80",
+    "ECSPort": "443",
     "ECSCPUAllocation": "1024",
-    "ECSMemoryAllocation": "3072"
+    "ECSMemoryAllocation": "3072",
+    "DomainName": "",
+    "HostedZoneName": "",
+    "AcmCertificateArn": "",
+    "ServiceName": "indexer",
+    "VersionNumber": "1.0.5-SNAPSHOT"
   },
   "Tags" : {
     "Environment" : "dev"
diff --git a/provider/indexer-aws/CloudFormation/Params/prod.template_configuration.json b/provider/indexer-aws/CloudFormation/Params/prod.template_configuration.json
index 013cc5a2bd6a96f28185a4b08291e228f18f9948..cf2353331086afe91f04f7abcaa4c9736a4a5d84 100644
--- a/provider/indexer-aws/CloudFormation/Params/prod.template_configuration.json
+++ b/provider/indexer-aws/CloudFormation/Params/prod.template_configuration.json
@@ -2,8 +2,8 @@
   "Parameters" : {
     "Environment" : "prod",
     "DeploymentRegion" : "us-east-1",
-    "ChildTemplateBasePath" : "https://s3.amazonaws.com/prod-os-indexer-cloudformation-scripts/Automated/",
     "ApplicationName" : "os-indexer",
+    "SearchApplicationName" : "os-search",
     "KeyName": "indexer-ecs-keypair",
     "DesiredCapacity": "2",
     "MinSize": "0",
@@ -29,9 +29,14 @@
     "ZoneAwarenessEnabled": "false",
     "ElasticsearchVersion": "6.8",
     "EBSVolumeSize": "10",
-    "ECSPort": "80",
+    "ECSPort": "443",
     "ECSCPUAllocation": "1024",
-    "ECSMemoryAllocation": "3072"
+    "ECSMemoryAllocation": "3072",
+    "DomainName": "",
+    "HostedZoneName": "",
+    "AcmCertificateArn": "",
+    "ServiceName": "indexer",
+    "VersionNumber": "1.0.5-SNAPSHOT"
   },
   "Tags" : {
     "Environment" : "prod"
diff --git a/provider/indexer-aws/CloudFormation/Params/uat.template_configuration.json b/provider/indexer-aws/CloudFormation/Params/uat.template_configuration.json
index c5e5a3af82545b5617f743aae1485117413132d2..10d7331ed0253013feff3eaf96e468aca3b80394 100644
--- a/provider/indexer-aws/CloudFormation/Params/uat.template_configuration.json
+++ b/provider/indexer-aws/CloudFormation/Params/uat.template_configuration.json
@@ -2,8 +2,8 @@
   "Parameters" : {
     "Environment" : "uat",
     "DeploymentRegion" : "us-east-1",
-    "ChildTemplateBasePath" : "https://s3.amazonaws.com/uat-os-indexer-cloudformation-scripts/Automated/",
     "ApplicationName" : "os-indexer",
+    "SearchApplicationName" : "os-search",
     "KeyName": "indexer-ecs-keypair",
     "DesiredCapacity": "2",
     "MinSize": "0",
@@ -29,9 +29,14 @@
     "ZoneAwarenessEnabled": "false",
     "ElasticsearchVersion": "6.8",
     "EBSVolumeSize": "10",
-    "ECSPort": "80",
+    "ECSPort": "443",
     "ECSCPUAllocation": "1024",
-    "ECSMemoryAllocation": "3072"
+    "ECSMemoryAllocation": "3072",
+    "DomainName": "",
+    "HostedZoneName": "",
+    "AcmCertificateArn": "",
+    "ServiceName": "indexer",
+    "VersionNumber": "1.0.5-SNAPSHOT"
   },
   "Tags" : {
     "Environment" : "uat"
diff --git a/provider/indexer-aws/Dockerfile b/provider/indexer-aws/Dockerfile
index bb98f5ab9611074293ed6f060512463bfb068597..07b7281a7d160013df35a5a150fdcaa8f5bf2c53 100644
--- a/provider/indexer-aws/Dockerfile
+++ b/provider/indexer-aws/Dockerfile
@@ -1,10 +1,26 @@
+# Copyright © Amazon Web Services
+#
+# Licensed 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 amazoncorretto:8
 
-ARG JAR_VERSION
-ENV JAR_FILE=indexer-aws-${JAR_VERSION}-spring-boot.jar
+ARG versionNumber
+ARG service
+ENV serviceName=${service}-aws
+ENV awsJar=${serviceName}-${versionNumber}-spring-boot.jar
 
 WORKDIR /
-COPY provider/indexer-aws/target/$JAR_FILE $JAR_FILE
+COPY provider/${serviceName}/target/${awsJar} ${awsJar}
 EXPOSE 8080
 
-CMD java -jar $JAR_FILE
+CMD ["sh","-c", " java -jar ${awsJar}"]
diff --git a/provider/indexer-aws/buildspec-jar-deploy.yml b/provider/indexer-aws/buildspec-jar-deploy.yml
new file mode 100644
index 0000000000000000000000000000000000000000..546f75409fd9e37722dd85a7298633e2f3b0932b
--- /dev/null
+++ b/provider/indexer-aws/buildspec-jar-deploy.yml
@@ -0,0 +1,59 @@
+# Copyright © Amazon Web Services
+#
+# Licensed 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.
+
+version: 0.2
+
+phases:
+  install:
+    runtime-versions:
+      java: openjdk8
+    commands:
+      - echo Entered the install phase...
+      - apt-get update -y
+      - apt-get install -y maven
+      - java -version
+      - mvn clean # .m2 is not created until the first Maven command
+      - cp ./provider/indexer-aws/maven/settings.xml /root/.m2/settings.xml # copy the AWS-specific settings.xml to the CodeBuild instance's .m2 folder
+      - cat /root/.m2/settings.xml
+      - java -version
+      - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
+      - echo $JAVA_HOME
+      - mvn -version
+      - echo "Look below for M2 bucket name:"
+      - echo $M2_REPO_S3_BUCKET
+      - aws s3 sync s3://$M2_REPO_S3_BUCKET /root/.m2 # copy previous state of the shared libraries' .m2 folder from S3 to local
+      - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2& # start the Docker Daemon
+      - timeout 15 sh -c "until docker info; do echo .; sleep 1; done" # wait for Docker to be ready before proceeding to the build steps
+  build:
+    commands:
+      - echo os-indexer Java build started on `date`...
+      - java -version
+      - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
+      - mvn -version
+      - echo All environment variables
+      - printenv
+      - mvn clean test -P indexer-core,indexer-aws
+      - echo ...os-indexer Java build completed on `date`.
+      - echo os-indexer beginning packaging to jar...
+      - mvn clean install -P indexer-core,indexer-aws -Ddeployment.environment=$ENVIRONMENT
+      - echo Uploading os-indexer JAR to S3...
+      - aws s3 cp provider/$JAR_SERVICE_BASE-aws/target s3://$JAR_DEPLOY_S3_BUCKET/$JAR_SERVICE_BASE-aws --recursive --exclude "*" --include "*.jar" # build and push the JAR(s) to S3
+
+cache:
+  paths:
+    - '/root/.m2/**/*'
+
+artifacts:
+  files:
+    - '**/*'
diff --git a/provider/indexer-aws/buildspec-post-deploy.yml b/provider/indexer-aws/buildspec-post-deploy.yml
index 02fd6e3a518ef177e7140ac5a164f94a60d26c46..5a576fa4c687e91a904ee66939f01d99df4b0981 100644
--- a/provider/indexer-aws/buildspec-post-deploy.yml
+++ b/provider/indexer-aws/buildspec-post-deploy.yml
@@ -30,6 +30,9 @@ phases:
       - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
       - echo $JAVA_HOME
       - mvn -version
+      - echo "Look below for M2 bucket name:"
+      - echo $M2_REPO_S3_BUCKET
+      - aws s3 sync s3://$M2_REPO_S3_BUCKET /root/.m2 # copy previous state of the shared libraries' .m2 folder from S3 to local
       - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2& # start the Docker Daemon
       - timeout 15 sh -c "until docker info; do echo .; sleep 1; done" # wait for Docker to be ready before proceeding to the build steps
   pre_build:
@@ -47,28 +50,24 @@ phases:
       - java -version
       - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
       - mvn -version
-      - echo Look below for repository bucket
-      - echo $S3_MAVEN_REPOSITORY
       - echo Setting environment variables from CloudFormation Exports... # use the AWS CLI commands to query for the CloudFormation export values created in the previous step and set the required environment variables
       - echo Environment - $ENVIRONMENT
       - echo SchemaCacheName - $SCHEMA_CACHE_NAME
       - echo IndexCacheName - $INDEX_CACHE_NAME
       - echo AWSRegion - $AWS_REGION
-      - export CACHE_CLUSTER_SCHEMA_ENDPOINT=$(aws cloudformation list-exports --query "Exports[?Name=='$ENVIRONMENT-$SCHEMA_CACHE_NAME-RedisEndpointAddress'].[Value]" --output text --region $AWS_REGION)
-      - export CACHE_CLUSTER_SCHEMA_PORT=$(aws cloudformation list-exports --query "Exports[?Name=='$ENVIRONMENT-$SCHEMA_CACHE_NAME-RedisEndpointPort'].[Value]" --output text --region $AWS_REGION)
-      - export CACHE_CLUSTER_INDEX_ENDPOINT=$(aws cloudformation list-exports --query "Exports[?Name=='$ENVIRONMENT-$INDEX_CACHE_NAME-RedisEndpointAddress'].[Value]" --output text --region $AWS_REGION)
-      - export CACHE_CLUSTER_INDEX_PORT=$(aws cloudformation list-exports --query "Exports[?Name=='$ENVIRONMENT-$INDEX_CACHE_NAME-RedisEndpointPort'].[Value]" --output text --region $AWS_REGION)
-      - export S3_DATA_BUCKET=$(aws cloudformation list-exports --query "Exports[?Name=='$ENVIRONMENT-S3BucketDataStorage'].[Value]" --output text --region $AWS_REGION)
-      - export SNS_TOPIC_NAME=$(aws cloudformation list-exports --query "Exports[?Name=='$ENVIRONMENT-OSDUIndexerSNSTopic'].[Value]" --output text --region $AWS_REGION)
+      - export VERSIONNUMBER=$(aws cloudformation list-exports --query "Exports[?Name=='$ENVIRONMENT-$APPLICATION_NAME-JarVersionNumber'].[Value]" --output text --region $AWS_REGION)
+      - echo VERSIONNUMBER - $VERSIONNUMBER
+      - export SERVICE=$(aws cloudformation list-exports --query "Exports[?Name=='$ENVIRONMENT-$APPLICATION_NAME-JarServiceName'].[Value]" --output text --region $AWS_REGION)
+      - echo SERVICE - $SERVICE
       - echo ...finished setting environment variables!
       - echo All environment variables
       - printenv
-      - mvn clean test -pl indexer-core,provider/indexer-aws -Ddeployment.environment=$ENVIRONMENT -Ddeployment.repositorybucket=$S3_MAVEN_REPOSITORY -Daws.accessKeyId=$AWS_ACCESS_KEY_ID -Daws.secretKey=$AWS_SECRET_KEY -Dazure.devops.token=$VSTS_FEED_TOKEN -DCACHE_CLUSTER_SCHEMA_ENDPOINT=$CACHE_CLUSTER_SCHEMA_ENDPOINT -DCACHE_CLUSTER_SCHEMA_PORT=$CACHE_CLUSTER_SCHEMA_PORT -DCACHE_CLUSTER_GROUP_ENDPOINT=$CACHE_CLUSTER_SCHEMA_ENDPOINT -DCACHE_CLUSTER_SCHEMA_PORT=$CACHE_CLUSTER_SCHEMA_PORT -DCACHE_CLUSTER_INDEX_ENDPOINT=$CACHE_CLUSTER_INDEX_ENDPOINT -DCACHE_CLUSTER_INDEX_PORT=$CACHE_CLUSTER_INDEX_PORT -DS3_DATA_BUCKET=$S3_DATA_BUCKET -DSNS_TOPIC_NAME=$SNS_TOPIC_NAME -DAWS_ACCOUNT_ID=$AWS_ACCOUNT_ID -DAWS_REGION=$AWS_REGION -DENVIRONMENT=$ENVIRONMENT
+      - mvn clean test -P indexer-core,indexer-aws
       - echo ...os-indexer Java build completed on `date`.
       - echo os-indexer beginning packaging to jar...
-      - mvn clean deploy -pl indexer-core,provider/indexer-aws -Ddeployment.environment=$ENVIRONMENT -Ddeployment.repositorybucket=$S3_MAVEN_REPOSITORY -Daws.accessKeyId=$AWS_ACCESS_KEY_ID_MAVEN -Daws.secretKey=$AWS_SECRET_ACCESS_KEY_MAVEN -Dazure.devops.token=$VSTS_FEED_TOKEN
+      - mvn clean install -P indexer-core,indexer-aws -Ddeployment.environment=$ENVIRONMENT -Dversion.number=$VERSIONNUMBER
       - echo os-indexer Docker image build started on `date`...
-      - docker build -f provider/indexer-aws/Dockerfile -t $REPOSITORY_URI:latest .
+      - docker build -f provider/indexer-aws/Dockerfile -t $REPOSITORY_URI:latest --build-arg versionNumber=$VERSIONNUMBER --build-arg service=$SERVICE .
       - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
       - echo ...os-indexer Docker image build completed on `date`.
       - echo Pushing the Docker image to ECR...
diff --git a/provider/indexer-aws/buildspec-pre-deploy.yml b/provider/indexer-aws/buildspec-pre-deploy.yml
index baf44c6068f3ec612bf4965e95c8f094e9ec8aae..14697d9c882a1905a8205f06328611e2d3c478ef 100644
--- a/provider/indexer-aws/buildspec-pre-deploy.yml
+++ b/provider/indexer-aws/buildspec-pre-deploy.yml
@@ -15,14 +15,6 @@
 version: 0.2
 
 phases:
-#  pre-build:
-#    commands:
-#      - echo Logging in to Amazon ECR...
-#      - aws --version
-#      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
-#      - REPOSITORY_URI=012345678910.dkr.ecr.us-east-1.amazonaws.com/hello-world
-#      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
-#      - IMAGE_TAG=build-$(echo $CODEBUILD_BUILD_ID | awk -F":" '{print $2}')
   install:
     runtime-versions:
       java: openjdk8
@@ -41,11 +33,11 @@ phases:
       - mvn -version
   build:
     commands:
-      - echo Starting 'Copying CloudFormation scripts to S3://$CFN_S3_BUCKET'
+      - echo Starting 'Copying CloudFormation scripts to S3://$CFN_S3_BUCKET/$APPLICATION_NAME'
       - pwd
       - ls
-      - aws s3 cp ./provider/indexer-aws/CloudFormation "s3://$CFN_S3_BUCKET" --exclude "*" --include "*.yml" --recursive --debug
-      - echo Ending 'Ending CloudFormation scripts to S3://$CFN_S3_BUCKET'
+      - aws s3 cp ./provider/indexer-aws/CloudFormation "s3://$CFN_S3_BUCKET/$APPLICATION_NAME" --exclude "*" --include "*.yml" --recursive --debug
+      - echo Ending 'Ending CloudFormation scripts to S3://$CFN_S3_BUCKET/$APPLICATION_NAME'
 #      - echo os-indexer build started on `date`...
       - pwd
       - ls -R -la
diff --git a/provider/indexer-aws/maven/settings.xml b/provider/indexer-aws/maven/settings.xml
index 54c4a7227d6191761d3b091148e6182d45d129cf..adba9a7602b0887236967efc41b49ccad08a7ca1 100644
--- a/provider/indexer-aws/maven/settings.xml
+++ b/provider/indexer-aws/maven/settings.xml
@@ -4,10 +4,12 @@
           xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
 
   <servers>
+    <!-- Azure DevOps Artifact Store -->
     <server>
       <id>os-core</id>
-      <username>slb-des-ext-collaboration</username>
-      <password>${VSTS_FEED_TOKEN}</password>
+      <username>${azure.devops.username}</username>
+      <!-- The generated token was last updated on 01/13/2020 and expires on or before 01/12/2021 -->
+      <password>${azure.devops.token}</password>
     </server>
 
   </servers>
diff --git a/provider/indexer-aws/pom.xml b/provider/indexer-aws/pom.xml
index 9a2981be01f8c46def71e2b20aba0de0715c3986..aa763a675d577afe9e3380606336703d0a09a48c 100644
--- a/provider/indexer-aws/pom.xml
+++ b/provider/indexer-aws/pom.xml
@@ -27,11 +27,12 @@
   <artifactId>indexer-aws</artifactId>
   <description>Storage service on AWS</description>
   <packaging>jar</packaging>
+    <version>${version.number}</version>
 
   <properties>
       <aws.version>1.11.637</aws.version>
       <deployment.environment>dev</deployment.environment>
-      <deployment.repositorybucket>osdu-local-maven-repository</deployment.repositorybucket>
+      <version.number>1.0.4-SNAPSHOT</version.number>
   </properties>
 
   <dependencies>
@@ -48,8 +49,8 @@
     </dependency>
     <dependency>
         <groupId>org.opengroup.osdu.core.aws</groupId>
-        <artifactId>aws-osdu-util</artifactId>
-        <version>0.0.9</version>
+        <artifactId>os-core-lib-aws</artifactId>
+        <version>0.0.10</version>
     </dependency>
 
     <!-- AWS managed packages -->
@@ -58,11 +59,6 @@
       <artifactId>aws-java-sdk-core</artifactId>
       <version>1.11.651</version>
     </dependency>
-    <dependency>
-      <groupId>com.github.awslabs</groupId>
-      <artifactId>aws-request-signing-apache-interceptor</artifactId>
-      <version>deb7941</version>
-    </dependency>
     <dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>aws-java-sdk</artifactId>
@@ -177,13 +173,6 @@
           </executions>
         </plugin>
       </plugins>
-      <extensions>
-          <extension>
-              <groupId>com.github.seahen</groupId>
-              <artifactId>maven-s3-wagon</artifactId>
-              <version>1.3.0</version>
-          </extension>
-      </extensions>
   </build>
   <repositories>
       <repository>
diff --git a/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/persistence/ElasticRepositoryImpl.java b/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/persistence/ElasticRepositoryImpl.java
index 22b0d8f5c5ca21fc079d1409889d9c5f2673f269..70e64ca21d70497fc21121729350d5b16f2a6a44 100644
--- a/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/persistence/ElasticRepositoryImpl.java
+++ b/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/persistence/ElasticRepositoryImpl.java
@@ -28,7 +28,8 @@ public class ElasticRepositoryImpl implements IElasticRepository {
     @Value("${aws.es.host}")
     String host;
 
-    int port = 8080;
+    @Value("${aws.es.port}")
+    int port;
 
     String userNameAndPassword = "testing";
 
diff --git a/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/service/ElasticClientHandlerAws.java b/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/service/ElasticClientHandlerAws.java
index 6a2498d0b89e2e85d9dd676039284605cf177fa4..3c7383a80aaa9aaf1a7fa8943673fa86dc0f371b 100644
--- a/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/service/ElasticClientHandlerAws.java
+++ b/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/service/ElasticClientHandlerAws.java
@@ -14,15 +14,12 @@
 
 package org.opengroup.osdu.indexer.aws.service;
 
-import com.amazonaws.auth.AWS4Signer;
-import com.amazonaws.auth.AWSCredentialsProvider;
-import com.amazonaws.http.AWSRequestSigningApacheInterceptor;
+import org.apache.http.Header;
 import org.apache.http.HttpHost;
-import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.message.BasicHeader;
 import org.elasticsearch.client.RestClient;
-import org.elasticsearch.client.RestHighLevelClient;
-import org.opengroup.osdu.core.aws.iam.IAMConfig;
 import org.opengroup.osdu.indexer.util.ElasticClientHandler;
+import org.elasticsearch.client.RestClientBuilder;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Primary;
 import org.springframework.stereotype.Component;
@@ -34,31 +31,38 @@ import javax.inject.Inject;
 @Component
 public class ElasticClientHandlerAws extends ElasticClientHandler {
 
-    @Value("${aws.es.serviceName}")
-    private String serviceName;
-
-    @Value("${aws.region}")
-    private String region;
-
-    @Value("${aws.es.host}")
-    String host;
+    private static final int REST_CLIENT_CONNECT_TIMEOUT = 60000;
+    private static final int REST_CLIENT_SOCKET_TIMEOUT = 60000;
+    private static final int REST_CLIENT_RETRY_TIMEOUT = 60000;
 
     public ElasticClientHandlerAws() {
     }
 
     @Override
-    public RestHighLevelClient createRestClient() {
+    public RestClientBuilder createClientBuilder(String host, String basicAuthenticationHeaderVal, int port, String protocolScheme, String tls) {
 
-        return esClient();
-    }
+        RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, protocolScheme));
+        builder.setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder
+                .setConnectTimeout(REST_CLIENT_CONNECT_TIMEOUT)
+                .setSocketTimeout(REST_CLIENT_SOCKET_TIMEOUT));
+        builder.setMaxRetryTimeoutMillis(REST_CLIENT_RETRY_TIMEOUT);
 
-    private RestHighLevelClient esClient() {
-        AWSCredentialsProvider credentials = new IAMConfig().amazonAWSCredentials();
-        AWS4Signer signer = new AWS4Signer();
-        signer.setServiceName(serviceName);
-        signer.setRegionName(region);
-        HttpRequestInterceptor interceptor = new AWSRequestSigningApacheInterceptor(serviceName, signer, credentials);
-        return new RestHighLevelClient(RestClient.builder(HttpHost.create(host)).setHttpClientConfigCallback(configCallBack -> configCallBack.addInterceptorLast(interceptor)));
+        if(isLocalHost(host)) {
+            builder.setHttpClientConfigCallback(httpAsyncClientBuilder -> httpAsyncClientBuilder.setSSLHostnameVerifier((s, sslSession) -> true));
+        }
+        Header[] defaultHeaders = new Header[]{
+                new BasicHeader("client.transport.nodes_sampler_interval", "30s"),
+                new BasicHeader("client.transport.ping_timeout", "30s"),
+                new BasicHeader("client.transport.sniff", "false"),
+                new BasicHeader("request.headers.X-Found-Cluster", host),
+                new BasicHeader("cluster.name", host),
+                new BasicHeader("xpack.security.transport.ssl.enabled", tls)
+        };
+        builder.setDefaultHeaders(defaultHeaders);
+        return builder;
+    }
 
+    private boolean isLocalHost(String uri) {
+        return (uri.contains("localhost") || uri.contains("127.0.0.1"));
     }
 }
diff --git a/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/util/IndexerQueueTaskBuilderAws.java b/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/util/IndexerQueueTaskBuilderAws.java
index 76607c97a21bd574e3f4f59d0b1f477c1543e799..bffecf18d7765fe7c52acaddc7dab246c64e13ec 100644
--- a/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/util/IndexerQueueTaskBuilderAws.java
+++ b/provider/indexer-aws/src/main/java/org/opengroup/osdu/indexer/aws/util/IndexerQueueTaskBuilderAws.java
@@ -14,33 +14,46 @@
 
 package org.opengroup.osdu.indexer.aws.util;
 
+import com.amazonaws.services.sns.AmazonSNS;
+import com.amazonaws.services.sns.model.MessageAttributeValue;
+import com.amazonaws.services.sns.model.PublishRequest;
 import com.amazonaws.services.sqs.AmazonSQS;
 import com.amazonaws.services.sqs.model.SendMessageRequest;
+import com.google.gson.Gson;
+import org.opengroup.osdu.core.aws.sns.AmazonSNSConfig;
 import org.opengroup.osdu.core.common.model.http.DpsHeaders;
 import org.opengroup.osdu.core.aws.sqs.AmazonSQSConfig;
+import org.opengroup.osdu.core.common.model.search.RecordChangedMessages;
 import org.opengroup.osdu.indexer.util.IndexerQueueTaskBuilder;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Primary;
 import org.springframework.stereotype.Component;
 
 import javax.inject.Inject;
+import java.util.HashMap;
+import java.util.Map;
 
 @Primary
 @Component
 public class IndexerQueueTaskBuilderAws extends IndexerQueueTaskBuilder {
 
-    private AmazonSQS sqsClient;
+    private AmazonSNS snsClient;
 
     @Value("${aws.region}")
     private String region;
 
-    @Value("${aws.sqs.queue}")
-    private String queueName;
+    @Value("${aws.sns.storage.arn}")
+    private String amazonSNSTopic;
+
+    private String retryString = "retry";
+
+    private Gson gson;
 
     @Inject
     public void init() {
-        AmazonSQSConfig sqsConfig = new AmazonSQSConfig(region);
-        sqsClient = sqsConfig.AmazonSQS();
+        AmazonSNSConfig config = new AmazonSNSConfig(region);
+        snsClient = config.AmazonSNS();
+        gson =new Gson();
     }
 
     @Override
@@ -54,8 +67,40 @@ public class IndexerQueueTaskBuilderAws extends IndexerQueueTaskBuilder {
     }
 
     private void createTask(String payload, DpsHeaders headers) {
-        String queueUrl = sqsClient.getQueueUrl(queueName).getQueueUrl();
-        SendMessageRequest messageRequest = new SendMessageRequest().withQueueUrl(queueUrl).withMessageBody(payload);
-        sqsClient.sendMessage(messageRequest);
+
+        Map<String, MessageAttributeValue> messageAttributes = new HashMap<>();
+        messageAttributes.put(DpsHeaders.ACCOUNT_ID, new MessageAttributeValue()
+                .withDataType("String")
+                .withStringValue(headers.getPartitionIdWithFallbackToAccountId()));
+        messageAttributes.put(DpsHeaders.DATA_PARTITION_ID, new MessageAttributeValue()
+                .withDataType("String")
+                .withStringValue(headers.getPartitionIdWithFallbackToAccountId()));
+        headers.addCorrelationIdIfMissing();
+        messageAttributes.put(DpsHeaders.CORRELATION_ID, new MessageAttributeValue()
+                .withDataType("String")
+                .withStringValue(headers.getCorrelationId()));
+        messageAttributes.put(DpsHeaders.USER_EMAIL, new MessageAttributeValue()
+                .withDataType("String")
+                .withStringValue(headers.getUserEmail()));
+        messageAttributes.put(DpsHeaders.AUTHORIZATION, new MessageAttributeValue()
+                .withDataType("String")
+                .withStringValue(headers.getAuthorization()));
+
+        RecordChangedMessages message = gson.fromJson(payload, RecordChangedMessages.class);
+        int retryCount;
+        if(message.getAttributes().containsKey(retryString)){
+            retryCount = Integer.parseInt(message.getAttributes().get(retryString));
+            retryCount++;
+        } else {
+            retryCount = 1;
+        }
+        messageAttributes.put(retryString, new MessageAttributeValue()
+                .withDataType("String")
+                .withStringValue(String.valueOf(retryCount)));
+
+        PublishRequest publishRequest = new PublishRequest(amazonSNSTopic, message.getData())
+                .withMessageAttributes(messageAttributes);
+
+        snsClient.publish(publishRequest);
     }
 }
diff --git a/provider/indexer-aws/src/main/resources/application.properties b/provider/indexer-aws/src/main/resources/application.properties
index a295c1956dba6ff653c0b84dce89f3697480431b..3ddc087e9e18032a061f0a2d5a8384df645d9a69 100644
--- a/provider/indexer-aws/src/main/resources/application.properties
+++ b/provider/indexer-aws/src/main/resources/application.properties
@@ -27,33 +27,24 @@ aws.s3.enable-https=true
 ## AWS SNS configuration
 aws.sns.region=${AWS_REGION}
 aws.sns.arn=arn:aws:sns:${AWS_REGION}:${AWS_ACCOUNT_ID}:${ENVIRONMENT}-${SNS_TOPIC_NAME}
-aws.sns.topic-name=${ENVIRONMENT}-${SNS_TOPIC_NAME}
+aws.sns.storage.arn=arn:aws:sns:${AWS_REGION}:${AWS_ACCOUNT_ID}:${SNS_STORAGE_TOPIC_NAME}
 
 ## AWS SQS Configuration
 aws.sqs.queue=${ENVIRONMENT}-osdu-indexer-queue
 
-#Spring Configuration
-spring.security.user.name=opendes@byoc.local
-spring.security.user.password=123
-spring.security.user.roles=service.storage.admin
-
-
 # AWS ES configuration
-#aws.es.host=https://search-${ENVIRONMENT}-osdu-indexer-i5bpf2gv4iv6ha2xi7rook2rga.${AWS_REGION}.es.amazonaws.com
-aws.es.host=https://search-dev-ado-test-y5m2xdyqha2npp36kh42vl3n7m.us-east-1.es.amazonaws.com
-aws.es.port=-1
+aws.es.host=${ELASTIC_HOST}
+aws.es.port=${ELASTIC_PORT}
 aws.es.userNameAndPassword=notused
 aws.region=${AWS_REGION}
 aws.es.serviceName=es
 
 GAE_SERVICE=indexer
 
-#STORAGE_SCHEMA_HOST=http://ECSALB-os-storage-1575155422.${AWS_REGION}.elb.amazonaws.com/api/storage/v2/schemas
-STORAGE_SCHEMA_HOST=http://localhost:8080/api/storage/v2/schemas
-#STORAGE_QUERY_RECORD_HOST=http://ECSALB-os-storage-1575155422.${AWS_REGION}.elb.amazonaws.com/api/storage/v2/query/records
-STORAGE_QUERY_RECORD_HOST=http://localhost:8080/api/storage/v2/query/records
-#STORAGE_QUERY_RECORD_FOR_CONVERSION_HOST=http://ECSALB-os-storage-1575155422.${AWS_REGION}.elb.amazonaws.com/api/storage/v2/query/records:batch
-STORAGE_QUERY_RECORD_FOR_CONVERSION_HOST=http://localhost:8080/api/storage/v2/query/records:batch
+# TODO This needs to be changed so it snot hard
+STORAGE_SCHEMA_HOST=https://${STORAGE_HOST}/api/storage/v2/schemas
+STORAGE_QUERY_RECORD_HOST=https://${STORAGE_HOST}/api/storage/v2/query/records
+STORAGE_QUERY_RECORD_FOR_CONVERSION_HOST=https://${STORAGE_HOST}/api/storage/v2/query/records:batch
 STORAGE_RECORDS_BATCH_SIZE=20
 INDEXER_QUEUE_HOST=http://sqs.${AWS_REGION}.amazonaws.com/${AWS_ACCOUNT_ID}/${ENVIRONMENT}-osdu-indexer-queue
 
@@ -72,4 +63,4 @@ aws.elasticache.cluster.schema.expiration=60
 MAX_CACHE_VALUE_SIZE=1000
 
 ## AWS Lambda configuration
-aws.lambda.get-groups-function-name=${ENVIRONMENT}-os-entitlements-GroupsFunction-1DTM5841SUIFO
\ No newline at end of file
+aws.lambda.get-groups-function-name=${ENVIRONMENT}-os-entitlements-GroupsFunction
\ No newline at end of file
diff --git a/provider/indexer-ibm/azure-build.yml b/provider/indexer-ibm/azure-build.yml
new file mode 100644
index 0000000000000000000000000000000000000000..705d5f2168a7f1c6a077d21d9c69066ed673f303
--- /dev/null
+++ b/provider/indexer-ibm/azure-build.yml
@@ -0,0 +1,55 @@
+# Maven
+# Build your Java project and run tests with Apache Maven.
+# Add steps that analyze code, save build artifacts, deploy, and more:
+# https://docs.microsoft.com/azure/devops/pipelines/languages/java
+
+trigger:
+  branches:
+    include:
+    - master
+  paths:
+    exclude:
+      - README.md
+      - .gitignore
+
+pool:
+  name: Hosted Ubuntu 1604
+  demands: maven
+
+steps:
+- task: Maven@3
+  displayName: 'build, test, code coverage'
+  inputs:
+    mavenPomFile: 'pom.xml'
+    options: '--settings ./indexer-core/maven/settings.xml -DVSTS_FEED_TOKEN=$(VSTS_FEED_TOKEN)'
+    testResultsFiles: '**/*/TEST-*.xml'
+    codeCoverageToolOption: JaCoCo
+    goals: 'install'
+
+- task: Maven@3
+  displayName: 'build, test, code coverage'
+  inputs:
+    mavenPomFile: 'pom.xml'
+    options: '--settings ./provider/indexer-azure/maven/settings.xml -DVSTS_FEED_TOKEN=$(VSTS_FEED_TOKEN) -P indexer-azure'
+    testResultsFiles: '**/*/TEST-*.xml'
+    codeCoverageToolOption: JaCoCo
+    goals: 'package'
+
+- task: CopyFiles@2
+  displayName: 'Copy Azure artifacts for maven deploy to: $(build.artifactstagingdirectory)'
+  inputs:
+    SourceFolder:
+    Contents: |
+      pom.xml
+      provider/indexer-azure/maven/settings.xml
+      provider/indexer-azure/pom.xml
+      provider/indexer-azure/target/*-spring-boot.jar
+    TargetFolder: '$(build.artifactstagingdirectory)'
+    
+- task: PublishBuildArtifacts@1
+  displayName: 'Publish Artifact: drop'
+  inputs:
+    PathtoPublish: '$(build.artifactstagingdirectory)'
+    ArtifactName: 'drop'
+    publishLocation: 'Container'
+  condition: succeededOrFailed()
diff --git a/provider/indexer-ibm/pom.xml b/provider/indexer-ibm/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..09f44294727c8e808e2a0feb2297e2013f051ca4
--- /dev/null
+++ b/provider/indexer-ibm/pom.xml
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2019 IBM Corp. All Rights Reserved.
+
+ Licensed 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.opengroup.osdu.indexer</groupId>
+        <artifactId>indexer-service</artifactId>
+        <version>1.0.4-SNAPSHOT</version>
+        <relativePath>../..</relativePath>
+    </parent>
+
+    <artifactId>indexer-ibm</artifactId>
+    <name>indexer-ibm</name>
+    <description>Indexer Service IBM</description>
+    <packaging>jar</packaging>
+
+     <dependencies>
+
+        <!-- OSDU core service dependencies -->        
+        <dependency>
+            <groupId>org.opengroup.osdu.indexer</groupId>
+            <artifactId>indexer-core</artifactId>
+            <version>1.0.4-SNAPSHOT</version>
+        </dependency>
+        
+   		<dependency>
+   			<groupId>org.opengroup.osdu</groupId>
+   			<artifactId>os-core-lib-ibm</artifactId>
+   			<version>0.0.13-SNAPSHOT</version>
+   		</dependency>
+   		        
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security.oauth</groupId>
+            <artifactId>spring-security-oauth2</artifactId>
+            <version>2.3.6.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-jwt</artifactId>
+            <version>1.0.10.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-oauth2-client</artifactId>
+        </dependency>
+		<dependency>
+			<groupId>org.springframework.data</groupId>
+			<artifactId>spring-data-commons</artifactId>
+			<version>2.2.0.RELEASE</version>
+		</dependency>
+
+        <dependency>
+            <groupId>org.elasticsearch</groupId>
+            <artifactId>elasticsearch</artifactId>
+            <version>6.6.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch.client</groupId>
+            <artifactId>elasticsearch-rest-client</artifactId>
+            <version>6.6.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch.client</groupId>
+            <artifactId>elasticsearch-rest-high-level-client</artifactId>
+            <version>6.6.2</version>
+        </dependency>
+
+        <!-- Test Dependencies -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.8.1</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito2 -->
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito2</artifactId>
+            <version>2.0.2</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.powermock/powermock-module-junit4 -->
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <version>2.0.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>3.0.0</version>
+            <scope>test</scope>
+        </dependency>
+        <!-- <dependency>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>cobertura-maven-plugin</artifactId>
+            <version>2.7</version>
+            <scope>test</scope>
+        </dependency> -->
+
+    </dependencies>
+
+    <repositories>
+        <repository>
+            <id>${gitlab-server}</id>
+            <url>https://community.opengroup.org/api/v4/groups/17/-/packages/maven</url>
+        </repository>
+    </repositories>
+
+    <distributionManagement>
+        <repository>
+            <id>${gitlab-server}</id>
+            <url>https://community.opengroup.org/api/v4/projects/25/packages/maven</url>
+        </repository>
+        <snapshotRepository>
+            <id>${gitlab-server}</id>
+            <url>https://community.opengroup.org/api/v4/projects/25/packages/maven</url>
+        </snapshotRepository>
+    </distributionManagement>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                        <configuration>
+                            <classifier>spring-boot</classifier>
+                            <mainClass>
+                                org.opengroup.osdu.indexer.ibm.IndexerIBMApplication
+                            </mainClass>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.4.2</version>
+                <configuration>
+                  <useSystemClassLoader>false</useSystemClassLoader>
+                  <threadCount>1</threadCount>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+
+</project>
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/IndexerIBMApplication.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/IndexerIBMApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7ec63076f86c413b8636f353863fccd502877a9
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/IndexerIBMApplication.java
@@ -0,0 +1,43 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm;
+
+import org.opengroup.osdu.indexer.IndexerApplication;
+import org.opengroup.osdu.indexer.ServerletInitializer;
+import org.opengroup.osdu.indexer.service.ElasticSettingServiceImpl;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.FilterType;
+
+
+@SpringBootApplication
+@ComponentScan(
+		basePackages = {"org.opengroup.osdu"},
+		excludeFilters = {
+				@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value=IndexerApplication.class),
+				@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value=ServerletInitializer.class),
+				@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value=ElasticSettingServiceImpl.class),
+		}
+		)
+public class IndexerIBMApplication {
+	
+	public static void main(String[] args) throws Exception {
+				
+		SpringApplication.run(IndexerIBMApplication.class, args);
+	
+	}
+
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/AttributesCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/AttributesCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1d8f1162004471449950e1d2ca8c07dd403052d
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/AttributesCache.java
@@ -0,0 +1,55 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.cache;
+
+import org.opengroup.osdu.core.common.cache.VmCache;
+import org.opengroup.osdu.core.common.provider.interfaces.IAttributesCache;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+@Component
+public class AttributesCache implements IAttributesCache<String,Set> {
+
+    private VmCache<String, Set> cache;
+
+    public AttributesCache(@Value("${INDEX_CACHE_EXPIRATION}") final String INDEX_CACHE_EXPIRATION,
+                           @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) {
+        cache = new VmCache<>(Integer.parseInt(INDEX_CACHE_EXPIRATION) * 60,
+                Integer.parseInt(MAX_CACHE_VALUE_SIZE));
+    }
+
+    @Override
+    public void put(String key, Set value) {
+        this.cache.put(key, value);
+    }
+
+    @Override
+    public Set get(String key) {
+        return this.cache.get(key);
+    }
+
+    @Override
+    public void delete(String key) {
+        this.cache.delete(key);
+    }
+
+    @Override
+    public void clearAll() {
+        this.cache.clearAll();
+    }
+
+}
\ No newline at end of file
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/ElasticCredentialsCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/ElasticCredentialsCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..339051e58dacf7ee0a0c36b16fde5b2484ad0cab
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/ElasticCredentialsCache.java
@@ -0,0 +1,60 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.cache;
+
+import org.opengroup.osdu.core.common.cache.VmCache;
+import org.opengroup.osdu.core.common.model.search.ClusterSettings;
+import org.opengroup.osdu.core.common.provider.interfaces.IElasticCredentialsCache;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ElasticCredentialsCache implements IElasticCredentialsCache<String, ClusterSettings>, AutoCloseable {
+
+    private VmCache<String, ClusterSettings> cache;
+
+    public ElasticCredentialsCache(@Value("${ELASTIC_CACHE_EXPIRATION}") final String ELASTIC_CACHE_EXPIRATION,
+                                   @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) {
+    	
+    	cache = new VmCache<>(Integer.parseInt(ELASTIC_CACHE_EXPIRATION) * 60,
+                Integer.parseInt(MAX_CACHE_VALUE_SIZE));
+    
+    }
+
+    @Override
+    public void close() throws Exception {
+        //this.cache.close();
+    }
+
+    @Override
+    public void put(String s, ClusterSettings o) {
+        this.cache.put(s,o);
+    }
+
+    @Override
+    public ClusterSettings get(String s) {
+        return this.cache.get(s);
+    }
+
+    @Override
+    public void delete(String s) {
+        this.cache.delete(s);
+    }
+
+    @Override
+    public void clearAll() {
+        this.cache.clearAll();
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/IndexCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/IndexCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..019f5ae06cd4c3c560d23be11629b293622e8296
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/IndexCache.java
@@ -0,0 +1,51 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.cache;
+
+import org.opengroup.osdu.core.common.cache.VmCache;
+import org.opengroup.osdu.core.common.provider.interfaces.IIndexCache;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class IndexCache implements IIndexCache<String, Boolean> {
+    private VmCache<String, Boolean> cache;
+
+    public IndexCache(@Value("${INDEX_CACHE_EXPIRATION}") final String INDEX_CACHE_EXPIRATION,
+                      @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) {
+        cache = new VmCache<>(Integer.parseInt(INDEX_CACHE_EXPIRATION) * 60,
+                Integer.parseInt(MAX_CACHE_VALUE_SIZE));
+    }
+
+    @Override
+    public void put(String s, Boolean o) {
+        this.cache.put(s, o);
+    }
+
+    @Override
+    public Boolean get(String s) {
+        return this.cache.get(s);
+    }
+
+    @Override
+    public void delete(String s) {
+        this.cache.delete(s);
+    }
+
+    @Override
+    public void clearAll() {
+        this.cache.clearAll();
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/JwtCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/JwtCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c6a37d24cd8321e0c9d3c17031c9943ec7f7178
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/JwtCache.java
@@ -0,0 +1,53 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.cache;
+
+import org.opengroup.osdu.core.common.cache.VmCache;
+import org.opengroup.osdu.core.common.model.search.IdToken;
+import org.opengroup.osdu.core.common.provider.interfaces.IJwtCache;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class JwtCache implements IJwtCache<String, IdToken> {
+    private VmCache<String, IdToken> cache;
+
+    // Azure service account id_token can be requested only for 1 hr
+    private final static int EXPIRED_AFTER = 59;
+
+    public JwtCache(@Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) {
+        cache = new VmCache<>(EXPIRED_AFTER * 60, Integer.parseInt(MAX_CACHE_VALUE_SIZE));
+    }
+
+    @Override
+    public void put(String s, IdToken o) {
+        this.cache.put(s, o);
+    }
+
+    @Override
+    public IdToken get(String s) {
+        return this.cache.get(s);
+    }
+
+    @Override
+    public void delete(String s) {
+        this.cache.delete(s);
+    }
+
+    @Override
+    public void clearAll() {
+        this.cache.clearAll();
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/KindsCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/KindsCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ab87c833f5bd6f3a431e331aa262f394d9fa057
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/KindsCache.java
@@ -0,0 +1,53 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.cache;
+
+import org.opengroup.osdu.core.common.cache.VmCache;
+import org.opengroup.osdu.core.common.provider.interfaces.IKindsCache;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+@Component
+public class KindsCache implements IKindsCache<String, Set> {
+    private VmCache<String, Set> cache;
+
+    public KindsCache(@Value("${KINDS_CACHE_EXPIRATION}") final String KINDS_CACHE_EXPIRATION,
+                      @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) {
+        cache = new VmCache<>(Integer.parseInt(KINDS_CACHE_EXPIRATION) * 60,
+                Integer.parseInt(MAX_CACHE_VALUE_SIZE));
+    }
+
+    @Override
+    public void put(String s, Set o) {
+        this.cache.put(s, o);
+    }
+
+    @Override
+    public Set get(String s) {
+        return this.cache.get(s);
+    }
+
+    @Override
+    public void delete(String s) {
+        this.cache.delete(s);
+    }
+
+    @Override
+    public void clearAll() {
+        this.cache.clearAll();
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/SchemaCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/SchemaCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..83bef463a83f7cfe8c38014e8a927a6f3ca97dca
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/SchemaCache.java
@@ -0,0 +1,51 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.cache;
+
+import org.opengroup.osdu.core.common.cache.VmCache;
+import org.opengroup.osdu.indexer.provider.interfaces.ISchemaCache;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SchemaCache implements ISchemaCache<String, String> {
+    private VmCache<String, String> cache;
+
+    public SchemaCache(@Value("${SCHEMA_CACHE_EXPIRATION}") final String SCHEMA_CACHE_EXPIRATION,
+                       @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) {
+        cache = new VmCache<>(Integer.parseInt(SCHEMA_CACHE_EXPIRATION) * 60,
+                Integer.parseInt(MAX_CACHE_VALUE_SIZE));
+    }
+
+    @Override
+    public void put(String s, String o) {
+        this.cache.put(s, o);
+    }
+
+    @Override
+    public String get(String s) {
+        return this.cache.get(s);
+    }
+
+    @Override
+    public void delete(String s) {
+        this.cache.delete(s);
+    }
+
+    @Override
+    public void clearAll() {
+        this.cache.clearAll();
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantInfoService.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantInfoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7d8128726195b31c9905814f73845e26f33a5f7
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantInfoService.java
@@ -0,0 +1,39 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.di;
+
+import javax.inject.Inject;
+
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
+import org.opengroup.osdu.core.common.multitenancy.ITenantInfoService;
+import org.opengroup.osdu.core.common.provider.interfaces.ITenantFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+@RequestScope
+@Component
+public class TenantInfoService implements ITenantInfoService {
+    @Inject
+    private ITenantFactory tenantFactory;
+
+    @Inject
+    private DpsHeaders headers;
+
+    @Override
+    public TenantInfo getTenantInfo() {
+        return tenantFactory.getTenantInfo(headers.getPartitionId());
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/model/ElasticSettingSchema.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/model/ElasticSettingSchema.java
new file mode 100644
index 0000000000000000000000000000000000000000..e504cbddb3a3870becf68235b9eb36ebdd6b1b8a
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/model/ElasticSettingSchema.java
@@ -0,0 +1,40 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.model;
+
+import javax.validation.constraints.NotEmpty;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ElasticSettingSchema {
+
+    @NotEmpty
+    private String host;
+
+    @NotEmpty
+    private String port;
+
+    @NotEmpty
+    private String usernameAndPassword;
+
+    @NotEmpty
+    private boolean isHttps;  
+
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticRepositoryIBM.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticRepositoryIBM.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a7b0835612c4c5b8ea62379015b75f5c42eff78
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticRepositoryIBM.java
@@ -0,0 +1,81 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.persistence;
+
+import javax.inject.Inject;
+
+import org.apache.http.HttpStatus;
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.core.common.model.search.ClusterSettings;
+import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
+import org.opengroup.osdu.core.common.provider.interfaces.IElasticRepository;
+import org.opengroup.osdu.core.common.search.Preconditions;
+import org.opengroup.osdu.indexer.ibm.model.ElasticSettingSchema;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ElasticRepositoryIBM implements IElasticRepository {
+
+    @Value("${ELASTIC_DATASTORE_KIND}")
+    private String ELASTIC_DATASTORE_KIND;
+
+    @Value("${ELASTIC_DATASTORE_ID}")
+    private String ELASTIC_DATASTORE_ID;
+
+    @Inject
+    private ISchemaRepository schemaRepository;
+
+    @Value("${ELASTIC_HOST}")
+    private String ELASTIC_HOST;
+    @Value("${ELASTIC_PORT:443}")
+    private String ELASTIC_PORT;
+    @Value("${ELASTIC_USER_PASSWORD}")
+    private String ELASTIC_USER_PASSWORD;
+
+    @Inject
+    private JaxRsDpsLog log;
+    
+    @Override
+    public ClusterSettings getElasticClusterSettings(TenantInfo tenantInfo) {
+
+        if(tenantInfo == null)
+            throw  new AppException(HttpStatus.SC_NOT_FOUND, "TenantInfo is null", "");
+
+        String settingId = tenantInfo.getName().concat("-").concat(ELASTIC_DATASTORE_ID);
+        ElasticSettingSchema schema = this.schemaRepository.get(settingId);
+
+        if (schema == null) {
+        	// if creds not in the db, use default from env
+        	log.warning(settingId + " credentials not found at database.");
+        	return new ClusterSettings(ELASTIC_HOST, Integer.parseInt(ELASTIC_PORT), ELASTIC_USER_PASSWORD, true, false);
+            //throw new AppException(HttpStatus.SC_NOT_FOUND, "Elastic setting not found", "The requested cluster setting was not found in CosmosDB.", String.format("Elastic setting with key: '%s' does not exist in CosmostDB.", ELASTIC_DATASTORE_KIND));
+        }
+
+        String host = schema.getHost();
+        String portString = schema.getPort();
+        String usernameAndPassword = schema.getUsernameAndPassword();
+
+        Preconditions.checkNotNullOrEmpty(host, "host cannot be null");
+        Preconditions.checkNotNullOrEmpty(portString, "port cannot be null");
+        Preconditions.checkNotNullOrEmpty(usernameAndPassword, "configuration cannot be null");
+
+        int port = Integer.parseInt(portString);
+
+        return new ClusterSettings(host, port, usernameAndPassword, schema.isHttps(), schema.isHttps());
+        
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingSchemaRepositoryImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingSchemaRepositoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..cde9a4b88acca4c11180810143b61735e472bafa
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingSchemaRepositoryImpl.java
@@ -0,0 +1,88 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.persistence;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.opengroup.osdu.core.ibm.auth.ServiceCredentials;
+import org.opengroup.osdu.core.ibm.cloudant.IBMCloudantClientFactory;
+import org.opengroup.osdu.indexer.ibm.model.ElasticSettingSchema;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Repository;
+
+import com.cloudant.client.api.Database;
+
+
+@Repository
+public class ElasticSettingSchemaRepositoryImpl implements ISchemaRepository {
+
+	@Value("${ibm.db.url}") 
+	private String dbUrl;
+	@Value("${ibm.db.apikey:#{null}}")
+	private String apiKey;
+	@Value("${ibm.db.user:#{null}}")
+	private String dbUser;
+	@Value("${ibm.db.password:#{null}}")
+	private String dbPassword;
+	
+	@Value("${ibm.env.prefix:local-dev}")
+	private String dbNamePrefix;
+	
+	private IBMCloudantClientFactory cloudantFactory;
+	private Database db;
+	
+	@Inject
+	private JaxRsDpsLog logger;
+
+    @PostConstruct
+    public void init(){
+        try {
+        	if (apiKey != null) {
+    			cloudantFactory = new IBMCloudantClientFactory(new ServiceCredentials(dbUrl, apiKey));
+    		} else {
+    			cloudantFactory = new IBMCloudantClientFactory(new ServiceCredentials(dbUrl, dbUser, dbPassword));
+    		}        	db = cloudantFactory.getDatabase(dbNamePrefix, "SearchSettings");
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+    }
+
+	@Override
+	public void add(ElasticSettingSchema schema, String id) {
+	  ElasticSettingsDoc sd = new ElasticSettingsDoc();
+	  sd.setId(id);
+	  sd.setSettingSchema(schema);
+	  db.save(sd);
+	}
+
+    @Override
+    public ElasticSettingSchema get(String id) {
+    	if (db.contains(id)) {
+    		ElasticSettingsDoc sd = db.find(ElasticSettingsDoc.class, id);
+    		ElasticSettingSchema newSchema = new ElasticSettingSchema();
+    		newSchema.setPort(sd.getSettingSchema().getPort());
+    		newSchema.setHost(sd.getSettingSchema().getHost());
+    		newSchema.setUsernameAndPassword(sd.getSettingSchema().getUsernameAndPassword());
+    		newSchema.setHttps(sd.getSettingSchema().isHttps());
+    		return newSchema;
+		} else {
+			logger.error(ElasticSettingsDoc.class + " with id " + id + " was not found in the database.");
+			return null;
+		}
+    }
+
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingsDoc.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingsDoc.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe417507f5f8a76e05eb8a9c3e49ebf867a254a2
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingsDoc.java
@@ -0,0 +1,40 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.persistence;
+
+import org.opengroup.osdu.indexer.ibm.model.ElasticSettingSchema;
+import org.springframework.data.annotation.Id;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ElasticSettingsDoc {
+	
+	public static final String DB_NAME =  "SearchSettings";   //collection name
+	
+    @Id
+    private String _id;
+    private String _rev;
+    private ElasticSettingSchema settingSchema;
+    
+	public void setId(String id) {
+		this._id = id;		
+	}
+	
+}
\ No newline at end of file
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ISchemaRepository.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ISchemaRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..114aa4fa554d1f76f91edd7c4f22f61d2996058f
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ISchemaRepository.java
@@ -0,0 +1,29 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.persistence;
+
+import org.opengroup.osdu.indexer.ibm.model.ElasticSettingSchema;
+
+public interface ISchemaRepository {
+    String SCHEMA_KIND = "IndexerSchema";
+
+    String SCHEMA = "schema";
+    String KIND = "KIND";
+
+    void add(ElasticSettingSchema schema, String id);
+
+    ElasticSettingSchema get(String id);
+}
+
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/publish/PublisherImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/publish/PublisherImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..809568c8c082923ee1e3e03bf3756e772a307c91
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/publish/PublisherImpl.java
@@ -0,0 +1,102 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.publish;
+
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.elasticsearch.common.Strings;
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.model.indexer.JobStatus;
+import org.opengroup.osdu.core.common.model.indexer.RecordStatus;
+import org.opengroup.osdu.core.common.model.search.RecordChangedMessages;
+import org.opengroup.osdu.core.ibm.messagebus.IMessageFactory;
+import org.opengroup.osdu.indexer.provider.interfaces.IPublisher;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+
+@Component
+@RequestScope
+public class PublisherImpl implements IPublisher {
+
+	@Inject
+	IMessageFactory mq;
+	
+    @Inject
+    private JaxRsDpsLog logger;
+
+
+    @Override
+    public void publishStatusChangedTagsToTopic(DpsHeaders headers, JobStatus indexerBatchStatus) throws Exception {
+
+        String tenant = headers.getPartitionId();
+        if (Strings.isNullOrEmpty(tenant))
+            tenant = headers.getAccountId();
+        
+        Map<String, String> message = new HashMap<>();
+        message.put(tenant, headers.getPartitionIdWithFallbackToAccountId());
+        headers.addCorrelationIdIfMissing();
+        message.put(DpsHeaders.CORRELATION_ID, headers.getCorrelationId());
+    
+        RecordChangedMessages recordChangedMessages = getRecordChangedMessage(headers, indexerBatchStatus);
+        message.put("data", recordChangedMessages.toString());
+
+        try {
+            logger.info("Indexer publishes message " + headers.getCorrelationId());
+            mq.sendMessage(IMessageFactory.INDEXER_QUEUE_NAME, message.toString());
+        }
+        catch (Exception e) {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+    private RecordChangedMessages getRecordChangedMessage(DpsHeaders headers, JobStatus indexerBatchStatus) {
+
+        Gson gson = new GsonBuilder().create();
+        Map<String, String> attributesMap = new HashMap<>();
+        Type listType = new TypeToken<List<RecordStatus>>() {}.getType();
+
+        JsonElement statusChangedTagsJson = gson.toJsonTree(indexerBatchStatus.getStatusesList(), listType);
+        String statusChangedTagsData = (statusChangedTagsJson.toString());
+
+        String tenant = headers.getPartitionId();
+        // This code it to provide backward compatibility to slb-account-id
+        if (!Strings.isNullOrEmpty(tenant)) {
+            attributesMap.put(DpsHeaders.DATA_PARTITION_ID, headers.getPartitionIdWithFallbackToAccountId());
+        } else {
+            attributesMap.put(DpsHeaders.ACCOUNT_ID, headers.getPartitionIdWithFallbackToAccountId());
+        }
+        headers.addCorrelationIdIfMissing();
+        attributesMap.put(DpsHeaders.CORRELATION_ID, headers.getCorrelationId());
+
+        RecordChangedMessages recordChangedMessages = new RecordChangedMessages();
+        // statusChangedTagsData is not ByteString but String
+        recordChangedMessages.setData(statusChangedTagsData);
+        recordChangedMessages.setAttributes(attributesMap);
+
+        return recordChangedMessages;
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/SecurityConfig.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/SecurityConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..2cf274beac0c1dd57bb2926fcd2b6fbe98fda4e4
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/SecurityConfig.java
@@ -0,0 +1,42 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.security;
+
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+ 
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                .csrf().disable()
+                .authorizeRequests()
+                .antMatchers("/", "/index.html",
+                        "/index-worker", "/_dps/task-handlers", "/_dps/task-handlers/**",
+                        "/reindex",
+                        "/v2/api-docs",
+                        "/swagger-resources/**",
+                        "/configuration/security",
+                        "/swagger",
+                        "/swagger-ui.html",
+                        "/webjars/**").permitAll()
+                .anyRequest().anonymous();
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/WhoamiController.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/WhoamiController.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd8b395e89b135ff31d2e81671463213521b31c4
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/WhoamiController.java
@@ -0,0 +1,36 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.security;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class WhoamiController {
+	@RequestMapping(value = { "/", "/whoami" })
+	@ResponseBody
+	public String whoami() {
+		final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+
+		String userName = auth.getName();
+		String roles = String.valueOf(auth.getAuthorities());
+		String details = String.valueOf(auth.getPrincipal());
+
+		return "user: " + userName + "<BR>" + "roles: " + roles + "<BR>" + "details: " + details;
+	}
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/DpsHeadersIBMQueue.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/DpsHeadersIBMQueue.java
new file mode 100644
index 0000000000000000000000000000000000000000..3fa378301ed518deb8c470dd853f7a3308f6ae6b
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/DpsHeadersIBMQueue.java
@@ -0,0 +1,57 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.util;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+@Component
+@RequestScope
+@Primary
+public class DpsHeadersIBMQueue extends DpsHeaders {
+
+    @Value("${indexer.queue.key}")
+    private String queueKey;
+
+    //TODO this should be moved to Azure client-lib
+    public static final String INDEXER_QUEUE_KEY = "x-functions-key";
+    
+    @Inject
+    public DpsHeadersIBMQueue(HttpServletRequest request) {
+
+        Map<String, String> headers = Collections
+                .list(request.getHeaderNames())
+                .stream()
+                .collect(Collectors.toMap(h -> h, request::getHeader));
+
+        headers.put(INDEXER_QUEUE_KEY,queueKey);
+       
+        this.addFromMap(headers);
+
+        // Add Correlation ID if missing
+        this.addCorrelationIdIfMissing();
+
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/IHeadersInfo.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/IHeadersInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..722e4c9f59c551cc712f71764377e13ae3252a60
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/IHeadersInfo.java
@@ -0,0 +1,35 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.util;
+
+import java.util.Map;
+
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+
+public interface IHeadersInfo {
+
+	DpsHeaders getHeaders();
+
+	String getUser();
+
+	String getPartitionId();
+
+	String getPrimaryPartitionId();
+
+	Map<String, String> getHeadersMap();
+
+	DpsHeaders getCoreServiceHeaders(Map<String, String> input);
+
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/KeyCloakProvider.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/KeyCloakProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..04d134da767d8a0020feb230a4e1f0da30c620ad
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/KeyCloakProvider.java
@@ -0,0 +1,110 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.util;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+
+@Component
+public class KeyCloakProvider {
+	
+	@Value("${ibm.keycloak.endpoint_url}")
+    private String url;
+	
+	@Value("${ibm.keycloak.realm}")
+    private String realm ;
+	
+	@Value("${ibm.keycloak.grant_type:password}")
+    private String grantType;
+	
+	@Value("${ibm.keycloak.scope:openid}")
+    private String scope;
+    
+    @Value("${ibm.keycloak.client_id}")
+    private String clientId;
+    
+    @Value("${ibm.keycloak.client_secret}")
+    private String clientSecret;
+    
+	public String getToken(String user, String password) throws IOException {
+		String endpoint = String.format("https://%s/auth/realms/%s/protocol/openid-connect/token", url, realm);
+        URL url = new URL(endpoint);
+        HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
+        con.setRequestMethod("POST");
+        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put("grant_type", grantType);
+        parameters.put("client_id", clientId);
+        parameters.put("client_secret", clientSecret);
+        parameters.put("username", user);
+        parameters.put("password", password);
+        parameters.put("scope", scope);
+
+        con.setDoOutput(true);
+        DataOutputStream out = new DataOutputStream(con.getOutputStream());
+        out.writeBytes(getParamsString(parameters));
+        out.flush();
+        out.close();
+
+        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+        String inputLine;
+        StringBuffer content = new StringBuffer();
+        while ((inputLine = in.readLine()) != null) {
+            content.append(inputLine);
+        }
+        in.close();
+
+        con.disconnect();
+
+        Gson gson = new Gson();
+        JsonObject jobj = gson.fromJson(content.toString(), JsonObject.class);
+        String token = jobj.get("access_token").getAsString();
+        return token;
+	}
+	
+	private static String getParamsString(Map<String, String> params)
+            throws UnsupportedEncodingException {
+        StringBuilder result = new StringBuilder();
+
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
+            result.append("=");
+            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
+            result.append("&");
+        }
+
+        String resultString = result.toString();
+        return resultString.length() > 0
+                ? resultString.substring(0, resultString.length() - 1)
+                : resultString;
+    }
+
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..12fd218f826bc9ea1177e433d305cbc951571605
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java
@@ -0,0 +1,123 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.util;
+
+import static org.opengroup.osdu.core.common.model.http.DpsHeaders.AUTHORIZATION;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.http.HttpStatus;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.model.search.DeploymentEnvironment;
+import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
+import org.opengroup.osdu.core.common.provider.interfaces.IRequestInfo;
+import org.opengroup.osdu.core.common.search.Preconditions;
+import org.opengroup.osdu.core.common.util.IServiceAccountJwtClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import com.google.common.base.Strings;
+
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+
+@Primary
+
+@Component
+//@RequestScope
+public class RequestInfoImpl implements IRequestInfo {
+
+    @Inject
+    private DpsHeaders headersMap;
+    
+    @Inject
+    private JaxRsDpsLog logger;
+
+    @Inject
+    private IServiceAccountJwtClient serviceAccountJwtClient;
+
+    @Inject
+    private TenantInfo tenantInfo;
+
+    @Value("${DEPLOYMENT_ENVIRONMENT}")
+    private String DEPLOYMENT_ENVIRONMENT;
+
+
+    @Override
+    public DpsHeaders getHeaders() {
+        if (headersMap == null) {
+            logger.warning("Headers Map DpsHeaders is null");
+            // throw to prevent null reference exception below
+            throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Invalid Headers", "Headers Map DpsHeaders is null");
+        }
+        DpsHeaders headers = this.getCoreServiceHeaders(headersMap.getHeaders());
+        return headers;
+    }
+
+    @Override
+    public String getPartitionId() {
+        return getHeaders().getPartitionIdWithFallbackToAccountId();
+    }
+
+    @Override
+    public Map<String, String> getHeadersMap() {
+        return getHeaders().getHeaders();
+    }
+
+    @Override
+    public Map<String, String> getHeadersMapWithDwdAuthZ() {
+        return getHeadersWithDwdAuthZ().getHeaders();
+    }
+
+    @Override
+    public DpsHeaders getHeadersWithDwdAuthZ() {
+        this.headersMap.put(AUTHORIZATION, this.checkOrGetAuthorizationHeader());
+        return getHeaders();
+    }
+
+    @Override
+    public boolean isCronRequest() { return false;}
+
+    @Override
+    public boolean isTaskQueueRequest() {
+        //if (!this.dpsHeaders.getHeaders().containsKey(INDEXER_QUEUE_KEY)) return false;
+
+//        String queueId = this.headersInfo.getHeadersMap().get(AppEngineHeaders.TASK_QUEUE_NAME);
+//        return queueId.endsWith(Constants.INDEXER_QUEUE_IDENTIFIER);
+        return false;
+    }
+
+    public String checkOrGetAuthorizationHeader() {
+        if (DeploymentEnvironment.valueOf(DEPLOYMENT_ENVIRONMENT) == DeploymentEnvironment.LOCAL) {
+            String authHeader = this.headersMap.getAuthorization();
+            if (Strings.isNullOrEmpty(authHeader)) {
+            	logger.error(HttpStatus.SC_UNAUTHORIZED + " : Invalid authorization header");
+                throw new AppException(HttpStatus.SC_UNAUTHORIZED, "Invalid authorization header", "Authorization token cannot be empty");
+            }
+            return "Bearer " + authHeader;
+        } else {
+            return "Bearer " + this.serviceAccountJwtClient.getIdToken(tenantInfo.getName());
+        }
+    }
+    
+    private DpsHeaders getCoreServiceHeaders(Map<String, String> input) {
+        Preconditions.checkNotNull(input, "input headers cannot be null");
+        DpsHeaders output = DpsHeaders.createFromMap(input);
+        return output;
+    }
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/ServiceAccountJwtClientImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/ServiceAccountJwtClientImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..adb2e3cd2fd6236b1e050c9c21d775045e137ef7
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/ServiceAccountJwtClientImpl.java
@@ -0,0 +1,83 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.util;
+
+import javax.inject.Inject;
+
+import org.apache.http.HttpStatus;
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.provider.interfaces.IJwtCache;
+import org.opengroup.osdu.core.common.provider.interfaces.ITenantFactory;
+import org.opengroup.osdu.core.common.util.IServiceAccountJwtClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+@Component
+@RequestScope
+public class ServiceAccountJwtClientImpl implements IServiceAccountJwtClient {
+	
+    @Inject
+    private ITenantFactory tenantInfoServiceProvider;
+    
+    @Inject
+    private DpsHeaders dpsHeaders;
+    
+    @Inject
+    private IJwtCache cacheService;
+    
+    @Inject
+    private JaxRsDpsLog logger;
+    
+    @Inject
+    private KeyCloakProvider keyCloack;
+    
+    @Value("${ibm.keycloak.useremail}")
+    private String userEmail;
+    
+    @Value("${ibm.keycloak.username}")
+    private String userName;
+    
+    @Value("${ibm.keycloak.password}")
+    private String userPassword;
+	
+    @Override
+    public String getIdToken(String tenantName) {
+        /*this.log.info("Tenant name received for auth token is: " + tenantName);
+        TenantInfo tenant = this.tenantInfoServiceProvider.getTenantInfo(tenantName);
+        if (tenant == null) {
+            this.log.error("Invalid tenant name receiving from azure");
+            throw new AppException(HttpStatus.SC_BAD_REQUEST, "Invalid tenant Name", "Invalid tenant Name from azure");
+        }*/
+        String ACCESS_TOKEN = "";
+        try {
+
+            this.dpsHeaders.put(DpsHeaders.USER_EMAIL, userEmail);
+
+            ACCESS_TOKEN = keyCloack.getToken(userName, userPassword);
+            
+        } catch (AppException e) {
+            throw e;
+        } catch (Exception e) {
+        	logger.error("Error generating token", e);
+            throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Persistence error", "Error generating token", e);
+        }
+
+        return ACCESS_TOKEN;
+    }
+    
+}
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/service/ElasticSettingServiceIBMImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/service/ElasticSettingServiceIBMImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..da352d885fa21eae88ce99f1ae83c680c8dd2bdd
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/service/ElasticSettingServiceIBMImpl.java
@@ -0,0 +1,61 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.indexer.service;
+
+import org.apache.http.HttpStatus;
+import org.opengroup.osdu.core.common.model.search.ClusterSettings;
+import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.opengroup.osdu.core.common.provider.interfaces.IElasticRepository;
+import org.opengroup.osdu.core.common.provider.interfaces.IElasticCredentialsCache;
+import org.opengroup.osdu.core.common.model.indexer.IElasticSettingService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import javax.inject.Inject;
+
+@Service
+public class ElasticSettingServiceIBMImpl implements IElasticSettingService {
+
+    @Inject
+    private TenantInfo tenantInfo;
+
+    @Inject
+    private IElasticRepository elasticRepository;
+    @Inject
+    private IElasticCredentialsCache elasticCredentialCache;
+    @Inject
+    private JaxRsDpsLog logger;
+
+    @Override
+    public ClusterSettings getElasticClusterInformation() {
+
+        String cacheKey = String.format("%s-%s", "ibm", tenantInfo.getName());
+        ClusterSettings clusterInfo = (ClusterSettings) this.elasticCredentialCache.get(cacheKey);
+        if (clusterInfo != null) {
+            return clusterInfo;
+        }
+
+        logger.warning(String.format("elastic-credential cache missed for tenant: %s", tenantInfo.getName()));
+
+        clusterInfo = this.elasticRepository.getElasticClusterSettings(tenantInfo);
+        if (clusterInfo == null) {
+            throw new AppException(HttpStatus.SC_NOT_FOUND, "Tenant not found", "No information about the given tenant was found");
+        }
+
+        this.elasticCredentialCache.put(cacheKey, clusterInfo);
+        return clusterInfo;
+    }
+}
diff --git a/provider/indexer-ibm/src/main/resources/application.properties b/provider/indexer-ibm/src/main/resources/application.properties
new file mode 100644
index 0000000000000000000000000000000000000000..242a77daef41824a0a7d59a4076135893f73d7ce
--- /dev/null
+++ b/provider/indexer-ibm/src/main/resources/application.properties
@@ -0,0 +1,64 @@
+server.servlet.contextPath=/api/indexer/v2/
+
+LOG_PREFIX=indexer
+
+logging.level.org.springframework.web=DEBUG
+server.port=8080
+JAVA_HEAP_OPTS=-Xms4096M -Xmx4096M
+JAVA_GC_OPTS=-XX:+UseG1GC -XX:+UseStringDeduplication -XX:InitiatingHeapOccupancyPercent=45
+
+DEFAULT_DATA_COUNTRY=US
+
+AUTHORIZE_API=https://entitlements:8080/api/entitlements/v1
+AUTHORIZE_API_KEY=tobeupdated
+LEGALTAG_API=http://legal:8080/api/legal/v1
+
+DEPLOYMENT_ENVIRONMENT=CLOUD
+
+SCHEMA_CACHE_EXPIRATION=60
+INDEX_CACHE_EXPIRATION=60
+ELASTIC_CACHE_EXPIRATION=1440
+CURSOR_CACHE_EXPIRATION=60
+KINDS_CACHE_EXPIRATION=2880
+ATTRIBUTES_CACHE_EXPIRATION=2880
+MAX_CACHE_VALUE_SIZE=1000
+KINDS_REDIS_DATABASE=1
+CRON_INDEX_CLEANUP_THRESHOLD_DAYS=3
+CRON_EMPTY_INDEX_CLEANUP_THRESHOLD_DAYS=7
+
+storage_service_url=http://localhost:8082
+#storage_service_url=https://os-storage-ibm-osdu-r2.osduadev-a1c3eaf78a86806e299f5f3f207556f0-0000.us-south.containers.appdomain.cloud
+STORAGE_SCHEMA_HOST=${storage_service_url}/api/storage/v2/schemas
+STORAGE_QUERY_RECORD_HOST=${storage_service_url}/api/storage/v2/query/records
+STORAGE_QUERY_RECORD_FOR_CONVERSION_HOST=${storage_service_url}/api/storage/v2/query/records:batch
+STORAGE_RECORDS_BATCH_SIZE=20
+
+ibm.db.url=REPLACE_ME
+## use apikey or user/password
+ibm.db.user=REPLACE_ME
+ibm.db.password=REPLACE_ME
+
+ibm.tenant.db.url=${ibm.db.url}
+## use apikey or user/password
+ibm.tenant.db.user=${ibm.db.user}
+ibm.tenant.db.password=${ibm.db.password}
+
+ibm.rabbitmq.uri=REPLACE_ME
+
+ibm.keycloak.endpoint_url=keycloak-host.com
+ibm.keycloak.realm=OSDU
+ibm.keycloak.client_id=osdu-login
+ibm.keycloak.client_secret=REPLACE_ME
+ibm.keycloak.username=REPLACE_ME
+ibm.keycloak.password=REPLACE_ME
+ibm.keycloak.useremail=osdu-user@osdu.opengroup.org
+
+#Indexer-Queue-header
+indexer.queue.key=abcd
+
+ELASTIC_DATASTORE_KIND=SearchSettings
+ELASTIC_DATASTORE_ID=indexer-service
+
+ELASTIC_HOST=elasticsearch.com
+ELASTIC_PORT=443
+ELASTIC_USER_PASSWORD=REPLACE_ME:REPLACE_ME
diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/CronServiceImplTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/CronServiceImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a51f2d8bf2bb95f8afd53db6cac16d81faa8b17f
--- /dev/null
+++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/CronServiceImplTest.java
@@ -0,0 +1,141 @@
+// Copyright 2017-2019, Schlumberger
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.service;
+
+import com.google.common.collect.Lists;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.opengroup.osdu.indexer.service.CronServiceImpl;
+import org.opengroup.osdu.core.common.model.search.IndexInfo;
+import org.opengroup.osdu.core.common.provider.interfaces.IRequestInfo;
+import org.opengroup.osdu.core.common.search.IndicesService;
+import org.opengroup.osdu.core.common.search.Config;
+import org.opengroup.osdu.indexer.util.ElasticClientHandler;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+
+
+@Ignore
+@RunWith(SpringRunner.class)
+@PrepareForTest({RestHighLevelClient.class})
+public class CronServiceImplTest {
+
+    @Mock
+    private RestHighLevelClient restHighLevelClient;
+    @Mock
+    private IndicesService indicesService;
+    @Mock
+    private ElasticClientHandler elasticClientHandler;
+    @Mock
+    private IRequestInfo requestInfo;
+    @Mock
+    private JaxRsDpsLog log;
+    @InjectMocks
+    private CronServiceImpl sut;
+
+    @InjectMocks
+    private DpsHeaders dpsHeaders;
+
+    @Before
+    public void setup() {
+        mockStatic(Config.class);
+
+        when(this.requestInfo.getHeaders()).thenReturn(dpsHeaders);
+
+        when(Config.getIndexCleanupThresholdDays()).thenReturn(3);
+        when(Config.getEmptyIndexCleanupThresholdDays()).thenReturn(7);
+    }
+
+    @Test
+    public void run_cleanup_when_cron_job_runs_with_correct_pattern() throws Exception {
+        final String indexPattern = "tenant1-index-*";
+
+        IndexInfo info = IndexInfo.builder().name("tenant1-index-1.0.0").documentCount("10").creationDate(Long.toString(Instant.now().minus(4, ChronoUnit.DAYS).toEpochMilli())).build();
+
+        when(this.requestInfo.getPartitionId()).thenReturn("tenant1");
+        when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient);
+        when(this.indicesService.getIndexInfo(this.restHighLevelClient, indexPattern)).thenReturn(Lists.newArrayList(info));
+
+        this.sut.cleanupIndices(indexPattern);
+
+        verify(this.indicesService, times(1)).deleteIndex(restHighLevelClient, "tenant1-index-1.0.0");
+        verify(this.indicesService, times(1)).getIndexInfo(restHighLevelClient, indexPattern);
+    }
+
+    @Test(expected = IOException.class)
+    public void run_cleanup_when_cron_job_runs_with_wrong_pattern() throws Exception {
+        IOException exception = new IOException("blah");
+        when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient);
+        when(this.indicesService.getIndexInfo(this.restHighLevelClient, "tenant1-test-*")).thenThrow(exception);
+
+        this.sut.cleanupIndices("tenant1-test-*");
+
+        verify(this.indicesService, times(0)).deleteIndex(any(), any());
+    }
+
+    @Test
+    public void run_cleanup_when_backend_does_not_have_empty_stale_indices() throws Exception {
+        IndexInfo info = IndexInfo.builder().name("tenant1-index-1.0.0").documentCount("10").creationDate(Long.toString(Instant.now().minus(8, ChronoUnit.DAYS).toEpochMilli())).build();
+
+        when(this.requestInfo.getPartitionId()).thenReturn("tenant1");
+        when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient);
+        when(this.indicesService.getIndexInfo(this.restHighLevelClient, null)).thenReturn(Lists.newArrayList(info));
+
+        this.sut.cleanupEmptyStaleIndices();
+
+        verify(this.indicesService, times(0)).deleteIndex(restHighLevelClient, null);
+        verify(this.indicesService, times(1)).getIndexInfo(restHighLevelClient, null);
+    }
+
+    @Test
+    public void run_cleanup_when_backend_have_empty_stale_indices() throws Exception {
+        IndexInfo info = IndexInfo.builder().name("tenant1-index-1.0.0").documentCount("0").creationDate(Long.toString(Instant.now().minus(8, ChronoUnit.DAYS).toEpochMilli())).build();
+
+        when(this.requestInfo.getPartitionId()).thenReturn("tenant1");
+        when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient);
+        when(this.indicesService.getIndexInfo(this.restHighLevelClient, null)).thenReturn(Lists.newArrayList(info));
+
+        this.sut.cleanupEmptyStaleIndices();
+
+        verify(this.indicesService, times(1)).deleteIndex(restHighLevelClient, "tenant1-index-1.0.0");
+        verify(this.indicesService, times(1)).getIndexInfo(restHighLevelClient, null);
+    }
+
+    @Test(expected = IOException.class)
+    public void run_cleanup_when_backend_throws_exception() throws Exception {
+        IOException exception = new IOException("blah");
+        when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient);
+        when(this.indicesService.getIndexInfo(this.restHighLevelClient, null)).thenThrow(exception);
+
+        this.sut.cleanupEmptyStaleIndices();
+
+        verify(this.indicesService, times(0)).deleteIndex(any(), any());
+    }
+}
\ No newline at end of file
diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexCopyServiceImplTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexCopyServiceImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b37cddf112a713c6e08166cce591a0ff5cb923a
--- /dev/null
+++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexCopyServiceImplTest.java
@@ -0,0 +1,191 @@
+// Copyright 2017-2019, Schlumberger
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.service;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.apache.http.HttpEntity;
+import org.apache.http.util.EntityUtils;
+import org.elasticsearch.client.Request;
+import org.elasticsearch.client.Response;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.indexer.logging.AuditLogger;
+import org.opengroup.osdu.indexer.service.IndexCopyServiceImpl;
+import org.opengroup.osdu.indexer.service.IndexerMappingService;
+import org.opengroup.osdu.core.common.model.search.ClusterSettings;
+import org.opengroup.osdu.core.common.provider.interfaces.IRequestInfo;
+import org.opengroup.osdu.core.common.model.indexer.IElasticSettingService;
+import org.opengroup.osdu.core.common.search.IndicesService;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.indexer.util.ElasticClientHandler;
+import org.opengroup.osdu.core.common.search.ElasticIndexNameResolver;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+@RunWith(SpringRunner.class)
+@PrepareForTest({RestHighLevelClient.class, Response.class, RestClient.class, HttpEntity.class, EntityUtils.class})
+public class IndexCopyServiceImplTest {
+    private final String correlationId = UUID.randomUUID().toString();
+
+    @Mock
+    private HttpEntity httpEntity;
+    @Mock
+    private HttpEntity httpEntityRequest;
+    @Mock
+    private IRequestInfo requestInfo;
+    @Mock
+    private DpsHeaders dpsHeaders;
+    @Mock
+    private RestClient restClient;
+    @Mock
+    private RestHighLevelClient restHighLevelClient;
+    @Mock
+    private IndicesService indicesService;
+    @Mock
+    private IndexerMappingService mappingService;
+    @Mock
+    private ElasticClientHandler elasticClientHandler;
+    @Mock
+    private ElasticIndexNameResolver elasticIndexNameResolver;
+    @Mock
+    private Response response;
+    @Mock
+    private IElasticSettingService elasticSettingService;
+    @Mock
+    private AuditLogger auditLogger;
+    @Mock
+    private Map<String, String> httpHeaders;
+    @InjectMocks
+    private IndexCopyServiceImpl sut;
+
+    private ClusterSettings commonCluster;
+
+    private Map<String, Object> correctMap;
+
+    @Before
+    public void setup() {
+
+        commonCluster = ClusterSettings.builder().host("commonhost").port(8080).userNameAndPassword("username:pwd").build();
+
+        httpHeaders = new HashMap<>();
+        httpHeaders.put(DpsHeaders.AUTHORIZATION, "testAuth");
+        httpHeaders.put(DpsHeaders.CORRELATION_ID, correlationId);
+        when(requestInfo.getHeadersMapWithDwdAuthZ()).thenReturn(httpHeaders);
+        when(response.getEntity()).thenReturn(httpEntity);
+
+        Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
+        String afterFormat = "{\"properties\":{\"id\":{\"type\":\"keyword\"}}}";
+        correctMap = new Gson().fromJson(afterFormat, mapType);
+
+        restHighLevelClient = mock(RestHighLevelClient.class);
+
+    }
+
+    @Test(expected = IOException.class)
+    public void should_throwIOException_when_indexMappingNotFound() throws Exception {
+        IOException exception = new IOException("Fail to get mapping for the given index from common cluster.");
+
+        when(this.mappingService.getIndexMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenThrow(exception);
+
+        this.sut.copyIndex("common:metadata:entity:1.0.0");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void should_throwIllegalArgExceptionCopyIndexRequest_copyIndexTest() {
+        try {
+            this.sut.copyIndex(null);
+        } catch (IOException e) {
+            fail("Should not throw IOException but illegalArgumentException.");
+        }
+    }
+
+    @Test
+    public void should_returnIndexMapping_getIndexMappingFromCommonClustertest() {
+        String mappingJson = "{\"common-metadata-entity-1.0.0\":{\"mappings\":{\"entity\":{\"properties\":{\"id\":{\"type\":\"keyword\"}}}}}}";
+        when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+        try {
+            when(this.mappingService.getIndexMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(mappingJson);
+            Map<String, Object> resultMap = this.sut.getIndexMappingsFromCommonCluster("test", "test");
+            Assert.assertEquals(resultMap, correctMap);
+        } catch (Exception ignored) {
+        }
+    }
+
+    @Test
+    public void should_returnClusterInfo_getCommonClusterInformationtest() {
+        try {
+            String[] correctCommonCluster = {"https://commonhost:8080", "username", "pwd"};
+
+            when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+
+            when(elasticSettingService.getElasticClusterInformation()).thenReturn(commonCluster);
+
+            String[] resultCommonCluster = this.sut.getCommonClusterInformation();
+            Assert.assertEquals(correctCommonCluster[0], resultCommonCluster[0]);
+            Assert.assertEquals(correctCommonCluster[1], resultCommonCluster[1]);
+            Assert.assertEquals(correctCommonCluster[2], resultCommonCluster[2]);
+        } catch (IOException ignored) {
+            fail("Should not throw this exception " + ignored.getMessage());
+        }
+    }
+
+    @Test(expected = AppException.class)
+    public void should_throwException_failToCreateIndexInTenantCluster_createIndexInTenantClustertest() {
+        try {
+            when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+            when(indicesService.createIndex(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(false);
+            this.sut.createIndexInTenantCluster("test", "test", "test", correctMap);
+        } catch (IOException ignored) {
+            fail("Should not throw this exception " + ignored.getMessage());
+        }
+    }
+
+    @Ignore
+    public void should_returnTaskIdResponse_reindexRequestSucceed_reindexInTenantClustertest() {
+        //TODO: fix the null Response from restHighLevelClient.getLowLevelClient().performRequest().
+        try {
+            String[] correctCommonCluster = {"https://commonhost:8080", "username", "pwd"};
+            Request request = new Request("POST", "/_reindex?wait_for_completion=false");
+            request.setEntity(httpEntityRequest);
+            when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+            when(indicesService.createIndex(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(false);
+            when(restHighLevelClient.getLowLevelClient()).thenReturn(restClient);
+            when(restClient.performRequest(request)).thenReturn(response);
+            when(response.getEntity()).thenReturn(httpEntity);
+            Assert.assertEquals(httpEntity, this.sut.reindexInTenantCluster("test", "test", correctCommonCluster));
+        } catch (IOException ignored) {
+        }
+    }
+}
diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe2905d770837c9bdf8d1f9bdb0d81d127667042
--- /dev/null
+++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java
@@ -0,0 +1,316 @@
+// Copyright 2017-2019, Schlumberger
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.service;
+
+import org.apache.http.StatusLine;
+import org.elasticsearch.ElasticsearchException;
+import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse;
+import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse.FieldMappingMetaData;
+import org.elasticsearch.action.bulk.BulkItemResponse.Failure;
+import org.elasticsearch.action.support.master.AcknowledgedResponse;
+import org.elasticsearch.client.*;
+import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+import org.elasticsearch.index.reindex.BulkByScrollResponse;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.opengroup.osdu.core.common.model.indexer.IndexSchema;
+import org.opengroup.osdu.indexer.service.IndexerMappingServiceImpl;
+import org.opengroup.osdu.core.common.model.search.RecordMetaAttribute;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.indexer.util.ElasticClientHandler;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.io.IOException;
+import java.util.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+@Ignore
+@RunWith(SpringRunner.class)
+@PrepareForTest({ RestHighLevelClient.class, IndicesClient.class })
+public class IndexerMappingServiceTest {
+
+	private final String kind = "tenant:test:test:1.0.0";
+	private final String index = "tenant-test-test-1.0.0";
+	private final String type = "test";
+	private final String mappingValid = "{\"dynamic\":false,\"properties\":{\"data\":{\"properties\":{\"Location\":{\"type\":\"geo_point\"}}},\"id\":{\"type\":\"keyword\"}}}";
+
+	@Mock
+	private RestClient restClient;
+	@Mock
+	private Response response;
+	@Mock
+	private StatusLine statusLine;
+
+	@InjectMocks
+	private IndexerMappingServiceImpl sut;
+
+	@Mock
+    private ElasticClientHandler elasticClientHandler;
+
+	@InjectMocks
+	private RestHighLevelClient restHighLevelClient;
+
+	@InjectMocks
+	private IndexSchema indexSchema;
+	@InjectMocks
+	private IndicesClient indicesClient;
+
+	@InjectMocks
+	private AcknowledgedResponse mappingResponse;
+
+	@Before
+	public void setup() throws IOException {
+		Map<String, String> dataMapping = new HashMap<>();
+		dataMapping.put("Location", "geo_point");
+		Map<String, Object> metaMapping = new HashMap<>();
+		metaMapping.put(RecordMetaAttribute.ID.getValue(), "keyword");
+		this.indexSchema = IndexSchema.builder().kind(kind).type(type).dataSchema(dataMapping).metaSchema(metaMapping)
+				.build();
+
+		this.indicesClient = PowerMockito.mock(IndicesClient.class);
+		this.restHighLevelClient = PowerMockito.mock(RestHighLevelClient.class);
+
+		when(this.restHighLevelClient.getLowLevelClient()).thenReturn(restClient);
+		when(this.restClient.performRequest(ArgumentMatchers.any())).thenReturn(response);
+		when(this.response.getStatusLine()).thenReturn(statusLine);
+		when(this.statusLine.getStatusCode()).thenReturn(200);
+	}
+
+	@Test
+	public void should_returnValidMapping_givenFalseMerge_createMappingTest() {
+		try {
+			String mapping = this.sut.createMapping(restHighLevelClient, indexSchema, index, false);
+			assertEquals(mappingValid, mapping);
+		} catch (Exception e) {
+			fail("Should not throw this exception" + e.getMessage());
+		}
+	}
+
+	@Test
+	public void should_returnValidMapping_givenTrueMerge_createMappingTest() {
+		try {
+			doReturn(this.indicesClient).when(this.restHighLevelClient).indices();
+			doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+
+			String mapping = this.sut.createMapping(this.restHighLevelClient, this.indexSchema, this.index, true);
+			assertEquals(this.mappingValid, mapping);
+		} catch (Exception e) {
+			fail("Should not throw this exception" + e.getMessage());
+		}
+	}
+
+	@Test
+	public void should_returnValidMapping_givenExistType_createMappingTest() {
+		try {
+			doReturn(this.indicesClient).when(this.restHighLevelClient).indices();
+			doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+
+			IndexerMappingServiceImpl indexerMappingServiceLocal = PowerMockito.spy(new IndexerMappingServiceImpl());
+			doReturn(false).when(indexerMappingServiceLocal).isTypeExist(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any());
+			String mapping = this.sut.createMapping(this.restHighLevelClient, this.indexSchema, this.index, true);
+			assertEquals(this.mappingValid, mapping);
+		} catch (Exception e) {
+			fail("Should not throw this exception" + e.getMessage());
+		}
+	}
+
+	@Test
+	public void should_update_indices_field_with_keyword_when_valid_indices() throws Exception {
+		try {
+			Set<String> indices = new HashSet<String>();
+			indices.add("indices 1");
+			GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class);
+			doReturn(this.indicesClient).when(this.restHighLevelClient).indices();
+			when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(getFieldMappingsResponse);
+			XContentBuilder builder = XContentFactory.jsonBuilder();
+			builder.startObject();
+			builder.field("any field", new HashMap());
+			builder.endObject();
+			BytesReference bytesReference = BytesReference.bytes(builder);
+			FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference);
+			Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>();
+			mapBuilder.put("data.any field", mappingMetaData);
+			Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>();
+			mappingBuilder.put("any index 1", mapBuilder);
+			mappingBuilder.put("any index 2", mapBuilder);
+			Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>();
+			mapping.put("indices 1", mappingBuilder);
+			when(getFieldMappingsResponse.mappings()).thenReturn(mapping);
+			doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			BulkByScrollResponse response = mock(BulkByScrollResponse.class);
+			doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>());
+			when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+			
+			this.sut.updateIndexMappingForIndicesOfSameType( indices,"any field");
+		} catch (Exception e) {
+			fail("Should not throw this exception" + e.getMessage());
+		}
+	}
+
+	@Test(expected = AppException.class)
+	public void should_throw_exception_if_someIndex_is_invalid_andWeIndexfield_with_keyword() throws Exception {
+		try {
+			Set<String> indices = new HashSet<String>();
+			indices.add("invalid 1");
+			GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class);
+			doReturn(this.indicesClient).when(this.restHighLevelClient).indices();
+			when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(getFieldMappingsResponse);
+			XContentBuilder builder = XContentFactory.jsonBuilder();
+			builder.startObject();
+			builder.field("any field", new HashMap());
+			builder.endObject();
+			BytesReference bytesReference = BytesReference.bytes(builder);
+			FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference);
+			Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>();
+			mapBuilder.put("data.any field", mappingMetaData);
+			Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>();
+			mappingBuilder.put("any index 1", mapBuilder);
+			mappingBuilder.put("any index 2", mapBuilder);
+			Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>();
+			mapping.put("indices 1", mappingBuilder);
+			when(getFieldMappingsResponse.mappings()).thenReturn(mapping);
+			doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			BulkByScrollResponse response = mock(BulkByScrollResponse.class);
+			doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>());
+			when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+			
+			this.sut.updateIndexMappingForIndicesOfSameType(indices,"any field");			
+		} catch (Exception e) {
+			throw e;
+		}
+	}
+
+	@Test(expected = AppException.class)
+	public void should_throw_exception_if_type_of_index_is_invalid_andWeIndexfield_with_keyword() throws Exception {
+		try {
+			Set<String> indices = new HashSet<String>();
+			indices.add("indices 1");			
+			GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class);
+			doReturn(this.indicesClient).when(this.restHighLevelClient).indices();
+			when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(getFieldMappingsResponse);
+			XContentBuilder builder = XContentFactory.jsonBuilder();
+			builder.startObject();
+			builder.field("any field", new HashMap());
+			builder.endObject();
+			BytesReference bytesReference = BytesReference.bytes(builder);
+			FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference);
+			Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>();
+			mapBuilder.put("data.any field", mappingMetaData);
+			Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>();
+			mappingBuilder.put("any index 1", mapBuilder);
+			mappingBuilder.put("any index 2", mapBuilder);
+			Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>();
+			mapping.put("indices 1", mappingBuilder);
+			when(getFieldMappingsResponse.mappings()).thenReturn(mapping);
+			doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			BulkByScrollResponse response = mock(BulkByScrollResponse.class);
+			doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>());
+			when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+			this.sut.updateIndexMappingForIndicesOfSameType(indices,"any field invalid");
+		} catch (Exception e) {
+			throw e;
+		}
+	}
+
+	@Test(expected = AppException.class)
+	public void should_throw_exception_if_elastic_search_failedToFetch_andWeIndexfield_with_keyword() throws Exception {
+		try {
+
+			Set<String> indices = new HashSet<String>();
+			indices.add("indices 1");
+			indices.add("indices Invalid");
+			GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class);
+			doReturn(this.indicesClient).when(this.restHighLevelClient).indices();
+			when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenThrow(new ElasticsearchException(""));
+			XContentBuilder builder = XContentFactory.jsonBuilder();
+			builder.startObject();
+			builder.field("any field", new HashMap());
+			builder.endObject();
+			BytesReference bytesReference = BytesReference.bytes(builder);
+			FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference);
+			Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>();
+			mapBuilder.put("data.any field", mappingMetaData);
+			Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>();
+			mappingBuilder.put("any index 1", mapBuilder);
+			mappingBuilder.put("any index 2", mapBuilder);
+			Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>();
+			mapping.put("indices 1", mappingBuilder);
+			when(getFieldMappingsResponse.mappings()).thenReturn(mapping);
+			doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			BulkByScrollResponse response = mock(BulkByScrollResponse.class);
+			doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>());
+			when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+			this.sut.updateIndexMappingForIndicesOfSameType(indices,"any field");
+		} catch (AppException e) {
+			throw e;
+		}
+	}
+
+	@Test(expected = AppException.class)
+	public void should_throw_exception_when_elastic_failedToIndex_indices_field_with_keyword() {
+		try {
+			Set<String> indices = new HashSet<String>();
+			indices.add("indices 1");
+			indices.add("indices Invalid");
+			GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class);
+			doReturn(this.indicesClient).when(this.restHighLevelClient).indices();
+			when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(getFieldMappingsResponse);
+			XContentBuilder builder = XContentFactory.jsonBuilder();
+			builder.startObject();
+			builder.field("any field", new HashMap());
+			builder.endObject();
+			BytesReference bytesReference = BytesReference.bytes(builder);
+			FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference);
+			Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>();
+			mapBuilder.put("data.any field", mappingMetaData);
+			Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>();
+			mappingBuilder.put("any index 1", mapBuilder);
+			mappingBuilder.put("any index 2", mapBuilder);
+			Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>();
+			mapping.put("indices 1", mappingBuilder);
+			when(getFieldMappingsResponse.mappings()).thenReturn(mapping);
+			doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			BulkByScrollResponse response = mock(BulkByScrollResponse.class);
+			doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class));
+			when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>());
+			when(this.indicesClient.putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class))).thenThrow(new ElasticsearchException(""));
+			when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+			this.sut.updateIndexMappingForIndicesOfSameType(indices,"any field");
+		} catch (AppException e) {
+			throw e;
+		} catch (Exception e) {
+			fail("Should not throw this exception" + e.getMessage());
+		}
+	}
+}
diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerSchemaServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerSchemaServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..25dd2ed446ebc1cb107a0149c139db46705569f2
--- /dev/null
+++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerSchemaServiceTest.java
@@ -0,0 +1,329 @@
+// Copyright 2017-2019, Schlumberger
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.service;
+
+import org.apache.http.HttpStatus;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.opengroup.osdu.core.common.model.indexer.IndexSchema;
+import org.opengroup.osdu.core.common.model.indexer.OperationType;
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.opengroup.osdu.indexer.provider.interfaces.ISchemaCache;
+import org.opengroup.osdu.indexer.service.IndexSchemaServiceImpl;
+import org.opengroup.osdu.indexer.service.IndexerMappingService;
+import org.opengroup.osdu.indexer.service.StorageService;
+import org.opengroup.osdu.core.common.model.http.RequestStatus;
+import org.opengroup.osdu.core.common.search.IndicesService;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.indexer.util.ElasticClientHandler;
+import org.opengroup.osdu.core.common.search.ElasticIndexNameResolver;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+import static org.mockito.MockitoAnnotations.initMocks;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+@Ignore
+@RunWith(SpringRunner.class)
+@PrepareForTest({RestHighLevelClient.class})
+public class IndexerSchemaServiceTest {
+
+    private final String kind = "tenant:test:test:1.0.0";
+    private final String emptySchema = null;
+    private final String someSchema = "{\"kind\":\"tenant:test:test:1.0.0\", \"schema\":[{\"path\":\"test-path\", \"kind\":\"tenant:test:test:1.0.0\"}]}";
+
+    @Mock
+    private JaxRsDpsLog log;
+    @Mock
+    private StorageService storageService;
+    @Mock
+    private ElasticClientHandler elasticClientHandler;
+    @Mock
+    private ElasticIndexNameResolver elasticIndexNameResolver;
+    @Mock
+    private IndexerMappingService mappingService;
+    @Mock
+    private IndicesService indicesService;
+    @Mock
+    private ISchemaCache schemaCache;
+    @InjectMocks
+    private IndexSchemaServiceImpl sut;
+
+    @Before
+    public void setup() {
+        initMocks(this);
+        RestHighLevelClient restHighLevelClient = mock(RestHighLevelClient.class);
+        when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+    }
+
+    @Test
+    public void should_returnNull_givenEmptySchema_getIndexerInputSchemaSchemaTest() throws Exception {
+        when(storageService.getStorageSchema(any())).thenReturn(emptySchema);
+
+        IndexSchema indexSchema = this.sut.getIndexerInputSchema(kind);
+
+        Assert.assertNotNull(indexSchema);
+    }
+
+    @Test
+    public void should_returnValidResponse_givenValidSchema_getIndexerInputSchemaTest() throws Exception {
+        when(storageService.getStorageSchema(any())).thenReturn(someSchema);
+
+        IndexSchema indexSchema = this.sut.getIndexerInputSchema(kind);
+
+        Assert.assertEquals(kind, indexSchema.getKind());
+    }
+
+    @Test
+    public void should_returnValidResponse_givenValidSchemaWithCacheHit_getIndexerInputSchemaTest() throws Exception {
+        when(storageService.getStorageSchema(any())).thenReturn(someSchema);
+        when(this.schemaCache.get(kind + "_flattened")).thenReturn(someSchema);
+
+        IndexSchema indexSchema = this.sut.getIndexerInputSchema(kind);
+
+        Assert.assertEquals(kind, indexSchema.getKind());
+    }
+
+    @Test
+    public void should_throw500_givenInvalidSchemaCacheHit_getIndexerInputSchemaTest() {
+        try {
+            String invalidSchema = "{}}";
+            when(storageService.getStorageSchema(any())).thenReturn(invalidSchema);
+
+            this.sut.getIndexerInputSchema(kind);
+            fail("Should throw exception");
+        } catch (AppException e) {
+            Assert.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getError().getCode());
+            Assert.assertEquals("An error has occurred while normalizing the schema.", e.getError().getMessage());
+        } catch (Exception e) {
+            fail("Should not throw exception" + e.getMessage());
+        }
+    }
+
+    @Test
+    public void should_return_basic_schema_when_storage_returns_no_schema() {
+        IndexSchema returnedSchema = this.sut.getIndexerInputSchema(kind);
+
+        assertNotNull(returnedSchema.getDataSchema());
+        assertNotNull(returnedSchema);
+        assertEquals(kind, returnedSchema.getKind());
+    }
+
+    @Test
+    public void should_create_schema_when_storage_returns_valid_schema() throws IOException, URISyntaxException {
+        String kind = "tenant1:avocet:completion:1.0.0";
+        String storageSchema = "{" +
+                "  \"kind\": \"tenant1:avocet:completion:1.0.0\"," +
+                "  \"schema\": [" +
+                "    {" +
+                "      \"path\": \"status\"," +
+                "      \"kind\": \"string\"" +
+                "    }," +
+                "    {" +
+                "      \"path\": \"startDate\"," +
+                "      \"kind\": \"string\"" +
+                "    }," +
+                "    {" +
+                "      \"path\": \"endDate\"," +
+                "      \"kind\": \"string\"" +
+                "    }," +
+                "    {" +
+                "      \"path\": \"type \"," +
+                "      \"kind\": \"string\"" +
+                "    }," +
+                "    {" +
+                "      \"path\": \"itemguid\"," +
+                "      \"kind\": \"string\"" +
+                "    }" +
+                "  ]" +
+                "}";
+        Map<String, OperationType> schemaMessages = new HashMap<>();
+        schemaMessages.put(kind, OperationType.create_schema);
+
+        when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-"));
+        when(this.schemaCache.get(kind)).thenReturn(null);
+        when(this.indicesService.isIndexExist(any(), any())).thenReturn(false);
+        when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema);
+
+        this.sut.processSchemaMessages(schemaMessages);
+
+        verify(this.mappingService, times(1)).getIndexMappingFromRecordSchema(any());
+        verify(this.indicesService, times(1)).createIndex(any(), any(), any(), any(), any());
+        verifyNoMoreInteractions(this.mappingService);
+    }
+
+    @Test
+    public void should_merge_mapping_when_storage_returns_valid_schema() throws IOException, URISyntaxException {
+        String kind = "tenant1:avocet:completion:1.0.0";
+        String storageSchema = "{" +
+                "  \"kind\": \"tenant1:avocet:completion:1.0.0\"," +
+                "  \"schema\": [" +
+                "    {" +
+                "      \"path\": \"status\"," +
+                "      \"kind\": \"string\"" +
+                "    }," +
+                "    {" +
+                "      \"path\": \"startDate\"," +
+                "      \"kind\": \"string\"" +
+                "    }" +
+                "  ]" +
+                "}";
+        Map<String, OperationType> schemaMessages = new HashMap<>();
+        schemaMessages.put(kind, OperationType.create_schema);
+
+        when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-"));
+        when(this.schemaCache.get(kind)).thenReturn(null);
+        when(this.indicesService.isIndexExist(any(), any())).thenReturn(true);
+        when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema);
+
+        this.sut.processSchemaMessages(schemaMessages);
+
+        verify(this.indicesService, times(0)).createIndex(any(), any(), any(), any(), any());
+        verify(this.mappingService, times(1)).createMapping(any(), any(), any(), anyBoolean());
+        verifyNoMoreInteractions(this.mappingService);
+    }
+
+    @Test
+    public void should_throw_mapping_conflict_when_elastic_backend_cannot_process_schema_changes() throws IOException, URISyntaxException {
+        String kind = "tenant1:avocet:completion:1.0.0";
+        String reason = String.format("Could not create type mapping %s/completion.", kind.replace(":", "-"));
+        String storageSchema = "{" +
+                "  \"kind\": \"tenant1:avocet:completion:1.0.0\"," +
+                "  \"schema\": [" +
+                "    {" +
+                "      \"path\": \"status\"," +
+                "      \"kind\": \"string\"" +
+                "    }" +
+                "  ]" +
+                "}";
+        Map<String, OperationType> schemaMessages = new HashMap<>();
+        schemaMessages.put(kind, OperationType.create_schema);
+
+        when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-"));
+        when(this.schemaCache.get(kind)).thenReturn(null);
+        when(this.indicesService.isIndexExist(any(), any())).thenReturn(true);
+        when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema);
+        when(this.mappingService.createMapping(any(), any(), any(), anyBoolean())).thenThrow(new AppException(HttpStatus.SC_BAD_REQUEST, reason, ""));
+
+        try {
+            this.sut.processSchemaMessages(schemaMessages);
+        } catch (AppException e){
+            assertEquals(e.getError().getCode(), RequestStatus.SCHEMA_CONFLICT);
+            assertEquals(e.getError().getMessage(), "error creating or merging index mapping");
+            assertEquals(e.getError().getReason(), reason);
+        } catch (Exception e) {
+            fail("Should not throw this exception " + e.getMessage());
+        }
+    }
+
+    @Test
+    public void should_throw_genericAppException_when_elastic_backend_cannot_process_schema_changes() throws IOException, URISyntaxException {
+        String kind = "tenant1:avocet:completion:1.0.0";
+        String reason = String.format("Could not create type mapping %s/completion.", kind.replace(":", "-"));
+        String storageSchema = "{" +
+                "  \"kind\": \"tenant1:avocet:completion:1.0.0\"," +
+                "  \"schema\": [" +
+                "    {" +
+                "      \"path\": \"status\"," +
+                "      \"kind\": \"string\"" +
+                "    }" +
+                "  ]" +
+                "}";
+        Map<String, OperationType> schemaMessages = new HashMap<>();
+        schemaMessages.put(kind, OperationType.create_schema);
+
+        when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-"));
+        when(this.schemaCache.get(kind)).thenReturn(null);
+        when(this.indicesService.isIndexExist(any(), any())).thenReturn(true);
+        when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema);
+        when(this.mappingService.createMapping(any(), any(), any(), anyBoolean())).thenThrow(new AppException(HttpStatus.SC_FORBIDDEN, reason, "blah"));
+
+        try {
+            this.sut.processSchemaMessages(schemaMessages);
+        } catch (AppException e){
+            assertEquals(e.getError().getCode(), HttpStatus.SC_FORBIDDEN);
+            assertEquals(e.getError().getMessage(), "blah");
+            assertEquals(e.getError().getReason(), reason);
+        } catch (Exception e) {
+            fail("Should not throw this exception " + e.getMessage());
+        }
+    }
+
+
+    @Test
+    public void should_log_and_do_nothing_when_storage_returns_invalid_schema() throws IOException, URISyntaxException {
+        String kind = "tenant1:avocet:completion:1.0.0";
+        String storageSchema = "{" +
+                "  \"kind\": \"tenant1:avocet:completion:1.0.0\"" +
+                "}";
+        Map<String, OperationType> schemaMessages = new HashMap<>();
+        schemaMessages.put(kind, OperationType.create_schema);
+
+        when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-"));
+        when(this.schemaCache.get(kind)).thenReturn(null);
+        when(this.indicesService.isIndexExist(any(), any())).thenReturn(true);
+        when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema);
+
+        this.sut.processSchemaMessages(schemaMessages);
+
+        verify(this.log).warning(eq("schema not found for kind: tenant1:avocet:completion:1.0.0"));
+    }
+
+    @Test
+    public void should_invalidateCache_when_purge_schema_and_schema_found_in_cache() throws IOException {
+        String kind = "tenant1:avocet:completion:1.0.0";
+        Map<String, OperationType> schemaMessages = new HashMap<>();
+        schemaMessages.put(kind, OperationType.purge_schema);
+
+        when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-"));
+        when(this.indicesService.isIndexExist(any(), any())).thenReturn(true);
+        when(this.schemaCache.get(kind)).thenReturn("schema");
+        when(this.schemaCache.get(kind + "_flattened")).thenReturn("flattened schema");
+
+        this.sut.processSchemaMessages(schemaMessages);
+
+        verify(this.schemaCache, times(2)).get(anyString());
+        verify(this.schemaCache, times(2)).delete(anyString());
+    }
+
+    @Test
+    public void should_log_warning_when_purge_schema_and_schema_not_found_in_cache() throws IOException {
+        String kind = "tenant1:avocet:completion:1.0.0";
+        Map<String, OperationType> schemaMessages = new HashMap<>();
+        schemaMessages.put(kind, OperationType.purge_schema);
+
+        when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-"));
+        when(this.indicesService.isIndexExist(any(), any())).thenReturn(false);
+
+        this.sut.processSchemaMessages(schemaMessages);
+
+        verify(this.log).warning(eq(String.format("Kind: %s not found", kind)));
+    }
+}
diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0043a0d898b93ace6e7a49c7f2e67b6f17f41726
--- /dev/null
+++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerServiceTest.java
@@ -0,0 +1,337 @@
+package org.opengroup.osdu.indexer.ibm.service;//// Copyright 2017-2019, Schlumberger
+////
+//// Licensed 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 org.opendes.indexer.service;
+//
+//import com.google.gson.Gson;
+//import com.google.gson.reflect.TypeToken;
+//import org.elasticsearch.action.bulk.BulkItemResponse;
+//import org.elasticsearch.action.bulk.BulkResponse;
+//import org.elasticsearch.client.RequestOptions;
+//import org.elasticsearch.client.RestHighLevelClient;
+//import org.junit.Before;
+//import org.junit.Ignore;
+//import org.junit.Test;
+//import org.junit.runner.RunWith;
+//import org.mockito.InjectMocks;
+//import org.mockito.Mock;
+//import org.mockito.Spy;
+//import org.opendes.client.api.DpsHeaders;
+//import org.opendes.core.logging.JaxRsDpsLog;
+//import org.opendes.core.model.DeploymentEnvironment;
+//import org.opendes.core.model.RecordChangedMessages;
+//import org.opendes.core.service.IndicesService;
+//import org.opendes.core.util.Config;
+//import org.opendes.core.util.ElasticClientHandler;
+//import org.opendes.core.util.ElasticIndexNameResolver;
+//import org.opendes.core.util.HeadersUtil;
+//import org.opendes.indexer.logging.AuditLogger;
+//import org.opendes.indexer.model.*;
+//import org.opendes.indexer.publish.IPublisher;
+//import org.opendes.indexer.util.IRequestInfo;
+//import org.opendes.indexer.util.IndexerQueueTaskBuilder;
+//import org.opendes.indexer.util.JobStatus;
+//import org.opendes.indexer.util.RecordInfo;
+//import org.powermock.core.classloader.annotations.PrepareForTest;
+//import org.springframework.context.annotation.Lazy;
+//import org.springframework.test.context.junit4.SpringRunner;
+//
+//import javax.inject.Inject;
+//import java.io.IOException;
+//import java.lang.reflect.Type;
+//import java.util.*;
+//
+//import static java.util.Collections.singletonList;
+//import static org.junit.Assert.*;
+//import static org.mockito.Matchers.any;
+//import static org.mockito.Mockito.verify;
+//import static org.mockito.Mockito.when;
+//import static org.powermock.api.mockito.PowerMockito.mock;
+//import static org.powermock.api.mockito.PowerMockito.mockStatic;
+//
+//@Ignore
+//@RunWith(SpringRunner.class)
+//@PrepareForTest({RestHighLevelClient.class, BulkResponse.class, StorageAcl.class, HeadersUtil.class, Config.class})
+//public class IndexerServiceTest {
+//
+//    private final String pubsubMsg = "[{\"id\":\"tenant1:doc:test1\",\"kind\":\"tenant1:testindexer1:well:1.0.0\",\"op\":\"update\"}," +
+//            "{\"id\":\"tenant1:doc:test2\",\"kind\":\"tenant1:testindexer2:well:1.0.0\",\"op\":\"create\"}]";
+//    private final String kind1 = "tenant1:testindexer1:well:1.0.0";
+//    private final String kind2 = "tenant1:testindexer2:well:1.0.0";
+//    private final String recordId1 = "tenant1:doc:test1";
+//    private final String recordId2 = "tenant1:doc:test2";
+//    private final String failureMassage = "test failure";
+//
+//    @Mock
+//    private IndexSchemaService indexSchemaService;
+//    @Mock
+//    private IndicesService indicesService;
+//    @Mock
+//    private IndexerMappingService indexerMappingService;
+//    @Mock
+//    private StorageService storageService;
+//    @Mock
+//    private IPublisher publisherImpl;
+//    @Mock
+//    private RestHighLevelClient restHighLevelClient;
+//    @Mock
+//    private ElasticClientHandler elasticClientHandler;
+//    @Mock
+//    private BulkResponse bulkResponse;
+//    @Mock
+//    private IRequestInfo requestInfo;
+//    @Mock
+//    private ElasticIndexNameResolver elasticIndexNameResolver;
+//    @Mock
+//    private AttributeParsingServiceImpl attributeParsingServiceImpl;
+//    @Mock
+//    private IndexerQueueTaskBuilder indexerQueueTaskBuilder;
+//    @Mock
+//    private JaxRsDpsLog log;
+//    @Mock
+//    private AuditLogger auditLogger;
+//    @InjectMocks
+//    private IndexerServiceImpl sut;
+//    @InjectMocks @Spy
+//    private JobStatus jobStatus = new JobStatus();
+//
+//    @Inject
+//    @Lazy
+//    private DpsHeaders dpsHeaders;
+//    private RecordChangedMessages recordChangedMessages;
+//    private List<RecordInfo> recordInfos;
+//
+//    @Before
+//    public void setup() throws IOException {
+//
+//        mockStatic(StorageAcl.class);
+//        mockStatic(Config.class);
+//
+//        when(Config.getDeploymentEnvironment()).thenReturn(DeploymentEnvironment.LOCAL);
+//        when(Config.getElasticClusterName()).thenReturn("CLUSTER");
+//        when(Config.getElasticServerAddress()).thenReturn("testsite");
+//
+//        dpsHeaders = new DpsHeaders();
+//        dpsHeaders.put(AppEngineHeaders.TASK_QUEUE_RETRY_COUNT, "1");
+//        dpsHeaders.put(DpsHeaders.AUTHORIZATION, "testAuth");
+//        when(requestInfo.getHeaders()).thenReturn(dpsHeaders);
+//        when(requestInfo.getHeadersMapWithDwdAuthZ()).thenReturn(dpsHeaders.getHeaders());
+//
+//        Type listType = new TypeToken<List<RecordInfo>>() {}.getType();
+//        recordInfos = (new Gson()).fromJson(pubsubMsg, listType);
+//
+//        when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient);
+//        when(restHighLevelClient.bulk(any(), any(RequestOptions.class))).thenReturn(bulkResponse);
+//
+//        BulkItemResponse[] responses = new BulkItemResponse[]{prepareResponseFail(), prepareResponseSuccess()};
+//        when(bulkResponse.getItems()).thenReturn(responses);
+//        Map<String, String> attr = new HashMap<>();
+//        attr.put(DpsHeaders.ACCOUNT_ID, "slb");
+//        recordChangedMessages = RecordChangedMessages.builder().attributes(attr).messageId("xxxx").publishTime("2000-01-02T10:10:44+0000").data("{}").build();
+//        when(StorageAcl.flattenAcl(any())).thenReturn(null);
+//    }
+//
+//    @Test
+//    public void should_returnNull_givenEmptyJobSubInfo_processRecordChangedMessageTest() throws Exception {
+//        JobStatus jobStatus = this.sut.processRecordChangedMessages(recordChangedMessages, new ArrayList<>());
+//
+//        assertNull(jobStatus);
+//    }
+//
+//    @Test
+//    public void should_returnValidJobStatus_givenNullSchema_processRecordChangedMessageTest() {
+//        try {
+//            indexSchemaServiceMock(kind1, null);
+//            indexSchemaServiceMock(kind2, null);
+//            List<ConversionStatus> conversionStatus = new LinkedList<>();
+//            List<Records.Entity> validRecords = new ArrayList<>();
+//            Map<String, Object> storageData = new HashMap<>();
+//            storageData.put("schema1", "test-value");
+//            storageData.put("schema2", "test-value");
+//            storageData.put("schema3", "test-value");
+//            storageData.put("schema4", "test-value");
+//            storageData.put("schema5", "test-value");
+//            storageData.put("schema6", "test-value");
+//            validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build());
+//            Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatus).build();
+//
+//            when(storageService.getStorageRecords(any())).thenReturn(storageRecords);
+//            when(indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true);
+//
+//            JobStatus jobStatus = this.sut.processRecordChangedMessages(recordChangedMessages, recordInfos);
+//
+//            assertEquals(2, jobStatus.getStatusesList().size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.WARN).size());
+//        } catch (Exception e) {
+//            fail("Should not throw this exception" + e.getMessage());
+//        }
+//    }
+//
+//    @Test
+//    public void should_returnValidJobStatus_givenFailedUnitsConversion_processRecordChangedMessageTest() {
+//        try {
+//            indexSchemaServiceMock(kind1, null);
+//            indexSchemaServiceMock(kind2, null);
+//            List<ConversionStatus> conversionStatuses = new LinkedList<>();
+//            List<String> status=new ArrayList<>();
+//            status.add("crs bla bla");
+//            ConversionStatus conversionStatus=ConversionStatus.builder().status("ERROR").errors(status).id(recordId2).build();
+//            conversionStatuses.add(conversionStatus);
+//            List<Records.Entity> validRecords = new ArrayList<>();
+//            Map<String, Object> storageData = new HashMap<>();
+//            storageData.put("schema1", "test-value");
+//            storageData.put("schema2", "test-value");
+//            storageData.put("schema3", "test-value");
+//            storageData.put("schema4", "test-value");
+//            storageData.put("schema5", "test-value");
+//            storageData.put("schema6", "test-value");
+//            validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build());
+//            Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatuses).build();
+//
+//            when(storageService.getStorageRecords(any())).thenReturn(storageRecords);
+//            when(indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true);
+//
+//            JobStatus jobStatus = this.sut.processRecordChangedMessages(recordChangedMessages, recordInfos);
+//
+//            assertEquals(2, jobStatus.getStatusesList().size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.WARN).size());
+//            assertTrue(jobStatus.getJobStatusByRecordId(jobStatus.getIdsByIndexingStatus(IndexingStatus.WARN).get(0)).getIndexProgress().getTrace().contains("crs bla bla"));
+//        } catch (Exception e) {
+//            fail("Should not throw this exception" + e.getMessage());
+//        }
+//    }
+//
+//    @Test
+//    public void should_returnValidJobStatus_givenNullSchemaForARecord_processRecordChangedMessageTest() {
+//        try {
+//            List<Records.Entity> validRecords = new ArrayList<>();
+//            List<ConversionStatus> conversionStatus = new LinkedList<>();
+//            Map<String, Object> storageData = new HashMap<>();
+//            storageData.put("schema1", "test-value");
+//            storageData.put("schema2", "test-value");
+//            storageData.put("schema3", "test-value");
+//            storageData.put("schema4", "test-value");
+//            storageData.put("schema5", "test-value");
+//            storageData.put("schema6", "test-value");
+//            validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build());
+//            Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatus).build();
+//            when(storageService.getStorageRecords(any())).thenReturn(storageRecords);
+//
+//            Map<String, String> schema = createSchema();
+//            indexSchemaServiceMock(kind1, schema);
+//            indexSchemaServiceMock(kind2, null);
+//            when(elasticIndexNameResolver.getIndexNameFromKind(kind2)).thenReturn("tenant1-testindexer2-well-1.0.0");
+//            when(indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true);
+//            JobStatus jobStatus = sut.processRecordChangedMessages(recordChangedMessages, recordInfos);
+//
+//            assertEquals(2, jobStatus.getStatusesList().size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.WARN).size());
+//            assertEquals("Indexed Successfully", jobStatus.getStatusesList().get(1).getIndexProgress().getTrace().pop());
+//            assertEquals("schema not found", jobStatus.getStatusesList().get(1).getIndexProgress().getTrace().pop());
+//        } catch (Exception e) {
+//            fail("Should not throw this exception" + e.getMessage());
+//        }
+//    }
+//
+//    @Test
+//    public void should_returnValidJobStatus_givenValidCreateAndUpdateRecords_processRecordChangedMessagesTest() {
+//        try {
+//            Map<String, Object> storageData = new HashMap<>();
+//            storageData.put("schema1", "test-value");
+//            List<ConversionStatus> conversionStatus = new LinkedList<>();
+//            List<Records.Entity> validRecords = new ArrayList<>();
+//            validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build());
+//            Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatus).build();
+//
+//            when(storageService.getStorageRecords(any())).thenReturn(storageRecords);
+//            when(indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true);
+//            Map<String, String> schema = createSchema();
+//            indexSchemaServiceMock(kind2, schema);
+//            indexSchemaServiceMock(kind1, null);
+//            JobStatus jobStatus = sut.processRecordChangedMessages(recordChangedMessages, recordInfos);
+//
+//            assertEquals(2, jobStatus.getStatusesList().size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.SUCCESS).size());
+//        } catch (Exception e) {
+//            fail("Should not throw this exception" + e.getMessage());
+//        }
+//    }
+//
+//    @Test
+//    public void should_properlyUpdateAuditLogs_givenValidCreateAndUpdateRecords() {
+//        try {
+//            Map<String, Object> storageData = new HashMap<>();
+//            List<ConversionStatus> conversionStatus = new LinkedList<>();
+//
+//            storageData.put("schema1", "test-value");
+//            List<Records.Entity> validRecords = new ArrayList<>();
+//            validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build());
+//            Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatus).build();
+//
+//            when(this.storageService.getStorageRecords(any())).thenReturn(storageRecords);
+//            when(this.indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true);
+//            Map<String, String> schema = createSchema();
+//            indexSchemaServiceMock(kind2, schema);
+//            indexSchemaServiceMock(kind1, null);
+//            JobStatus jobStatus = this.sut.processRecordChangedMessages(recordChangedMessages, recordInfos);
+//
+//            assertEquals(2, jobStatus.getStatusesList().size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size());
+//            assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.SUCCESS).size());
+//
+//            verify(this.auditLogger).indexCreateRecordSuccess(singletonList("RecordStatus(id=tenant1:doc:test2, kind=tenant1:testindexer2:well:1.0.0, operationType=create, status=SUCCESS)"));
+//            verify(this.auditLogger).indexUpdateRecordFail(singletonList("RecordStatus(id=tenant1:doc:test1, kind=tenant1:testindexer1:well:1.0.0, operationType=update, status=FAIL)"));
+//        } catch (Exception e) {
+//            fail("Should not throw this exception" + e.getMessage());
+//        }
+//    }
+//
+//    private BulkItemResponse prepareResponseFail() {
+//        BulkItemResponse responseFail = mock(BulkItemResponse.class);
+//        when(responseFail.isFailed()).thenReturn(true);
+//        when(responseFail.getFailureMessage()).thenReturn(failureMassage);
+//        when(responseFail.getId()).thenReturn(recordId1);
+//        when(responseFail.getFailure()).thenReturn(new BulkItemResponse.Failure("failure index", "failure type", "failure id", new Exception("test failure")));
+//        return responseFail;
+//    }
+//
+//    private BulkItemResponse prepareResponseSuccess() {
+//        BulkItemResponse responseSuccess = mock(BulkItemResponse.class);
+//        when(responseSuccess.getId()).thenReturn(recordId2);
+//        return responseSuccess;
+//    }
+//
+//    private void indexSchemaServiceMock(String kind, Map<String, String> schema) {
+//        if (schema == null) {
+//            IndexSchema indexSchema = IndexSchema.builder().kind(kind).dataSchema(null).build();
+//            when(indexSchemaService.getIndexerInputSchema(kind)).thenReturn(indexSchema);
+//        } else {
+//            IndexSchema indexSchema = IndexSchema.builder().kind(kind).dataSchema(schema).build();
+//            when(indexSchemaService.getIndexerInputSchema(kind)).thenReturn(indexSchema);
+//        }
+//    }
+//
+//    private Map<String, String> createSchema() {
+//        Map<String, String> schema = new HashMap<>();
+//        schema.put("schema1", "keyword");
+//        schema.put("schema2", "boolean");
+//        schema.put("schema3", "date");
+//        schema.put("schema6", "object");
+//        return schema;
+//    }
+//}
diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/ReindexServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/ReindexServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6a102b7821fa8a58bf38b0a992ce2203d8a2a47
--- /dev/null
+++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/ReindexServiceTest.java
@@ -0,0 +1,145 @@
+// Copyright 2017-2019, Schlumberger
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.service;
+
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.model.indexer.RecordQueryResponse;
+import org.opengroup.osdu.core.common.model.indexer.RecordReindexRequest;
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.opengroup.osdu.indexer.service.ReindexServiceImpl;
+import org.opengroup.osdu.indexer.service.StorageService;
+import org.opengroup.osdu.indexer.util.IndexerQueueTaskBuilder;
+import org.opengroup.osdu.core.common.provider.interfaces.IRequestInfo;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.*;
+
+import static org.junit.Assert.fail;
+import static org.mockito.MockitoAnnotations.initMocks;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+@Ignore
+@RunWith(SpringRunner.class)
+public class ReindexServiceTest {
+
+    private final String cursor = "100";
+
+    private final String correlationId = UUID.randomUUID().toString();
+
+    @Mock
+    private StorageService storageService;
+
+    @Mock
+    private Map<String, String> httpHeaders;
+    @Mock
+    private IRequestInfo requestInfo;
+    @Mock
+    private IndexerQueueTaskBuilder indexerQueueTaskBuilder;
+    @Mock
+    private JaxRsDpsLog log;
+    @InjectMocks
+    private ReindexServiceImpl sut;
+
+    private RecordReindexRequest recordReindexRequest;
+    private RecordQueryResponse recordQueryResponse;
+
+    @Before
+    public void setup() {
+        initMocks(this);
+
+        mockStatic(UUID.class);
+
+        recordReindexRequest = RecordReindexRequest.builder().kind("tenant:test:test:1.0.0").cursor(cursor).build();
+        recordQueryResponse = new RecordQueryResponse();
+
+        httpHeaders = new HashMap<>();
+        httpHeaders.put(DpsHeaders.AUTHORIZATION, "testAuth");
+        httpHeaders.put(DpsHeaders.CORRELATION_ID, correlationId);
+        DpsHeaders standardHeaders = DpsHeaders.createFromMap(httpHeaders);
+        when(requestInfo.getHeaders()).thenReturn(standardHeaders);
+        when(requestInfo.getHeadersMapWithDwdAuthZ()).thenReturn(httpHeaders);
+    }
+
+    @Test
+    public void should_returnNull_givenNullResponseResult_reIndexRecordsTest() {
+        try {
+            recordQueryResponse.setResults(null);
+            when(storageService.getRecordsByKind(ArgumentMatchers.any())).thenReturn(recordQueryResponse);
+
+            String response = sut.reindexRecords(recordReindexRequest);
+
+            Assert.assertNull(response);
+        } catch (Exception e) {
+            fail("Should not throw this exception" + e.getMessage());
+        }
+    }
+
+    @Test
+    public void should_returnNull_givenEmptyResponseResult_reIndexRecordsTest() {
+        try {
+            recordQueryResponse.setResults(new ArrayList<>());
+            when(storageService.getRecordsByKind(ArgumentMatchers.any())).thenReturn(recordQueryResponse);
+
+            String response = sut.reindexRecords(recordReindexRequest);
+
+            Assert.assertNull(response);
+        } catch (Exception e) {
+            fail("Should not throw this exception" + e.getMessage());
+        }
+    }
+
+    @Test
+    public void should_returnRecordQueryRequestPayload_givenValidResponseResult_reIndexRecordsTest() {
+        try {
+            recordQueryResponse.setCursor(cursor);
+            List<String> results = new ArrayList<>();
+            results.add("test1");
+            recordQueryResponse.setResults(results);
+            when(storageService.getRecordsByKind(ArgumentMatchers.any())).thenReturn(recordQueryResponse);
+
+            String taskQueuePayload = sut.reindexRecords(recordReindexRequest);
+
+            Assert.assertEquals("{\"kind\":\"tenant:test:test:1.0.0\",\"cursor\":\"100\"}", taskQueuePayload);
+        } catch (Exception e) {
+            fail("Should not throw exception" + e.getMessage());
+        }
+    }
+
+    @Test
+    public void should_returnRecordChangedMessage_givenValidResponseResult_reIndexRecordsTest() {
+        try {
+            List<String> results = new ArrayList<>();
+            results.add("test1");
+            recordQueryResponse.setResults(results);
+            when(storageService.getRecordsByKind(ArgumentMatchers.any())).thenReturn(recordQueryResponse);
+
+            String taskQueuePayload = sut.reindexRecords(recordReindexRequest);
+
+            Assert.assertEquals(String.format("{\"data\":\"[{\\\"id\\\":\\\"test1\\\",\\\"kind\\\":\\\"tenant:test:test:1.0.0\\\",\\\"op\\\":\\\"create\\\"}]\",\"attributes\":{\"slb-correlation-id\":\"%s\"}}", correlationId), taskQueuePayload);
+        } catch (Exception e) {
+            fail("Should not throw exception" + e.getMessage());
+        }
+    }
+}
diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/StorageServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/StorageServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..93e90b30e58f53034af05e8580b945c759f85fe9
--- /dev/null
+++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/StorageServiceTest.java
@@ -0,0 +1,211 @@
+// Copyright 2017-2019, Schlumberger
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.service;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.opengroup.osdu.core.common.model.indexer.RecordInfo;
+import org.opengroup.osdu.core.common.model.indexer.RecordQueryResponse;
+import org.opengroup.osdu.core.common.model.indexer.RecordReindexRequest;
+import org.opengroup.osdu.core.common.model.indexer.Records;
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.opengroup.osdu.indexer.service.StorageServiceImpl;
+import org.opengroup.osdu.core.common.model.indexer.JobStatus;
+import org.opengroup.osdu.core.common.model.http.HttpResponse;
+import org.opengroup.osdu.core.common.provider.interfaces.IRequestInfo;
+import org.opengroup.osdu.core.common.http.IUrlFetchService;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.springframework.http.HttpStatus;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.lang.reflect.Type;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+@Ignore
+@RunWith(SpringRunner.class)
+public class StorageServiceTest {
+
+    @Mock
+    private IUrlFetchService urlFetchService;
+    @Mock
+    private JobStatus jobStatus;
+    @Mock
+    private JaxRsDpsLog log;
+    @Mock
+    private IRequestInfo requestInfo;
+    @InjectMocks
+    private StorageServiceImpl sut;
+
+    private List<String> ids;
+
+    @Before
+    public void setup() {
+
+        String recordChangedMessages = "[{\"id\":\"tenant1:doc:1dbf528e0e0549cab7a08f29fbfc8465\",\"kind\":\"tenant1:testindexer1528919679710:well:1.0.0\",\"op\":\"purge\"}," +
+                "{\"id\":\"tenant1:doc:1dbf528e0e0549cab7a08f29fbfc8465\",\"kind\":\"tenant1:testindexer1528919679710:well:1.0.0\",\"op\":\"create\"}]";
+
+        when(this.requestInfo.getHeadersMap()).thenReturn(new HashMap<>());
+
+        Type listType = new TypeToken<List<RecordInfo>>() {}.getType();
+
+        List<RecordInfo> msgs = (new Gson()).fromJson(recordChangedMessages, listType);
+        jobStatus.initialize(msgs);
+        ids = Arrays.asList("tenant1:doc:1dbf528e0e0549cab7a08f29fbfc8465", "tenant1:doc:1dbf528e0e0549cab7a08f29fbfc8465");
+    }
+
+    @Test
+    public void should_return404_givenNullData_getValidStorageRecordsTest() throws URISyntaxException {
+
+        HttpResponse httpResponse = mock(HttpResponse.class);
+        Mockito.when(httpResponse.getBody()).thenReturn(null);
+
+        when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse);
+
+        should_return404_getValidStorageRecordsTest();
+    }
+
+    @Test
+    public void should_return404_givenEmptyData_getValidStorageRecordsTest() throws URISyntaxException {
+
+        String emptyDataFromStorage = "{\"records\":[],\"notFound\":[]}";
+
+        HttpResponse httpResponse = mock(HttpResponse.class);
+        Mockito.when(httpResponse.getBody()).thenReturn(emptyDataFromStorage);
+
+        when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse);
+
+        should_return404_getValidStorageRecordsTest();
+    }
+
+    @Test
+    public void should_returnOneValidRecords_givenValidData_getValidStorageRecordsTest() throws URISyntaxException {
+
+        String validDataFromStorage = "{\"records\":[{\"id\":\"testid\", \"version\":1, \"kind\":\"tenant:test:test:1.0.0\"}],\"notFound\":[\"invalid1\"]}";
+
+        HttpResponse httpResponse = mock(HttpResponse.class);
+        Mockito.when(httpResponse.getBody()).thenReturn(validDataFromStorage);
+
+        when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse);
+        Records storageRecords = this.sut.getStorageRecords(ids);
+
+        assertEquals(1, storageRecords.getRecords().size());
+    }
+
+    @Test
+    public void should_returnValidResponse_givenValidRecordQueryRequest_getRecordListByKind() throws Exception {
+
+        RecordReindexRequest recordReindexRequest = RecordReindexRequest.builder().kind("tenant:test:test:1.0.0").cursor("100").build();
+
+        HttpResponse httpResponse = new HttpResponse();
+        httpResponse.setBody(new Gson().toJson(recordReindexRequest, RecordReindexRequest.class));
+
+        when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse);
+
+        RecordQueryResponse recordQueryResponse = this.sut.getRecordsByKind(recordReindexRequest);
+
+        assertEquals("100", recordQueryResponse.getCursor());
+        assertNull(recordQueryResponse.getResults());
+    }
+
+    @Test
+    public void should_returnValidResponse_givenValidKind_getSchemaByKind() throws Exception {
+
+        String validSchemaFromStorage = "{" +
+                "  \"kind\": \"tenant:test:test:1.0.0\"," +
+                "  \"schema\": [" +
+                "    {" +
+                "      \"path\": \"msg\"," +
+                "      \"kind\": \"string\"" +
+                "    }," +
+                "    {" +
+                "      \"path\": \"references.entity\"," +
+                "      \"kind\": \"string\"" +
+                "    }" +
+                "  ]," +
+                "  \"ext\": null" +
+                "}";
+        String kind = "tenant:test:test:1.0.0";
+
+        HttpResponse httpResponse = new HttpResponse();
+        httpResponse.setResponseCode(HttpStatus.OK.value());
+        httpResponse.setBody(validSchemaFromStorage);
+
+        when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse);
+
+        String recordSchemaResponse = this.sut.getStorageSchema(kind);
+
+        assertNotNull(recordSchemaResponse);
+    }
+
+    @Test
+    public void should_returnNullResponse_givenAbsentKind_getSchemaByKind() throws Exception {
+
+        String kind = "tenant:test:test:1.0.0";
+
+        HttpResponse httpResponse = new HttpResponse();
+        httpResponse.setResponseCode(HttpStatus.NOT_FOUND.value());
+
+        when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse);
+
+        String recordSchemaResponse = this.sut.getStorageSchema(kind);
+
+        assertNull(recordSchemaResponse);
+    }
+
+    @Test
+    public void should_returnOneValidRecords_givenValidData_getValidStorageRecordsWithInvalidConversionTest() throws URISyntaxException {
+
+        String validDataFromStorage = "{\"records\":[{\"id\":\"testid\", \"version\":1, \"kind\":\"tenant:test:test:1.0.0\"}],\"notFound\":[\"invalid1\"],\"conversionStatuses\": [{\"id\":\"testid\",\"status\":\"ERROR\",\"errors\":[\"conversion error occured\"] } ]}";
+
+        HttpResponse httpResponse = mock(HttpResponse.class);
+        Mockito.when(httpResponse.getBody()).thenReturn(validDataFromStorage);
+
+        when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse);
+        Records storageRecords = this.sut.getStorageRecords(ids);
+
+        assertEquals(1, storageRecords.getRecords().size());
+
+        assertEquals(1, storageRecords.getConversionStatuses().get(0).getErrors().size());
+
+        assertEquals("conversion error occured", storageRecords.getConversionStatuses().get(0).getErrors().get(0));
+    }
+
+    private void should_return404_getValidStorageRecordsTest() {
+        try {
+            this.sut.getStorageRecords(ids);
+            fail("Should throw exception");
+        } catch (AppException e) {
+            assertEquals(HttpStatus.NOT_FOUND, e.getError().getCode());
+        } catch (Exception e) {
+            fail("Should not throw this exception" + e.getMessage());
+        }
+    }
+}
diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/TenantInfoServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/TenantInfoServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..378d7a8a467e06ab994545a06f56dc295ad906db
--- /dev/null
+++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/TenantInfoServiceTest.java
@@ -0,0 +1,92 @@
+// Copyright 2017-2019, Schlumberger
+//
+// Licensed 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 org.opengroup.osdu.indexer.ibm.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
+import org.opengroup.osdu.core.common.provider.interfaces.ITenantFactory;
+import org.opengroup.osdu.indexer.ibm.di.TenantInfoService;
+import org.opengroup.osdu.indexer.ibm.util.IHeadersInfo;
+import org.springframework.http.HttpHeaders;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+public class TenantInfoServiceTest {
+
+    private static final String HEADER_NAME = "ANY_HEADER";
+    private static final String HEADER_VALUE = "ANY_VALUE";
+
+    @Mock
+    private ITenantFactory tenantFactory;
+    @Mock
+    private IHeadersInfo headersInfo;
+    
+    @InjectMocks
+    private TenantInfoService sut;
+
+    @Mock
+    private TenantInfo info;
+
+    @Mock
+    private HttpHeaders httpHeaders;
+
+    @InjectMocks
+    private DpsHeaders HEADERS;
+
+    @Before
+    public void setup() {
+        HEADERS.put(HEADER_NAME, HEADER_VALUE);
+    }
+
+    @Ignore
+    @Test
+    public void should_return_validTenant_given_validAccountId() {
+
+        when(this.info.getName()).thenReturn("tenant1");
+        when(tenantFactory.getTenantInfo("tenant1")).thenReturn(info);
+
+        when(this.headersInfo.getHeaders()).thenReturn(HEADERS);
+
+        when(this.headersInfo.getPartitionId()).thenReturn("tenant1");
+
+        when(this.sut.getTenantInfo()).thenReturn(info);
+
+        assertNotNull(this.sut.getTenantInfo());
+        assertEquals("tenant1", this.sut.getTenantInfo().getName());
+    }
+
+    @Ignore
+    @Test(expected = AppException.class)
+    public void should_throwException_given_invalidAccountId() {
+
+        when(this.info.getName()).thenReturn("tenant2");
+        when(tenantFactory.getTenantInfo("tenant1")).thenReturn(null);
+
+        when(this.sut.getTenantInfo()).thenReturn(info);
+
+        assertNotNull(this.sut.getTenantInfo());
+    }
+}
\ No newline at end of file
diff --git a/testing/indexer-test-aws/pom.xml b/testing/indexer-test-aws/pom.xml
index 800ef6ef3cc40354fe5c420e8bcb8d5b3002e835..551f70b808eca63f6e409c470c43e818e0db58b9 100644
--- a/testing/indexer-test-aws/pom.xml
+++ b/testing/indexer-test-aws/pom.xml
@@ -25,6 +25,24 @@
     <version>0.0.2-SNAPSHOT</version>
     <packaging>jar</packaging>
 
+    <repositories>
+        <repository>
+            <id>${gitlab-server}</id>
+            <url>https://community.opengroup.org/api/v4/groups/17/-/packages/maven</url>
+        </repository>
+    </repositories>
+
+    <distributionManagement>
+        <repository>
+            <id>${gitlab-server}</id>
+            <url>https://community.opengroup.org/api/v4/projects/25/packages/maven</url>
+        </repository>
+        <snapshotRepository>
+            <id>${gitlab-server}</id>
+            <url>https://community.opengroup.org/api/v4/projects/25/packages/maven</url>
+        </snapshotRepository>
+    </distributionManagement>
+
     <properties>
         <maven.compiler.target>1.8</maven.compiler.target>
         <maven.compiler.source>1.8</maven.compiler.source>
@@ -43,15 +61,8 @@
         <!-- AWS specific packages -->
         <dependency>
             <groupId>org.opengroup.osdu.core.aws</groupId>
-            <artifactId>aws-osdu-util</artifactId>
-            <version>0.0.9</version>
-        </dependency>
-
-        <!-- AWS managed packages -->
-        <dependency>
-            <groupId>com.github.awslabs</groupId>
-            <artifactId>aws-request-signing-apache-interceptor</artifactId>
-            <version>deb7941</version>
+            <artifactId>os-core-lib-aws</artifactId>
+            <version>0.0.10</version>
         </dependency>
 
         <!-- Testing -->
@@ -138,44 +149,12 @@
         </dependency>
 
     </dependencies>
-
-    <repositories>
-        <repository>
-            <id>${gitlab-server}</id>
-            <url>https://community.opengroup.org/api/v4/groups/17/-/packages/maven</url>
-        </repository>
-
-        <repository>
-            <id>jitpack.io</id>
-            <url>https://jitpack.io</url>
-        </repository>
-    </repositories>
-
-    <distributionManagement>
-        <repository>
-            <id>${gitlab-server}</id>
-            <url>https://community.opengroup.org/api/v4/projects/25/packages/maven</url>
-        </repository>
-        <snapshotRepository>
-            <id>${gitlab-server}</id>
-            <url>https://community.opengroup.org/api/v4/projects/25/packages/maven</url>
-        </snapshotRepository>
-    </distributionManagement>
     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>2.5</version>
-                <configuration>
-                    <systemPropertyVariables>
-                        <AWS_COGNITO_CLIENT_ID>3rmgmg8mup281ttc1mbut1pimc</AWS_COGNITO_CLIENT_ID>
-                        <AWS_COGNITO_AUTH_FLOW>USER_PASSWORD_AUTH</AWS_COGNITO_AUTH_FLOW>
-                        <AWS_COGNITO_AUTH_PARAMS_USER>test-user-with-access@testing.com</AWS_COGNITO_AUTH_PARAMS_USER>
-                        <AWS_COGNITO_AUTH_PARAMS_USER_NO_ACCESS>test-user-without-access@testing.com</AWS_COGNITO_AUTH_PARAMS_USER_NO_ACCESS>
-                        <AWS_COGNITO_AUTH_PARAMS_PASSWORD>Password123*</AWS_COGNITO_AUTH_PARAMS_PASSWORD>
-                    </systemPropertyVariables>
-                </configuration>
             </plugin>
         </plugins>
     </build>
diff --git a/testing/indexer-test-aws/src/test/java/org/opengroup/osdu/util/ElasticUtilsAws.java b/testing/indexer-test-aws/src/test/java/org/opengroup/osdu/util/ElasticUtilsAws.java
index fff624c682d59709622511e86e0ce499191f8ff1..77a0440064ca1c7c41194dd3ca45a93613ac6b5f 100644
--- a/testing/indexer-test-aws/src/test/java/org/opengroup/osdu/util/ElasticUtilsAws.java
+++ b/testing/indexer-test-aws/src/test/java/org/opengroup/osdu/util/ElasticUtilsAws.java
@@ -14,24 +14,36 @@
 
 package org.opengroup.osdu.util;
 
-import com.amazonaws.auth.AWS4Signer;
-import com.amazonaws.auth.AWSCredentialsProvider;
-import com.amazonaws.http.AWSRequestSigningApacheInterceptor;
+import org.apache.http.Header;
 import org.apache.http.HttpHost;
-import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.message.BasicHeader;
 import org.elasticsearch.client.RestClient;
-import org.elasticsearch.client.RestHighLevelClient;
-import org.opengroup.osdu.core.aws.iam.IAMConfig;
+import org.elasticsearch.client.RestClientBuilder;
 
 public class ElasticUtilsAws extends ElasticUtils {
 
+    private static final int REST_CLIENT_CONNECT_TIMEOUT = 60000;
+    private static final int REST_CLIENT_SOCKET_TIMEOUT = 60000;
+    private static final int REST_CLIENT_RETRY_TIMEOUT = 60000;
+
     @Override
-    protected RestHighLevelClient createClient(String username, String password, String host) {
-        AWSCredentialsProvider credentials = new IAMConfig().amazonAWSCredentials();
-        AWS4Signer signer = new AWS4Signer();
-        signer.setServiceName(username);
-        signer.setRegionName(password);
-        HttpRequestInterceptor interceptor = new AWSRequestSigningApacheInterceptor(username, signer, credentials);
-        return new RestHighLevelClient(RestClient.builder(HttpHost.create(host)).setHttpClientConfigCallback(configCallBack -> configCallBack.addInterceptorLast(interceptor)));
+    public RestClientBuilder createClientBuilder(String host, String usernameAndPassword, int port) {
+        RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, "https"));
+        builder.setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder.setConnectTimeout(REST_CLIENT_CONNECT_TIMEOUT)
+                .setSocketTimeout(REST_CLIENT_SOCKET_TIMEOUT));
+        builder.setMaxRetryTimeoutMillis(REST_CLIENT_RETRY_TIMEOUT);
+        builder.setHttpClientConfigCallback(httpAsyncClientBuilder -> httpAsyncClientBuilder.setSSLHostnameVerifier((s, sslSession) -> true));
+
+        Header[] defaultHeaders = new Header[]{
+                new BasicHeader("client.transport.nodes_sampler_interval", "30s"),
+                new BasicHeader("client.transport.ping_timeout", "30s"),
+                new BasicHeader("client.transport.sniff", "false"),
+                new BasicHeader("request.headers.X-Found-Cluster", Config.getElasticHost()),
+                new BasicHeader("cluster.name", Config.getElasticHost()),
+                new BasicHeader("xpack.security.transport.ssl.enabled", Boolean.toString(true))
+        };
+
+        builder.setDefaultHeaders(defaultHeaders);
+        return builder;
     }
 }
diff --git a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/Config.java b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/Config.java
index c76cb4307f74d17257d0c540c8dd5f16be784421..f015548d59648765ae22ea33a698299622a13b70 100644
--- a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/Config.java
+++ b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/Config.java
@@ -22,6 +22,10 @@ public class Config {
     private static final String DEFAULT_ENTITLEMENTS_DOMAIN = "";
 
 
+    public static int getPort() {
+        return Integer.parseInt(getEnvironmentVariableOrDefaultValue("ELASTIC_PORT", String.valueOf(PORT)));
+    }
+
     public static String getOtherRelevantDataCountries() {
         return getEnvironmentVariableOrDefaultValue("OTHER_RELEVANT_DATA_COUNTRIES", DEFAULT_OTHER_RELEVANT_DATA_COUNTRIES);
     }
diff --git a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/ElasticUtils.java b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/ElasticUtils.java
index fa8e8f716b3031a86a0d417b3feeb3ebbbf9fdf8..230f44d2e13228dac9e0d8a39ff110570220103c 100644
--- a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/ElasticUtils.java
+++ b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/ElasticUtils.java
@@ -257,12 +257,24 @@ public class ElasticUtils {
     }
 
 
-    protected RestHighLevelClient createClient(String username, String password, String host) {
+    private RestHighLevelClient createClient(String username, String password, String host) {
 
         RestHighLevelClient restHighLevelClient;
         int port = Config.PORT;
         try {
             String rawString = String.format("%s:%s", username, password);
+
+            RestClientBuilder builder = createClientBuilder(host,rawString, port);
+
+            restHighLevelClient = new RestHighLevelClient(builder);
+
+        } catch (Exception e) {
+            throw new AssertionError("Setup elastic error");
+        }
+        return restHighLevelClient;
+    }
+
+    public RestClientBuilder createClientBuilder(String host, String usernameAndPassword, int port) {
             RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, "https"));
             builder.setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder.setConnectTimeout(REST_CLIENT_CONNECT_TIMEOUT)
                     .setSocketTimeout(REST_CLIENT_SOCKET_TIMEOUT));
@@ -275,15 +287,12 @@ public class ElasticUtils {
                     new BasicHeader("request.headers.X-Found-Cluster", Config.getElasticHost()),
                     new BasicHeader("cluster.name", Config.getElasticHost()),
                     new BasicHeader("xpack.security.transport.ssl.enabled", Boolean.toString(true)),
-                    new BasicHeader("Authorization", String.format("Basic %s", Base64.getEncoder().encodeToString(rawString.getBytes()))),
+                new BasicHeader("Authorization", String.format("Basic %s", Base64.getEncoder().encodeToString(usernameAndPassword.getBytes()))),
             };
 
             builder.setDefaultHeaders(defaultHeaders);
-            restHighLevelClient = new RestHighLevelClient(builder);
-
-        } catch (Exception e) {
-            throw new AssertionError("Setup elastic error");
-        }
-        return restHighLevelClient;
+        return builder;
     }
+
+
 }
\ No newline at end of file
diff --git a/testing/indexer-test-ibm/pom.xml b/testing/indexer-test-ibm/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4ff1ccc6d0bb0f83c9fbba32ef04c3191a328990
--- /dev/null
+++ b/testing/indexer-test-ibm/pom.xml
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.opengroup.osdu.indexer</groupId>
+    <artifactId>indexer-test-ibm</artifactId>
+    <version>0.0.2</version>
+    <packaging>jar</packaging>
+
+    <properties>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <cucumber.version>1.2.5</cucumber.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.api-client</groupId>
+            <artifactId>google-api-client</artifactId>
+            <version>1.28.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.fasterxml.jackson.core</groupId>
+                    <artifactId>jackson-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opengroup.osdu.indexer</groupId>
+            <artifactId>indexer-test-core</artifactId>
+            <version>0.0.2-SNAPSHOT</version>
+        </dependency>
+
+        <!-- Cucumber -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>info.cukes</groupId>
+            <artifactId>cucumber-java</artifactId>
+            <version>${cucumber.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>info.cukes</groupId>
+            <artifactId>cucumber-junit</artifactId>
+            <version>${cucumber.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Gson: Java to Json conversion -->
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.8.5</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-json-provider</artifactId>
+            <version>2.9.9</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>javax.json</artifactId>
+            <version>1.1.4</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-client</artifactId>
+            <version>1.19.4</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.2</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.6</version>
+        </dependency>
+
+        <!--Elasticsearch-->
+        <dependency>
+            <groupId>org.elasticsearch</groupId>
+            <artifactId>elasticsearch</artifactId>
+            <version>6.6.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch.client</groupId>
+            <artifactId>elasticsearch-rest-client</artifactId>
+            <version>6.6.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch.client</groupId>
+            <artifactId>elasticsearch-rest-high-level-client</artifactId>
+            <version>6.6.2</version>
+        </dependency>
+
+        <!--Logging-->
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-to-slf4j</artifactId>
+            <version>2.11.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-jdk14</artifactId>
+            <version>1.8.0-beta4</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>27.1-jre</version>
+        </dependency>
+    </dependencies>
+
+    <repositories>
+        <repository>
+            <id>${gitlab-server}</id>
+            <url>https://community.opengroup.org/api/v4/groups/17/-/packages/maven</url>
+        </repository>
+    </repositories>
+
+    <distributionManagement>
+        <repository>
+            <id>${gitlab-server}</id>
+            <url>https://community.opengroup.org/api/v4/projects/25/packages/maven</url>
+        </repository>
+        <snapshotRepository>
+            <id>${gitlab-server}</id>
+            <url>https://community.opengroup.org/api/v4/projects/25/packages/maven</url>
+        </snapshotRepository>
+    </distributionManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.22.2</version>
+                <executions>
+                    <execution>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>test</goal>
+                        </goals>
+                        <configuration>
+                            <excludes>
+                                <exclude>none</exclude>
+                            </excludes>
+                            <includes>
+                                <include>**/index</include>
+                            </includes>
+                        </configuration>
+                    </execution>
+                </executions>
+                <configuration>
+                    <trimStackTrace>false</trimStackTrace>
+                    <systemPropertyVariables>
+
+                        <KEYCLOAK_URL>keycloak-osdu-r2.osduadev-a1c3eaf78a86806e299f5f3f207556f0-0000.us-south.containers.appdomain.cloud</KEYCLOAK_URL>
+                        <KEYCLOAK_REALM>OSDU</KEYCLOAK_REALM>
+                        <KEYCLOAK_CLIENT_ID>osdu-login</KEYCLOAK_CLIENT_ID>
+                        <AUTH_USER_ACCESS>osdu-user</AUTH_USER_ACCESS>
+                        <AUTH_USER_ACCESS_PASSWORD>CHANGE_ME</AUTH_USER_ACCESS_PASSWORD>
+
+                    	  <ENTITLEMENTS_DOMAIN>ibm.com</ENTITLEMENTS_DOMAIN>
+                        <OTHER_RELEVANT_DATA_COUNTRIES>US</OTHER_RELEVANT_DATA_COUNTRIES>
+                        <!-- must be a valid, existing tag -->
+                        <LEGAL_TAG>opendes-public-usa-dataset</LEGAL_TAG>
+
+                     	  <!-- tenant1 MUSTG be opendes to work -->
+                        <DEFAULT_DATA_PARTITION_ID_TENANT1>opendes</DEFAULT_DATA_PARTITION_ID_TENANT1>
+						            <DEFAULT_DATA_PARTITION_ID_TENANT2>tenant2</DEFAULT_DATA_PARTITION_ID_TENANT2>
+
+                        <!-- test against OpenShift -->
+           				      <STORAGE_HOST>https://storage.osdu-r2-dev-a1c3eaf78a86806e299f5f3f207556f0-0000.us-south.containers.appdomain.cloud/api/storage/v2/</STORAGE_HOST>
+
+            						<!-- test locally -->
+            						<!--
+            						<STORAGE_HOST>http://localhost:8082/api/storage/v2/</STORAGE_HOST>
+            						-->
+						            <ELASTIC_HOST>CHANGE_ME.us-south.containers.appdomain.cloud</ELASTIC_HOST>
+                        <ELASTIC_USER_NAME>CHANGE_ME</ELASTIC_USER_NAME>
+                        <ELASTIC_PASSWORD>CHANGE_ME</ELASTIC_PASSWORD>
+                        <ELASTIC_PORT>443</ELASTIC_PORT>
+
+
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4978ddfccad197628432d4ccb8b5985b4af5017e
--- /dev/null
+++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java
@@ -0,0 +1,13 @@
+package org.opengroup.osdu.step_definitions.index.record;
+
+import cucumber.api.CucumberOptions;
+import cucumber.api.junit.Cucumber;
+import org.junit.runner.RunWith;
+
+@RunWith(Cucumber.class)
+@CucumberOptions(
+        features = "classpath:features/indexrecord/IndexRecord.feature",
+        glue = {"classpath:org.opengroup.osdu.step_definitions/index/record"},
+        plugin = {"pretty", "junit:target/cucumber-reports/TEST-indexrecord.xml"})
+public class RunTest {
+}
\ No newline at end of file
diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java
new file mode 100644
index 0000000000000000000000000000000000000000..126934d41fd3689a45218c6c78fe575741c33aa3
--- /dev/null
+++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java
@@ -0,0 +1,53 @@
+package org.opengroup.osdu.step_definitions.index.record;
+
+import org.opengroup.osdu.common.RecordSteps;
+import org.opengroup.osdu.util.ElasticUtilsIBM;
+import org.opengroup.osdu.util.IBMHTTPClient;
+
+import cucumber.api.DataTable;
+import cucumber.api.Scenario;
+import cucumber.api.java.Before;
+import cucumber.api.java.en.Given;
+import cucumber.api.java.en.Then;
+import cucumber.api.java.en.When;
+import lombok.extern.java.Log;
+
+@Log
+public class Steps extends RecordSteps {
+
+    public Steps() {
+        super(new IBMHTTPClient(), new ElasticUtilsIBM());
+    }
+
+    @Before
+    public void before(Scenario scenario) {
+        this.scenario = scenario;
+        this.httpClient = new IBMHTTPClient();
+    }
+
+    @Given("^the schema is created with the following kind$")
+    public void the_schema_is_created_with_the_following_kind(DataTable dataTable) {
+        super.the_schema_is_created_with_the_following_kind(dataTable);
+    }
+
+    @When("^I ingest records with the \"(.*?)\" with \"(.*?)\" for a given \"(.*?)\"$")
+    public void i_ingest_records_with_the_for_a_given(String record, String dataGroup, String kind) {
+        super.i_ingest_records_with_the_for_a_given(record, dataGroup, kind);
+    }
+
+    @Then("^I should get the (\\d+) documents for the \"([^\"]*)\" in the Elastic Search$")
+    public void i_should_get_the_documents_for_the_in_the_Elastic_Search(int expectedCount, String index) throws Throwable {
+        super.i_should_get_the_documents_for_the_in_the_Elastic_Search(expectedCount, index);
+    }
+
+    @Then("^I should get the elastic \"(.*?)\" for the \"([^\"]*)\" and \"([^\"]*)\" in the Elastic Search$")
+    public void i_should_get_the_elastic_for_the_tenant_testindex_timestamp_well_in_the_Elastic_Search(String expectedMapping, String type, String index) throws Throwable {
+        super.i_should_get_the_elastic_for_the_tenant_testindex_timestamp_well_in_the_Elastic_Search(expectedMapping, type, index);
+    }
+
+    @Then("^I should get the (\\d+) documents for the \"([^\"]*)\" in the Elastic Search with out \"(.*?)\"$")
+    public void iShouldGetTheNumberDocumentsForTheIndexInTheElasticSearchWithOutSkippedAttribute(int expectedCount, String index, String skippedAttributes) throws Throwable {
+        super.iShouldGetTheNumberDocumentsForTheIndexInTheElasticSearchWithOutSkippedAttribute(expectedCount, index, skippedAttributes);
+    }
+
+}
\ No newline at end of file
diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/ElasticUtilsIBM.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/ElasticUtilsIBM.java
new file mode 100644
index 0000000000000000000000000000000000000000..5174b7c932b7629c6f67747b3c45525ee2cb0812
--- /dev/null
+++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/ElasticUtilsIBM.java
@@ -0,0 +1,106 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.util;
+
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+import org.apache.http.message.BasicHeader;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
+import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
+import org.elasticsearch.client.RestHighLevelClient;
+
+import lombok.extern.java.Log;
+
+/**
+ * All util methods to use elastic apis for tests
+ * It should be used only in the Setup or TearDown phase of the test
+ */
+@Log
+public class ElasticUtilsIBM extends ElasticUtils {
+
+    private static final int REST_CLIENT_CONNECT_TIMEOUT = 5000;
+    private static final int REST_CLIENT_SOCKET_TIMEOUT = 60000;
+    private static final int REST_CLIENT_RETRY_TIMEOUT = 60000;
+
+    public ElasticUtilsIBM() {
+        super();
+    }
+
+    protected RestHighLevelClient createClient(String username, String password, String host) {
+
+        RestHighLevelClient restHighLevelClient;
+        int port = Config.getPort();
+        try {
+            String rawString = String.format("%s:%s", username, password);
+            RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, "https"));
+            builder.setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder.setConnectTimeout(REST_CLIENT_CONNECT_TIMEOUT)
+                    .setSocketTimeout(REST_CLIENT_SOCKET_TIMEOUT));
+            builder.setMaxRetryTimeoutMillis(REST_CLIENT_RETRY_TIMEOUT);
+
+            Header[] defaultHeaders = new Header[]{
+                    new BasicHeader("client.transport.nodes_sampler_interval", "30s"),
+                    new BasicHeader("client.transport.ping_timeout", "30s"),
+                    new BasicHeader("client.transport.sniff", "false"),
+                    new BasicHeader("request.headers.X-Found-Cluster", Config.getElasticHost()),
+                    new BasicHeader("cluster.name", Config.getElasticHost()),
+                    new BasicHeader("xpack.security.transport.ssl.enabled", Boolean.toString(true)),
+                    new BasicHeader("Authorization", String.format("Basic %s", Base64.getEncoder().encodeToString(rawString.getBytes()))),
+            };
+
+
+            SSLContext sslContext = SSLContext.getInstance("SSL");
+            // set up a TrustManager that trusts everything
+            sslContext.init(null, new TrustManager[]{new X509TrustManager() {
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+
+                public void checkClientTrusted(X509Certificate[] certs, String authType) {
+                }
+
+                public void checkServerTrusted(X509Certificate[] certs, String authType) {
+                }
+            }}, new SecureRandom());
+
+            builder.setHttpClientConfigCallback(new HttpClientConfigCallback() {
+
+                public HttpAsyncClientBuilder customizeHttpClient(
+                        HttpAsyncClientBuilder httpClientBuilder) {
+                    return httpClientBuilder
+                            .setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
+                            .setSSLContext(sslContext);
+                }
+            })
+                    .setDefaultHeaders(defaultHeaders);
+
+            restHighLevelClient = new RestHighLevelClient(builder);
+
+        } catch (Exception e) {
+            throw new AssertionError("Setup elastic error");
+        }
+        return restHighLevelClient;
+    }
+}
\ No newline at end of file
diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/IBMHTTPClient.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/IBMHTTPClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..c313c3833f037039a61f939ad31af6a04870f031
--- /dev/null
+++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/IBMHTTPClient.java
@@ -0,0 +1,39 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.util;
+
+import lombok.ToString;
+import lombok.extern.java.Log;
+
+import com.google.common.base.Strings;
+
+@Log
+@ToString
+public class IBMHTTPClient extends HTTPClient {
+	 private static String token = null;
+
+	    @Override
+	    public synchronized String getAccessToken() {
+	        if(Strings.isNullOrEmpty(token)) {
+	            try {
+	                token = "Bearer " + IdentityUtilIBM.getAccessToken();
+	            } catch (Exception e) {
+	                e.printStackTrace();
+	            }
+	        }
+	        return token;
+	    }
+    
+}
\ No newline at end of file
diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/IdentityUtilIBM.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/IdentityUtilIBM.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f2fdb06f2a0a28de33481be957bfc1a875716f3
--- /dev/null
+++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/IdentityUtilIBM.java
@@ -0,0 +1,32 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.util;
+
+import java.io.IOException;
+
+public class IdentityUtilIBM {
+		
+    public static String getAccessToken(){
+        try {
+        	String user = System.getProperty("AUTH_USER_ACCESS");
+        	String pass = System.getProperty("AUTH_USER_ACCESS_PASSWORD");
+			return KeyCloakProvider.getToken(user, pass);
+		} catch (IOException e) {
+			e.printStackTrace();
+			return null;
+		}
+    }
+
+}
diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/KeyCloakProvider.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/KeyCloakProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..d890d9a99b609e25dbe777c5f14fec9aa8f1846b
--- /dev/null
+++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/KeyCloakProvider.java
@@ -0,0 +1,144 @@
+// Copyright 2020 IBM Corp. All Rights Reserved.
+//
+// Licensed 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 org.opengroup.osdu.util;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+
+public class KeyCloakProvider {
+	
+    private static String url;
+    private static String realm;
+	private static String client_id;
+	private static String grant_type = "password";
+
+	static {
+	    disableSslVerification();
+	    url = System.getProperty("KEYCLOAK_URL");
+	    realm = System.getProperty("KEYCLOAK_REALM");
+	    client_id = System.getProperty("KEYCLOAK_CLIENT_ID");
+	}
+
+	private static void disableSslVerification() {
+	    try
+	    {
+	        // Create a trust manager that does not validate certificate chains
+	        TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
+	            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+	                return null;
+	            }
+	            public void checkClientTrusted(X509Certificate[] certs, String authType) {
+	            }
+	            public void checkServerTrusted(X509Certificate[] certs, String authType) {
+	            }
+	        }
+	        };
+
+	        // Install the all-trusting trust manager
+	        SSLContext sc = SSLContext.getInstance("SSL");
+	        sc.init(null, trustAllCerts, new java.security.SecureRandom());
+	        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+
+	        // Create all-trusting host name verifier
+	        HostnameVerifier allHostsValid = new HostnameVerifier() {
+	            public boolean verify(String hostname, SSLSession session) {
+	                return true;
+	            }
+	        };
+
+	        // Install the all-trusting host verifier
+	        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
+	    } catch (NoSuchAlgorithmException e) {
+	        e.printStackTrace();
+	    } catch (KeyManagementException e) {
+	        e.printStackTrace();
+	    }
+	}
+
+
+	public static String getToken(String user, String pwd) throws IOException {
+		String token_endpoint = String.format("https://%s/auth/realms/%s/protocol/openid-connect/token", url, realm);
+        URL url = new URL(token_endpoint);
+        HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
+        con.setRequestMethod("POST");
+        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put("grant_type", grant_type);
+        parameters.put("client_id", client_id);
+        parameters.put("username", user);
+        parameters.put("password", pwd);
+
+        con.setDoOutput(true);
+        DataOutputStream out = new DataOutputStream(con.getOutputStream());
+        out.writeBytes(getParamsString(parameters));
+        out.flush();
+        out.close();
+
+        BufferedReader in = new BufferedReader(
+                new InputStreamReader(con.getInputStream()));
+        String inputLine;
+        StringBuffer content = new StringBuffer();
+        while ((inputLine = in.readLine()) != null) {
+            content.append(inputLine);
+        }
+        in.close();
+
+        con.disconnect();
+
+        Gson gson = new Gson();
+        JsonObject jobj = gson.fromJson(content.toString(), JsonObject.class);
+        String token = jobj.get("access_token").getAsString();
+        return token;
+	}
+	
+	private static String getParamsString(Map<String, String> params)
+            throws UnsupportedEncodingException {
+        StringBuilder result = new StringBuilder();
+
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
+            result.append("=");
+            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
+            result.append("&");
+        }
+
+        String resultString = result.toString();
+        return resultString.length() > 0
+                ? resultString.substring(0, resultString.length() - 1)
+                : resultString;
+    }
+
+}
diff --git a/testing/pom.xml b/testing/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1bac11ececef04b5fad0f21269dd96c5b4f59a97
--- /dev/null
+++ b/testing/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2017-2019, The Open Group
+
+  Licensed 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>org.opengroup.osdu</groupId>
+	<artifactId>indexer-test</artifactId>
+	<version>0.0.5-SNAPSHOT</version>
+	<description>Indexer Service Integration Test Root Project</description>
+	<packaging>pom</packaging>
+
+	<licenses>
+		<license>
+			<name>Apache License, Version 2.0</name>
+			<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
+			<distribution>repo</distribution>
+		</license>
+	</licenses>
+
+	<modules>
+		<module>indexer-test-core</module>
+		<module>indexer-test-aws</module>
+		<module>indexer-test-azure</module>
+		<module>indexer-test-gcp</module>
+	</modules>
+
+	<repositories>
+		<repository>
+			<id>${gitlab-server}</id>
+			<url>https://community.opengroup.org/api/v4/groups/17/-/packages/maven</url>
+		</repository>
+	</repositories>
+
+	<distributionManagement>
+		<repository>
+			<id>${gitlab-server}</id>
+			<url>https://community.opengroup.org/api/v4/projects/19/packages/maven</url>
+		</repository>
+		<snapshotRepository>
+			<id>${gitlab-server}</id>
+			<url>https://community.opengroup.org/api/v4/projects/19/packages/maven</url>
+		</snapshotRepository>
+	</distributionManagement>
+</project>