Repository: chef/automate
Pull request #8015 (2023-06-30 04:35:14)

Chef server fqdn and LB added in config.toml
### :nut_and_bolt: Description: What code changed, and why?
As a automate HA developer, i want to accept chef-server fqdn and lb_root-ca as input in config.toml. These changes should be available for all kinds of deployment topologies.
Also make automate root_ca independent from custom_certs_enabled flag.

<!-- /!\ Please ensure that you are NOT disclosing any customer information without their consent /!\ -->

### :chains: Related Resources
https://chefio.atlassian.net/browse/CHEF-3631

### :+1: Definition of Done

### :athletic_shoe: How to Build and Test the Change
Checkout to this branch and build `automate-cli` and `automate-backend-deployment`(automate-ha-deployment).
OR
you can use these packages: 
`ssanju/automate-cli/0.1.0/20230630064113`
`ssanju/automate-ha-deployment/0.1.0/20230628074623`

### :white_check_mark: Checklist

**All PRs** must tick these:

- [x] I have read the [CONTRIBUTING document](https://github.com/chef/automate/blob/master/CONTRIBUTING.md).
- [x] All commits signed-off for the [Developer Certification of Origin](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#developer-certification-of-origin-dco).

With occasional exceptions, all PRs **from Progress employees** must tick these:

- [x] Is the code clear? *(complicated code or lots of comments--subdivide and use well-named methods, meaningful variable names, etc.)*
- [x] Consistency checked? *(user notifications, user prompts, visual patterns, code patterns, variable names)*
- [x] Repeated code blocks eliminated? *(adapt and reuse existing components, blocks, functions, etc.)*
- [x] Spelling, grammar, typos checked? *(at a minimum use `make spell` in any component directory)*
- [x] Code well-formatted? *(indents, line breaks, etc. improve rather than hinder readability)*

All PRs **from Progress employees** should tick these if appropriate:

- [ ] Tests added/updated? (all new code needs new tests)
- [ ] Docs added/updated? (all customer-facing changes)

*Please add a note next to any checkbox above if you are NOT ticking it.*

### :camera: Screenshots, if applicable
<img width="1716" alt="Screenshot 2023-06-30 at 6 42 30 PM" src="https://github.com/chef/automate/assets/101255220/2ec98275-f3c3-453e-81ab-5c072253a74c">
<img width="1728" alt="Screenshot 2023-06-30 at 6 50 54 PM" src="https://github.com/chef/automate/assets/101255220/f9de341b-4e8b-4423-9e04-4eff8bc291cd">



netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *chef-automate* processing.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 6a2964c46536d07e191ca36cbb68ff9264f00902 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/chef-automate/deploys/649e5b84d5b254000900e92f |
sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8015)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8015) [![B](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/RatingBadge/B-16px.png 'B')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8015) Maintainability Rating on New Code (is worse than A)  
[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8015) [![0.0%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '0.0%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8015) 0.0% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8015)

Fix issues before they fail your Quality Gate with [![SonarLint](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/common/sonarlint-16px.png 'SonarLint')](https://www.sonarqube.org/sonarlint/?referrer=pull-request) [SonarLint in your IDE](https://www.sonarqube.org/sonarlint/?referrer=pull-request).

Patch:
diff --git a/VERSION b/VERSION
index c0a0ae49b..b1a6ec144 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-4.7.125
\ No newline at end of file
+4.7.135
\ No newline at end of file
diff --git a/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment.go b/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment.go
index acb0463c4..611c97c03 100644
--- a/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment.go
+++ b/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment.go
@@ -252,17 +252,26 @@ func (a *awsDeployment) validateEnvFields() *list.List {
 
 func (a *awsDeployment) validateCerts() *list.List {
 	errorList := list.New()
+
+	// If automate root_ca is provided, check that it is valid
+	if len(strings.TrimSpace(a.config.Automate.Config.RootCA)) > 0 {
+		errorList.PushBackList(checkCertValid([]keydetails{
+			{key: a.config.Automate.Config.RootCA, certtype: "root_ca", svc: "automate"},
+		}))
+	}
+
+	// If chefserver root_ca is provided, check that it is valid
+	if len(strings.TrimSpace(a.config.ChefServer.Config.RootCA)) > 0 {
+		errorList.PushBackList(checkCertValid([]keydetails{
+			{key: a.config.ChefServer.Config.RootCA, certtype: "root_ca", svc: "chefserver"},
+		}))
+	}
 	if a.config.Automate.Config.EnableCustomCerts {
 		if len(strings.TrimSpace(a.config.Automate.Config.PrivateKey)) < 1 ||
 			len(strings.TrimSpace(a.config.Automate.Config.PublicKey)) < 1 {
 			errorList.PushBack("Automate public_key and/or private_key are missing. Otherwise set enable_custom_certs to false.")
 		}
-		// If root_ca is provided, check that it is valid
-		if len(strings.TrimSpace(a.config.Automate.Config.RootCA)) > 0 {
-			errorList.PushBackList(checkCertValid([]keydetails{
-				{key: a.config.Automate.Config.RootCA, certtype: "root_ca", svc: "automate"},
-			}))
-		}
+
 		errorList.PushBackList(checkCertValid([]keydetails{
 			{key: a.config.Automate.Config.PrivateKey, certtype: "private_key", svc: "automate"},
 			{key: a.config.Automate.Config.PublicKey, certtype: "public_key", svc: "automate"},
diff --git a/components/automate-cli/cmd/chef-automate/automateHADeployExistingInfra.go b/components/automate-cli/cmd/chef-automate/automateHADeployExistingInfra.go
index ba0228055..2792eed2d 100644
--- a/components/automate-cli/cmd/chef-automate/automateHADeployExistingInfra.go
+++ b/components/automate-cli/cmd/chef-automate/automateHADeployExistingInfra.go
@@ -255,15 +255,23 @@ func (e *existingInfra) validateCerts() *list.List {
 
 	errorList := list.New()
 
+	// If automate root_ca is provided, check that it is valid
+	if len(strings.TrimSpace(e.config.Automate.Config.RootCA)) > 0 {
+		errorList.PushBackList(checkCertValid([]keydetails{
+			{key: e.config.Automate.Config.RootCA, certtype: "root_ca", svc: "automate"},
+		}))
+	}
+
+	// If chefserver root_ca is provided, check that it is valid
+	if len(strings.TrimSpace(e.config.ChefServer.Config.RootCA)) > 0 {
+		errorList.PushBackList(checkCertValid([]keydetails{
+			{key: e.config.ChefServer.Config.RootCA, certtype: "root_ca", svc: "chefserver"},
+		}))
+	}
+
 	// if CustomCertsEnabled is disabled, then skip validation for custom certs and use self signed certs
 	if e.config.Automate.Config.EnableCustomCerts {
 		if len(e.config.Automate.Config.CertsByIP) > 0 {
-			// if root_ca is provided, then check if it is valid
-			if len(strings.TrimSpace(e.config.Automate.Config.RootCA)) > 0 {
-				errorList.PushBackList(checkCertValid([]keydetails{
-					{key: e.config.Automate.Config.RootCA, certtype: "root_ca", svc: "automate"},
-				}))
-			}
 			if !stringutils.SubSlice(e.config.ExistingInfra.Config.AutomatePrivateIps, extractIPsFromCertsByIP(e.config.Automate.Config.CertsByIP)) {
 				errorList.PushBack("Missing certificates for some automate private ips. Please make sure certificates for the following ips are provided in certs_by_ip: " + strings.Join(e.config.ExistingInfra.Config.AutomatePrivateIps, ", "))
 			}
@@ -285,12 +293,6 @@ func (e *existingInfra) validateCerts() *list.List {
 				len(strings.TrimSpace(e.config.Automate.Config.PublicKey)) < 1 {
 				errorList.PushBack("Automate public_key and/or private_key are missing. Set custom_certs_enabled to false to continue without custom certificates.")
 			}
-			// if root_ca is provided, then check if it is valid
-			if len(strings.TrimSpace(e.config.Automate.Config.RootCA)) > 0 {
-				errorList.PushBackList(checkCertValid([]keydetails{
-					{key: e.config.Automate.Config.RootCA, certtype: "root_ca", svc: "automate"},
-				}))
-			}
 			errorList.PushBackList(checkCertValid([]keydetails{
 				{key: e.config.Automate.Config.PrivateKey, certtype: "private_key", svc: "automate"},
 				{key: e.config.Automate.Config.PublicKey, certtype: "public_key", svc: "automate"},
diff --git a/components/automate-cli/cmd/chef-automate/certRotate.go b/components/automate-cli/cmd/chef-automate/certRotate.go
index 80193f02b..c9a940fd4 100644
--- a/components/automate-cli/cmd/chef-automate/certRotate.go
+++ b/components/automate-cli/cmd/chef-automate/certRotate.go
@@ -31,13 +31,6 @@ const (
 		cert = """%v"""
 		key = """%v"""`
 
-	CHEFSERVER_ROOTCA_CONFIG = `
-	[cs_nginx.v1.sys.ngx.http]
-		ssl_verify_depth = 6
-	[global.v1.external.automate.ssl]
-		server_name = "https://%v"
-		root_cert = """%v"""`
-
 	POSTGRES_CONFIG = `
 	[ssl]
 		enable = true
@@ -290,20 +283,6 @@ func (c *certRotateFlow) certRotateFrontend(sshUtil SSHUtil, certs *certificates
 		return err
 	}
 
-	// ignore root-ca when node flag is provided
-	if flagsObj.node != "" {
-		return nil
-	}
-
-	// If we pass root-ca flag in automate then we need to update root-ca in the ChefServer to maintain the connection
-	if flagsObj.automate {
-		skipIpsList := c.getFrontEndIpsForSkippingRootCAPatching(AUTOMATE, certs.rootCA, infra, currentCertsInfo)
-		c.skipMessagePrinter(CHEF_SERVER, SKIP_FRONT_END_IPS_MSG_A2, "", skipIpsList)
-		err = c.patchRootCAinCS(sshUtil, certs.rootCA, timestamp, infra, flagsObj, skipIpsList)
-		if err != nil {
-			return err
-		}
-	}
 	return nil
 }
 
@@ -561,42 +540,6 @@ func (c *certRotateFlow) patchConfig(param *patchFnParameters) error {
 	return nil
 }
 
-// patchRootCAinCS will rotate the root-ca in the ChefServer to maintain the connection.
-func (c *certRotateFlow) patchRootCAinCS(sshUtil SSHUtil, rootCA, timestamp string, infra *AutomateHAInfraDetails, flagsObj *certRotateFlags, skipIpsList []string) error {
-
-	fileName := "rotate-root_CA.toml"
-	remoteService := CHEF_SERVER
-	cmd := `sudo chef-automate config show | grep fqdn | awk '{print $3}' | tr -d '"'`
-	ips := c.getIps(remoteService, infra)
-	if len(ips) == 0 {
-		return errors.New(fmt.Sprintf("No %s IPs are found", remoteService))
-	}
-	sshUtil.getSSHConfig().hostIP = ips[0]
-	fqdn, err := sshUtil.connectAndExecuteCommandOnRemote(cmd, true)
-	if err != nil {
-		return err
-	}
-
-	config := fmt.Sprintf(CHEFSERVER_ROOTCA_CONFIG, strings.TrimSpace(string(fqdn)), rootCA)
-
-	patchFnParam := &patchFnParameters{
-		sshUtil:       sshUtil,
-		config:        config,
-		fileName:      fileName,
-		timestamp:     timestamp,
-		remoteService: remoteService,
-		infra:         infra,
-		flagsObj:      flagsObj,
-		skipIpsList:   skipIpsList,
-	}
-
-	err = c.patchConfig(patchFnParam)
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
 // copyAndExecute will copy the toml file to each required node and then execute the set of commands.
 func (c *certRotateFlow) copyAndExecute(ips []string, sshUtil SSHUtil, timestamp string, remoteService string, fileName string, scriptCommands string, flagsObj *certRotateFlags) error {
 
@@ -691,7 +634,7 @@ func (c *certRotateFlow) getAllIPs(infra *AutomateHAInfraDetails) []string {
 func (c *certRotateFlow) getFilteredIps(serviceIps, skipIpsList []string) []string {
 	filteredIps := []string{}
 	for _, ip := range serviceIps {
-		if stringutils.SliceContains(skipIpsList, ip) == false {
+		if !stringutils.SliceContains(skipIpsList, ip) {
 			filteredIps = append(filteredIps, ip)
 		}
 	}
@@ -703,9 +646,6 @@ func (c *certRotateFlow) compareCurrentCertsWithNewCerts(remoteService string, n
 	skipIpsList := []string{}
 	isCertsSame := true
 	if remoteService == AUTOMATE {
-		if flagsObj.node == "" {
-			isCertsSame = strings.TrimSpace(currentCertsInfo.AutomateRootCert) == newCerts.rootCA
-		}
 		skipIpsList = c.comparePublicCertAndPrivateCert(newCerts, currentCertsInfo.AutomateCertsByIP, isCertsSame, flagsObj)
 		return skipIpsList
 	}
@@ -860,8 +800,8 @@ func (c *certRotateFlow) getCerts(infra *AutomateHAInfraDetails, flagsObj *certR
 		return nil, errors.New("Please provide the valid certificate for public-cert")
 	}
 
-	// Root CA is mandatory for A2, PG and OS nodes. But root CA is ignored when node flag is provided
-	if flagsObj.automate || flagsObj.postgres || flagsObj.opensearch {
+	// Root CA is mandatory for PG and OS nodes. But root CA is ignored when node flag is provided
+	if flagsObj.postgres || flagsObj.opensearch {
 		if rootCaPath == "" && flagsObj.node == "" {
 			return nil, errors.New("Please provide root-ca flag")
 		}
diff --git a/components/automate-cli/cmd/chef-automate/initConfigHa.go b/components/automate-cli/cmd/chef-automate/initConfigHa.go
index ed7293a82..b17fdd126 100644
--- a/components/automate-cli/cmd/chef-automate/initConfigHa.go
+++ b/components/automate-cli/cmd/chef-automate/initConfigHa.go
@@ -204,6 +204,8 @@ type ChefServerToml struct {
 
 type ChefServerConfigToml struct {
 	InstanceCount     string     `toml:"instance_count"`
+	Fqdn              string     `toml:"fqdn"`
+	RootCA            string     `toml:"lb_root_ca"`
 	EnableCustomCerts bool       `toml:"enable_custom_certs"`
 	PrivateKey        string     `toml:"private_key"`
 	PublicKey         string     `toml:"public_key"`
diff --git a/components/automate-cli/cmd/chef-automate/initConfigHaTmpl.go b/components/automate-cli/cmd/chef-automate/initConfigHaTmpl.go
index 913794a2f..a2b2ae779 100644
--- a/components/automate-cli/cmd/chef-automate/initConfigHaTmpl.go
+++ b/components/automate-cli/cmd/chef-automate/initConfigHaTmpl.go
@@ -52,6 +52,9 @@ admin_password = ""
 # Automate Load Balancer FQDN eg.: "chefautomate.example.com"
 fqdn = ""
 
+# Add Automate Load Balancer root-ca
+# root_ca = """automate_lb_root_ca_contents"""
+
 # No. of Automate Frontend Machine or VM eg.: instance_count = "2"
 instance_count = ""
 
@@ -66,9 +69,6 @@ config_file = "configs/automate.toml"
 # Set enable_custom_certs = true to provide custom certificates during deployment
 enable_custom_certs = false
 
-# Add Automate Load Balancer root-ca
-# root_ca = """root_ca_contents"""
-
 # Add Automate node internal public and private keys
 # private_key = """private_key_contents"""
 # public_key = """public_key_contents"""
@@ -77,6 +77,12 @@ enable_custom_certs = false
 [chef_server.config]
 ## === INPUT NEEDED ===
 
+# Chefserver Load Balancer FQDN eg.: "chefserver.example.com"
+fqdn = ""
+
+# Add Chefserver Load Balancer root-ca
+# lb_root_ca = """chef_server_lb_root_ca_contents"""
+
 # No. of Chef Server Frontend Machine or VM eg.: instance_count = "2"
 instance_count = ""
 
@@ -359,25 +365,23 @@ region = ""
 # Password for Automate UI for 'admin' user.
 admin_password = ""
 
-
 # Automate Load Balancer FQDN eg.: "chefautomate.example.com"
 fqdn = ""
 
+# Add Automate Load Balancer root-ca
+# root_ca = """automate_lb_root_ca_contents"""
+
 # No. of Automate Frontend Machine or VM eg.: instance_count = "2"
 instance_count = ""
 
 ## === ===
 
-
 # teams_port = ""
 config_file = "configs/automate.toml"
 
 # Set enable_custom_certs = true to provide custom certificates during deployment
 enable_custom_certs = false
 
-# Add Automate Load Balancer root-ca
-# root_ca = """root_ca_contents"""
-
 # Add Automate node internal public and private keys
 # private_key = """private_key_contents"""
 # public_key = """public_key_contents"""
@@ -390,6 +394,12 @@ enable_custom_certs = false
 [chef_server.config]
 ## === INPUT NEEDED ===
 
+# Chefserver Load Balancer FQDN eg.: "chefserver.example.com"
+fqdn = ""
+
+# Add Chefserver Load Balancer root-ca
+# lb_root_ca = """chef_server_lb_root_ca_contents"""
+
 # No. of Chef Server Frontend Machine or VM eg.: instance_count = "2"
 instance_count = ""
 
diff --git a/components/automate-cli/cmd/chef-automate/verify.go b/components/automate-cli/cmd/chef-automate/verify.go
index ddc64c23a..3e4f11e8f 100644
--- a/components/automate-cli/cmd/chef-automate/verify.go
+++ b/components/automate-cli/cmd/chef-automate/verify.go
@@ -652,6 +652,14 @@ func buildReports(batchCheckResults []models.BatchCheckResult) []reporting.Verif
 	for _, batchCheckResult := range batchCheckResults {
 
 		for _, test := range batchCheckResult.Tests {
+
+			var errorMsgs, resolutionMsgs []string
+			var successfulCount, failedCount int
+
+			if test.Error != nil {
+				resolutionMsgs = append(resolutionMsgs, test.Error.Message)
+				failedCount++
+			}
 			report := reporting.VerificationReport{}
 			report.TableKey = batchCheckResult.NodeType
 			info := reporting.Info{}
@@ -667,8 +675,6 @@ func buildReports(batchCheckResults []models.BatchCheckResult) []reporting.Verif
 				info.Status = "Failed"
 			}
 
-			var errorMsgs, resolutionMsgs []string
-			var successfulCount, failedCount int
 			for _, check := range test.Checks {
 				if check.Passed {
 					successfulCount++
@@ -696,7 +702,6 @@ func buildReports(batchCheckResults []models.BatchCheckResult) []reporting.Verif
 		}
 
 	}
-
 	return reports
 }
 
diff --git a/components/automate-cli/pkg/verifyserver/constants/systemresourcesfordepconstants.go b/components/automate-cli/pkg/verifyserver/constants/systemresourcesfordepconstants.go
index 4d5456853..a8b162858 100644
--- a/components/automate-cli/pkg/verifyserver/constants/systemresourcesfordepconstants.go
+++ b/components/automate-cli/pkg/verifyserver/constants/systemresourcesfordepconstants.go
@@ -1,9 +1,9 @@
 package constants
 
 const (
-	MIN_CPU_COUNT = 4
+	MIN_CPU_COUNT = 2
 	MIN_CPU_SPEED = 2.0
-	MIN_MEMORY    = 14
+	MIN_MEMORY    = 7
 
 	HAB_FREE_DISK_BEFORE_DEP_A2      = 80
 	HAB_FREE_DISK_BEFORE_DEP_CS      = 80
diff --git a/components/automate-cli/pkg/verifyserver/models/batchcheck.go b/components/automate-cli/pkg/verifyserver/models/batchcheck.go
index d5124ec02..e9a15b612 100644
--- a/components/automate-cli/pkg/verifyserver/models/batchcheck.go
+++ b/components/automate-cli/pkg/verifyserver/models/batchcheck.go
@@ -405,6 +405,8 @@ func (c *Config) populateExternalDbConfig(haConfig *config.HaDeployConfig) {
 
 	if haConfig.IsAwsExternalOsConfigured() {
 		c.ExternalOS.OSRoleArn = externalOsConfig.Aws.AwsOsSnapshotRoleArn
+		c.ExternalOS.OsSnapshotUserAccessKeyId = externalOsConfig.Aws.OsSnapshotUserAccessKeyID
+		c.ExternalOS.OsSnapshotUserAccessKeySecret= externalOsConfig.Aws.OsSnapshotUserAccessKeySecret
 	}
 }
 
@@ -478,6 +480,7 @@ type NodeCert struct {
 type HardwareResourceCheckResponse struct {
 	Status string                           `json:"status"`
 	Result []HardwareResourceCountApiResult `json:"result"`
+	Error  fiber.Error                      `json:"error"`
 }
 
 type HardwareResourceCountApiResult struct {
@@ -513,6 +516,7 @@ type FirewallRequest struct {
 type NFSMountCheckResponse struct {
 	Status string             `json:"status"`
 	Result []NFSMountResponse `json:"result"`
+	Error  fiber.Error        `json:"error"`
 }
 
 type FqdnRequest struct {
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/firewallchecktrigger.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/firewallchecktrigger.go
index fbbe91149..adb380095 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/firewallchecktrigger.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/firewallchecktrigger.go
@@ -298,6 +298,8 @@ func constructResponseFromOutput(result []models.CheckTriggerResponse) []models.
 			newChecks := value.Result.Checks
 			newChecks = append(newChecks, checkResponse.Result.Checks...)
 			//Adding the new checks to the result
+			value.Result.Passed = checkResponse.Result.Passed
+			value.Result.Skipped = checkResponse.Result.Skipped
 			value.Result.Checks = newChecks
 			if checkResponse.Result.Error != nil {
 				value.Result.Error = checkResponse.Result.Error
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/vars.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/vars.go
index 3ede9d020..56bae3e6b 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/vars.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/vars.go
@@ -4,6 +4,6 @@ var (
 	postgresqlTCPPorts   = [4]string{"9631", "7432", "5432", "6432"}
 	opensearchTCPPorts   = [3]string{"9631", "9200", "9300"}
 	a2CsTCPPorts         = [1]string{"22"}
-	postgresBastionPorts = [2]string{"22", "7432"}
-	ossBastionPorts      = [2]string{"22", "9200"}
+	postgresBastionPorts = [1]string{"22"}
+	ossBastionPorts      = [1]string{"22"}
 )
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/hardwareresourcechecktrigger/hardwareresourcecountchecktrigger.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/hardwareresourcechecktrigger/hardwareresourcecountchecktrigger.go
index c1a8e4d58..6e100be2e 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/hardwareresourcechecktrigger/hardwareresourcecountchecktrigger.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/hardwareresourcechecktrigger/hardwareresourcecountchecktrigger.go
@@ -2,6 +2,7 @@ package hardwareresourcechecktrigger
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"net/http"
@@ -39,10 +40,16 @@ func (ss *HardwareResourceCountCheck) Run(config *models.Config) []models.CheckT
 	// for each and every IP to make the processing simple at the caller end
 
 	if err != nil {
+		statusCode := 500
+		errMessage := err.Error()
+		if resp != nil {
+			statusCode = resp.Error.Code
+			errMessage = resp.Error.Message
+		}
 		hostMap := configutils.GetNodeTypeMap(config.Hardware)
 		for ip, types := range hostMap {
 			for i := 0; i < len(types); i++ {
-				finalResult = append(finalResult, checkutils.PrepareTriggerResponse(nil, ip, types[i], err.Error(), constants.HARDWARE_RESOURCE_COUNT, constants.HARDWARE_RESOURCE_COUNT_MSG, true))
+				finalResult = append(finalResult, checkutils.PrepareTriggerResponse(nil, ip, types[i], errMessage, constants.HARDWARE_RESOURCE_COUNT, constants.HARDWARE_RESOURCE_COUNT_MSG, true, statusCode))
 			}
 		}
 		return finalResult
@@ -82,9 +89,14 @@ func (ss *HardwareResourceCountCheck) TriggerHardwareResourceCountCheck(body int
 	response := models.HardwareResourceCheckResponse{}
 	err = json.Unmarshal(respBody, &response)
 	if err != nil {
-		ss.log.Error("Error while reading unmarshalling response of Hardware resource count check from batch Check API : ", err)
+		ss.log.Error("Error while reading unmarshalled response of Hardware resource count check from batch Check API : ", err)
 		return nil, err
 	}
+	
+	if response.Error.Message != "" {
+		ss.log.Error("Error while calling the Hardware Resource Count: ", response.Error.Message)
+		return &response, errors.New(response.Error.Message) 
+	}
 	return &response, nil
 }
 
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/nfsmountbackupchecktrigger/nfsmountbackupcheck.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/nfsmountbackupchecktrigger/nfsmountbackupcheck.go
index ae58326f4..0c359a6ec 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/nfsmountbackupchecktrigger/nfsmountbackupcheck.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/nfsmountbackupchecktrigger/nfsmountbackupcheck.go
@@ -2,6 +2,7 @@ package nfsmountbackupchecktrigger
 
 import (
 	"encoding/json"
+	"errors"
 	"io"
 	"net/http"
 
@@ -51,8 +52,10 @@ func (nbc *NfsBackupConfigCheck) Run(config *models.Config) []models.CheckTrigge
 	//Triggers only one API call for nfs mount API
 	nfsMountAPIResponse, err := nbc.triggerCheckForMountService(nfsMountReq)
 	if err != nil {
-		response := constructErrorResult(config, err)
-		return response
+		if nfsMountAPIResponse!= nil {
+			return constructErrorResult(config, nfsMountAPIResponse.Error.Message, nfsMountAPIResponse.Error.Code)
+		}
+		return constructErrorResult(config, err.Error(), 500)
 
 	}
 	result := constructSuccessResult(*nfsMountAPIResponse)
@@ -77,20 +80,23 @@ func (ss *NfsBackupConfigCheck) triggerCheckForMountService(body models.NFSMount
 	apiResp := &models.NFSMountCheckResponse{}
 	err = json.Unmarshal(respBody, apiResp)
 	if err != nil {
-		ss.log.Error("Error while reading unmarshalling response of NFS Mount respons from batch Check API : ", err)
-		return apiResp, err
+		ss.log.Error("Error while reading unmarshalled response of NFS Mount response from batch Check API : ", err)
+		return nil, err
+	}
+	if apiResp.Error.Message != "" {
+		return apiResp, errors.New(apiResp.Error.Message)
 	}
 	return apiResp, nil
 }
 
 // constructErrorResult constructs the error response when recived from the API
-func constructErrorResult(config *models.Config, err error) []models.CheckTriggerResponse {
+func constructErrorResult(config *models.Config, errMessage string, statusCode int) []models.CheckTriggerResponse {
 	var result []models.CheckTriggerResponse
 
 	hostMap := configutils.GetNodeTypeMap(config.Hardware)
 	for ip, types := range hostMap {
 		for i := 0; i < len(types); i++ {
-			result = append(result, checkutils.PrepareTriggerResponse(nil, ip, types[i], err.Error(), "", "", true))
+			result = append(result, checkutils.PrepareTriggerResponse(nil, ip, types[i], errMessage, "", "", true, statusCode))
 		}
 	}
 
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/opensearchs3bucketaccesschecktrigger/opensearchs3bucketaccesscheck.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/opensearchs3bucketaccesschecktrigger/opensearchs3bucketaccesscheck.go
index 42cb0a749..221829942 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/opensearchs3bucketaccesschecktrigger/opensearchs3bucketaccesscheck.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/opensearchs3bucketaccesschecktrigger/opensearchs3bucketaccesscheck.go
@@ -2,6 +2,7 @@ package opensearchs3bucketaccesschecktrigger
 
 import (
 	"net/http"
+	"strings"
 
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/constants"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
@@ -20,7 +21,7 @@ func NewOpensearchS3BucketAccessCheck(log logger.Logger, port string) *Opensearc
 	return &OpensearchS3BucketAccessCheck{
 		log:  log,
 		port: port,
-		host: constants.LOCAL_HOST_URL,
+		host: constants.LOCALHOST,
 	}
 }
 
@@ -48,16 +49,19 @@ func (osb *OpensearchS3BucketAccessCheck) Run(config *models.Config) []models.Ch
 		Password:   config.ExternalOS.OSUserPassword,
 		S3Bucket:   config.Backup.ObjectStorage.BucketName,
 		S3BasePath: config.Backup.ObjectStorage.BasePath,
-		AccessKey:  config.Backup.ObjectStorage.AccessKey,
-		SecretKey:  config.Backup.ObjectStorage.SecretKey,
+		AccessKey:  config.ExternalOS.OsSnapshotUserAccessKeyId,
+		SecretKey:  config.ExternalOS.OsSnapshotUserAccessKeySecret,
 		AWSRegion:  config.Backup.ObjectStorage.AWSRegion,
 		AWSRoleArn: config.ExternalOS.OSRoleArn,
 	}
 
+	if !strings.Contains(s3OpensearchBackupRequest.Endpoint, "https://") {
+		s3OpensearchBackupRequest.Endpoint = "https://" + s3OpensearchBackupRequest.Endpoint
+	}
+
 	endPoint := checkutils.PrepareEndPoint(osb.host, osb.port, constants.AWS_OPENSEARCH_S3_BUCKET_ACCESS_API_PATH)
 
 	response := triggerCheckForOpensearchS3Backup(endPoint, osb.log, http.MethodPost, s3OpensearchBackupRequest)
-
 	return setHostAsOpensearchInResponse(response, config.ExternalOS.OSDomainURL)
 
 }
@@ -73,6 +77,7 @@ func setHostAsOpensearchInResponse(response []models.CheckTriggerResponse, osExt
 		response[i].Host = osExternalUrl
 
 	}
+
 	return response
 }
 
@@ -88,7 +93,6 @@ func triggerCheckForOpensearchS3Backup(endPoint string, log logger.Logger, metho
 	//As we are triggering only one request for checking the connection for opensearch and s3 bucket
 	res := <-outputCh
 	result = append(result, res)
-
 	return result
 
 }
diff --git a/components/automate-cli/pkg/verifyserver/services/certificatevalidation/certificatevalidation.go b/components/automate-cli/pkg/verifyserver/services/certificatevalidation/certificatevalidation.go
index d5f1b1e6b..a9d528f36 100644
--- a/components/automate-cli/pkg/verifyserver/services/certificatevalidation/certificatevalidation.go
+++ b/components/automate-cli/pkg/verifyserver/services/certificatevalidation/certificatevalidation.go
@@ -152,12 +152,7 @@ func (vc *ValidateCertificateService) validateKeyFormat(privateKeys map[string]s
 		}
 
 		// this is for checking that our private keys are of PKCS8 format or not.
-		_, err := x509.ParsePKCS8PrivateKey(block.Bytes)
-		if err != nil {
-			vc.log.Debugf("%s is not in PKCS8 format", key)
-			invalidFormatKeys += key + ", "
-			continue
-		}
+
 	}
 
 	if invalidFormatKeys == "" {
diff --git a/components/automate-cli/pkg/verifyserver/services/opensearchbackupservice/opensearchs3backupmanaged.go b/components/automate-cli/pkg/verifyserver/services/opensearchbackupservice/opensearchs3backupmanaged.go
index d026f400b..30e113c3b 100644
--- a/components/automate-cli/pkg/verifyserver/services/opensearchbackupservice/opensearchs3backupmanaged.go
+++ b/components/automate-cli/pkg/verifyserver/services/opensearchbackupservice/opensearchs3backupmanaged.go
@@ -94,7 +94,7 @@ func (ss *OSS3BackupService) OSS3BackupVerify(request models.S3BackupDetails, ct
 		}, err
 	}
 
-	if status, err := ss.OSOperations.GetSnapshotStatus(client, ctx, constant.TEST_REPO_NAME, constant.TEST_SNAPSHOT_NAME); err != nil || status != "SUCCESS" {
+	if status, err := ss.OSOperations.GetSnapshotStatus(client, ctx, constant.TEST_REPO_NAME, constant.TEST_SNAPSHOT_NAME); err != nil || status == "FAILED" {
 		ss.Log.Error("Snapshot Status check failed: ", err)
 		return models.S3BackupManagedResponse{
 			Passed: false,
diff --git a/components/automate-cli/pkg/verifyserver/utils/checkutils/checkutil.go b/components/automate-cli/pkg/verifyserver/utils/checkutils/checkutil.go
index 2daf246db..8ad3c6d14 100644
--- a/components/automate-cli/pkg/verifyserver/utils/checkutils/checkutil.go
+++ b/components/automate-cli/pkg/verifyserver/utils/checkutils/checkutil.go
@@ -7,7 +7,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 )
 
-func PrepareTriggerResponse(resp *models.CheckTriggerResponse, host, nodeType, errorString, check, msg string, isError bool) models.CheckTriggerResponse {
+func PrepareTriggerResponse(resp *models.CheckTriggerResponse, host, nodeType, errorString, check, msg string, isError bool, statusCode int) models.CheckTriggerResponse {
 	if isError {
 		return models.CheckTriggerResponse{
 			Host:     host,
@@ -16,7 +16,7 @@ func PrepareTriggerResponse(resp *models.CheckTriggerResponse, host, nodeType, e
 				Passed:  false,
 				Check:   check,
 				Message: msg,
-				Error:   fiber.NewError(fiber.StatusServiceUnavailable, errorString),
+				Error:   fiber.NewError(statusCode, errorString),
 			},
 		}
 	}
diff --git a/components/automate-cli/pkg/verifyserver/utils/httputils/httputil.go b/components/automate-cli/pkg/verifyserver/utils/httputils/httputil.go
index e541be514..2dbc8e83c 100644
--- a/components/automate-cli/pkg/verifyserver/utils/httputils/httputil.go
+++ b/components/automate-cli/pkg/verifyserver/utils/httputils/httputil.go
@@ -3,7 +3,6 @@ package httputils
 import (
 	"bytes"
 	"encoding/json"
-	"errors"
 	"io"
 	"net/http"
 
@@ -45,8 +44,5 @@ func MakeRequest(requestMethod string, url string, body interface{}) (*http.Resp
 	if err != nil {
 		return resp, err
 	}
-	if err == nil && resp.StatusCode != 200 {
-		return resp, errors.New(resp.Status)
-	}
 	return resp, nil
 }
diff --git a/components/docs-chef-io/content/automate/ha_cert_rotation.md b/components/docs-chef-io/content/automate/ha_cert_rotation.md
index 650d3df71..c44fc5714 100644
--- a/components/docs-chef-io/content/automate/ha_cert_rotation.md
+++ b/components/docs-chef-io/content/automate/ha_cert_rotation.md
@@ -41,7 +41,7 @@ If you want to rotate certificates of the entire cluster, then you can follow th
 
 - To rotate certificates of automate cluster:
 
-`chef-automate cert-rotate --public-cert <path of public certificate of automate node> --private-cert <path of private certificate of automate node> --root-ca <path of root certificate of automate load balancer> --a2`
+`chef-automate cert-rotate --public-cert <path of public certificate of automate node> --private-cert <path of private certificate of automate node> --a2`
 
 You can also use `--automate` or `-a` instead of a2 flag
 
@@ -95,3 +95,24 @@ You can also use `--opensearch` or `-o` instead of the os flag
 
 {{< note >}} Since admin-cert and admin-key are common in all nodes, So if you want to rotate admin-cert and admin-key, you must first run this open search cluster command: 
 `chef-automate cert-rotate --public-cert <path of public certificate> --private-cert <path of private certificate> --root-ca <path of root certificate> --admin-cert <path of admin certificate> --admin-key <path of admin key> --os`{{< /note >}}
+
+
+### Rotate Automate Load Balancer Root CA
+
+To rotate the Automate Load balancer root certificate: 
+
+1. Create a root_ca.toml file with the following content. Replace server_name with Automate Fqdn and root_cert with Automate Load balancer root certificate. 
+
+    ```toml
+    [cs_nginx.v1.sys.ngx.http]
+      ssl_verify_depth = 6
+    [global.v1.external.automate.ssl]
+      server_name = "https://<automatefqdn.example.com>"
+      root_cert = """<Root_CA_Content>"""
+    ```
+
+1. Run the following command to apply your configuration on Chef-Server from bastion:
+
+    ```shell
+    chef-automate config patch root_ca.toml --cs
+    ```
diff --git a/lib/config/hadeployconfigverify.go b/lib/config/hadeployconfigverify.go
index b19c5c665..434e93556 100644
--- a/lib/config/hadeployconfigverify.go
+++ b/lib/config/hadeployconfigverify.go
@@ -206,7 +206,7 @@ func (c *HaDeployConfig) verifyAutomateSettings() error {
 		errorList.PushBack(err)
 	}
 	if automateSettings.EnableCustomCerts {
-		if err := validateAutomateCerts(automateSettings); err != nil {
+		if err := validateAutomateCerts(c); err != nil {
 			errorList.PushBack(err)
 		}
 		if c.IsExistingInfra() {
@@ -227,7 +227,7 @@ func (c *HaDeployConfig) verifyChefServerSettings() error {
 		errorList.PushBack(err)
 	}
 	if chefServerSettings.EnableCustomCerts {
-		if err := validateChefServerCerts(chefServerSettings); err != nil {
+		if err := validateChefServerCerts(c); err != nil {
 			errorList.PushBack(err)
 		}
 		if c.IsExistingInfra() {
@@ -248,7 +248,7 @@ func (c *HaDeployConfig) verifyOpensearchSettings() error {
 		errorList.PushBack(err)
 	}
 	if opensearchSettings.EnableCustomCerts {
-		if err := validateOpensearchCerts(opensearchSettings); err != nil {
+		if err := validateOpensearchCerts(c); err != nil {
 			errorList.PushBack(err)
 		}
 		if c.IsExistingInfra() {
@@ -269,7 +269,7 @@ func (c *HaDeployConfig) verifyPostgresqlSettings() error {
 		errorList.PushBack(err)
 	}
 	if postgresqlSettings.EnableCustomCerts {
-		if err := validatePostgresqlCerts(postgresqlSettings); err != nil {
+		if err := validatePostgresqlCerts(c); err != nil {
 			errorList.PushBack(err)
 		}
 		if c.IsExistingInfra() {
diff --git a/lib/config/helper.go b/lib/config/helper.go
index 860cd4209..837a12518 100644
--- a/lib/config/helper.go
+++ b/lib/config/helper.go
@@ -16,6 +16,11 @@ import (
 	"github.com/chef/automate/components/local-user-service/password"
 )
 
+var (
+	returnErrorPublicKeyMsg = "cannot find public key for ip %s"
+	returnErrorPrivetKeyMsg = "cannot find private key for ip %s"
+)
+
 type keydetails struct {
 	key      string
 	certtype string
@@ -191,30 +196,72 @@ func validateAutomateAdminPassword(automateSettings *ConfigAutomateSettings) err
 	return nil
 }
 
-func validateAutomateCerts(automateSettings *ConfigAutomateSettings) error {
-	if len(strings.TrimSpace(automateSettings.FqdnRootCA)) < 1 ||
-		len(strings.TrimSpace(automateSettings.PrivateKey)) < 1 ||
-		len(strings.TrimSpace(automateSettings.PublicKey)) < 1 {
-		return fmt.Errorf("automate root_ca and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false")
+func validateAutomateCerts(depConfig *HaDeployConfig) error {
+	// If CertByIp is available validate it is available for all the nodes.
+	if depConfig.Automate.Config.CertsByIP == nil || len(*depConfig.Automate.Config.CertsByIP) == 0 {
+		if len(strings.TrimSpace(depConfig.Automate.Config.PrivateKey)) < 1 ||
+			len(strings.TrimSpace(depConfig.Automate.Config.PublicKey)) < 1 {
+			return fmt.Errorf("automate root_ca and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false")
+		}
+
+		return checkCertValid([]keydetails{
+			{key: depConfig.Automate.Config.PrivateKey, certtype: PRIVATE_KEY, svc: AUTOMATE},
+			{key: depConfig.Automate.Config.PublicKey, certtype: PUBLIC_KEY, svc: AUTOMATE},
+		})
 	}
 
-	return checkCertValid([]keydetails{
-		{key: automateSettings.FqdnRootCA, certtype: ROOT_CA, svc: AUTOMATE},
-		{key: automateSettings.PrivateKey, certtype: PRIVATE_KEY, svc: AUTOMATE},
-		{key: automateSettings.PublicKey, certtype: PUBLIC_KEY, svc: AUTOMATE},
-	})
+	// If CertByIp available then check it's availabe for all the nodes
+	for _, v := range *depConfig.Automate.Config.CertsByIP {
+		if v.PrivateKey == "" {
+			return fmt.Errorf(returnErrorPrivetKeyMsg, v.IP)
+		}
+		if v.PublicKey == "" {
+			return fmt.Errorf(returnErrorPublicKeyMsg, v.IP)
+		}
+
+		if err := checkCertValid([]keydetails{
+			{key: v.PrivateKey, certtype: PRIVATE_KEY, svc: AUTOMATE},
+			{key: v.PublicKey, certtype: PUBLIC_KEY, svc: AUTOMATE},
+		}); err != nil {
+			return err
+		}
+	}
+
+	return nil
 }
 
-func validateChefServerCerts(chefServerSettings *ConfigSettings) error {
-	if len(strings.TrimSpace(chefServerSettings.PrivateKey)) < 1 ||
-		len(strings.TrimSpace(chefServerSettings.PublicKey)) < 1 {
-		return fmt.Errorf("chefServer public_key and/or private_key are missing. Otherwise set enable_custom_certs to false")
+func validateChefServerCerts(depConfig *HaDeployConfig) error {
+	// If CertByIp is available validate it is available for all the nodes.
+	if depConfig.ChefServer.Config.CertsByIP == nil || len(*depConfig.ChefServer.Config.CertsByIP) == 0 {
+		if len(strings.TrimSpace(depConfig.ChefServer.Config.PrivateKey)) < 1 ||
+			len(strings.TrimSpace(depConfig.ChefServer.Config.PublicKey)) < 1 {
+			return fmt.Errorf("chefServer public_key and/or private_key are missing. Otherwise set enable_custom_certs to false")
+		}
+
+		return checkCertValid([]keydetails{
+			{key: depConfig.ChefServer.Config.PrivateKey, certtype: PRIVATE_KEY, svc: CHEFSERVER},
+			{key: depConfig.ChefServer.Config.PublicKey, certtype: PUBLIC_KEY, svc: CHEFSERVER},
+		})
+	}
+
+	// If CertByIp available then check it's availabe for all the nodes
+	for _, v := range *depConfig.ChefServer.Config.CertsByIP {
+		if v.PrivateKey == "" {
+			return fmt.Errorf(returnErrorPrivetKeyMsg, v.IP)
+		}
+		if v.PublicKey == "" {
+			return fmt.Errorf(returnErrorPublicKeyMsg, v.IP)
+		}
+
+		if err := checkCertValid([]keydetails{
+			{key: v.PrivateKey, certtype: PRIVATE_KEY, svc: CHEFSERVER},
+			{key: v.PublicKey, certtype: PUBLIC_KEY, svc: CHEFSERVER},
+		}); err != nil {
+			return err
+		}
 	}
 
-	return checkCertValid([]keydetails{
-		{key: chefServerSettings.PrivateKey, certtype: PRIVATE_KEY, svc: CHEFSERVER},
-		{key: chefServerSettings.PublicKey, certtype: PUBLIC_KEY, svc: CHEFSERVER},
-	})
+	return nil
 }
 
 func checkCertValid(keys []keydetails) error {
@@ -249,36 +296,95 @@ func validateRootCACertificate(certBytes []byte, svc string) error {
 	return nil
 }
 
-func validatePostgresqlCerts(postgresqlSettings *ConfigSettings) error {
-	if len(strings.TrimSpace(postgresqlSettings.RootCA)) < 1 ||
-		len(strings.TrimSpace(postgresqlSettings.PrivateKey)) < 1 ||
-		len(strings.TrimSpace(postgresqlSettings.PublicKey)) < 1 {
-		return fmt.Errorf("postgresql root_ca and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false")
+func validatePostgresqlCerts(depConfig *HaDeployConfig) error {
+	if depConfig.Postgresql.Config.CertsByIP == nil || len(*depConfig.Postgresql.Config.CertsByIP) == 0 {
+		if len(strings.TrimSpace(depConfig.Postgresql.Config.RootCA)) < 1 ||
+			len(strings.TrimSpace(depConfig.Postgresql.Config.PrivateKey)) < 1 ||
+			len(strings.TrimSpace(depConfig.Postgresql.Config.PublicKey)) < 1 {
+			return fmt.Errorf("postgresql root_ca and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false")
+		}
+
+		return checkCertValid([]keydetails{
+			{key: depConfig.Postgresql.Config.RootCA, certtype: ROOT_CA, svc: POSTGRESQL},
+			{key: depConfig.Postgresql.Config.PrivateKey, certtype: PRIVATE_KEY, svc: POSTGRESQL},
+			{key: depConfig.Postgresql.Config.PublicKey, certtype: PUBLIC_KEY, svc: POSTGRESQL},
+		})
 	}
 
-	return checkCertValid([]keydetails{
-		{key: postgresqlSettings.RootCA, certtype: ROOT_CA, svc: POSTGRESQL},
-		{key: postgresqlSettings.PrivateKey, certtype: PRIVATE_KEY, svc: POSTGRESQL},
-		{key: postgresqlSettings.PublicKey, certtype: PUBLIC_KEY, svc: POSTGRESQL},
-	})
+	if depConfig.Postgresql.Config.RootCA == "" {
+		return fmt.Errorf("cannot find root_ca for ip for PostgresSQL")
+	}
+
+	// If CertByIp available then check it's availabe for all the nodes
+	for _, v := range *depConfig.Postgresql.Config.CertsByIP {
+		if v.PrivateKey == "" {
+			return fmt.Errorf(returnErrorPrivetKeyMsg, v.IP)
+		}
+
+		if v.PublicKey == "" {
+			return fmt.Errorf(returnErrorPublicKeyMsg, v.IP)
+		}
+
+		if err := checkCertValid([]keydetails{
+			{key: depConfig.Postgresql.Config.RootCA, certtype: ROOT_CA, svc: POSTGRESQL},
+			{key: v.PrivateKey, certtype: PRIVATE_KEY, svc: POSTGRESQL},
+			{key: v.PublicKey, certtype: PUBLIC_KEY, svc: POSTGRESQL},
+		}); err != nil {
+			return err
+		}
+
+	}
+
+	return nil
 }
 
-func validateOpensearchCerts(opensearchSettings *ConfigOpensearchSettings) error {
-	if len(strings.TrimSpace(opensearchSettings.RootCA)) < 1 ||
-		len(strings.TrimSpace(opensearchSettings.AdminKey)) < 1 ||
-		len(strings.TrimSpace(opensearchSettings.AdminCert)) < 1 ||
-		len(strings.TrimSpace(opensearchSettings.PrivateKey)) < 1 ||
-		len(strings.TrimSpace(opensearchSettings.PublicKey)) < 1 {
+func validateOpensearchCerts(depConfig *HaDeployConfig) error {
+	if depConfig.Opensearch.Config.CertsByIP == nil || len(*depConfig.Opensearch.Config.CertsByIP) == 0 {
+		if len(strings.TrimSpace(depConfig.Opensearch.Config.RootCA)) < 1 ||
+			len(strings.TrimSpace(depConfig.Opensearch.Config.AdminKey)) < 1 ||
+			len(strings.TrimSpace(depConfig.Opensearch.Config.AdminCert)) < 1 ||
+			len(strings.TrimSpace(depConfig.Opensearch.Config.PrivateKey)) < 1 ||
+			len(strings.TrimSpace(depConfig.Opensearch.Config.PublicKey)) < 1 {
+			return fmt.Errorf("opensearch root_ca and/or admin_key and/or admin_cert and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false")
+		}
+
+		return checkCertValid([]keydetails{
+			{key: depConfig.Opensearch.Config.RootCA, certtype: ROOT_CA, svc: OPENSEARCH},
+			{key: depConfig.Opensearch.Config.AdminKey, certtype: ADMIN_KEY, svc: OPENSEARCH},
+			{key: depConfig.Opensearch.Config.AdminCert, certtype: ADMIN_CERT, svc: OPENSEARCH},
+			{key: depConfig.Opensearch.Config.PrivateKey, certtype: PRIVATE_KEY, svc: OPENSEARCH},
+			{key: depConfig.Opensearch.Config.PublicKey, certtype: PUBLIC_KEY, svc: OPENSEARCH},
+		})
+	}
+
+	if len(strings.TrimSpace(depConfig.Opensearch.Config.RootCA)) < 1 ||
+		len(strings.TrimSpace(depConfig.Opensearch.Config.AdminKey)) < 1 ||
+		len(strings.TrimSpace(depConfig.Opensearch.Config.AdminCert)) < 1 {
 		return fmt.Errorf("opensearch root_ca and/or admin_key and/or admin_cert and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false")
 	}
 
-	return checkCertValid([]keydetails{
-		{key: opensearchSettings.RootCA, certtype: ROOT_CA, svc: OPENSEARCH},
-		{key: opensearchSettings.AdminKey, certtype: ADMIN_KEY, svc: OPENSEARCH},
-		{key: opensearchSettings.AdminCert, certtype: ADMIN_CERT, svc: OPENSEARCH},
-		{key: opensearchSettings.PrivateKey, certtype: PRIVATE_KEY, svc: OPENSEARCH},
-		{key: opensearchSettings.PublicKey, certtype: PUBLIC_KEY, svc: OPENSEARCH},
-	})
+	// If CertByIp available then check it's availabe for all the nodes
+	for _, v := range *depConfig.Opensearch.Config.CertsByIP {
+		if v.PrivateKey == "" {
+			return fmt.Errorf(returnErrorPrivetKeyMsg, v.IP)
+		}
+		if v.PublicKey == "" {
+			return fmt.Errorf(returnErrorPublicKeyMsg, v.IP)
+		}
+
+		if err := checkCertValid([]keydetails{
+			{key: depConfig.Opensearch.Config.RootCA, certtype: ROOT_CA, svc: OPENSEARCH},
+			{key: depConfig.Opensearch.Config.AdminKey, certtype: ADMIN_KEY, svc: OPENSEARCH},
+			{key: depConfig.Opensearch.Config.AdminCert, certtype: ROOT_CA, svc: OPENSEARCH},
+			{key: v.PrivateKey, certtype: PRIVATE_KEY, svc: OPENSEARCH},
+			{key: v.PublicKey, certtype: PUBLIC_KEY, svc: OPENSEARCH},
+		}); err != nil {
+			return err
+		}
+
+	}
+
+	return nil
 }
 
 func validateIPList(ipList []string, prefix string) error {
diff --git a/terraform/a2ha-terraform/modules/automate/files/config.toml.erb b/terraform/a2ha-terraform/modules/automate/files/config.toml.erb
index 801d3db39..cb8591e23 100644
--- a/terraform/a2ha-terraform/modules/automate/files/config.toml.erb
+++ b/terraform/a2ha-terraform/modules/automate/files/config.toml.erb
@@ -130,7 +130,7 @@
   [global.v1.external.automate.auth]
     token = "<%= overrides[:automate_dc_token] %>"
 
-  <% if overrides[:automate_custom_certs_enabled] == true && overrides[:automate_root_ca] -%>
+  <% if overrides[:automate_root_ca] -%>
     [cs_nginx.v1.sys.ngx.http]
       ssl_verify_depth = 6
     [global.v1.external.automate.ssl]


Test patch:
diff --git a/components/automate-cli/cmd/chef-automate/certRotate_test.go b/components/automate-cli/cmd/chef-automate/certRotate_test.go
index 4c3b58ec9..cde597978 100644
--- a/components/automate-cli/cmd/chef-automate/certRotate_test.go
+++ b/components/automate-cli/cmd/chef-automate/certRotate_test.go
@@ -260,9 +260,8 @@ func TestGetCerts(t *testing.T) {
 				automate:        true,
 				privateCertPath: ValidCertPath,
 				publicCertPath:  ValidCertPath,
-				rootCAPath:      ValidCertPath,
 			},
-			rootCaWant:      FileContent,
+			rootCaWant:      "",
 			publicCertWant:  FileContent,
 			privateCertWant: FileContent,
 			adminCertWant:   "",
@@ -349,26 +348,11 @@ func TestGetCerts(t *testing.T) {
 				publicCertPath:  ValidCertPath,
 			},
 			rootCaWant:      "",
-			publicCertWant:  "",
-			privateCertWant: "",
-			adminCertWant:   "",
-			adminKeyWant:    "",
-			isError:         true,
-		},
-		{
-			testCaseDescription: "All paths given but root-ca path is invalid(file not exist) flag is automate service",
-			flagsObj: certRotateFlags{
-				automate:        true,
-				privateCertPath: ValidCertPath,
-				publicCertPath:  ValidCertPath,
-				rootCAPath:      "./xyx-cert.go",
-			},
-			rootCaWant:      "",
-			publicCertWant:  "",
-			privateCertWant: "",
+			publicCertWant:  FileContent,
+			privateCertWant: FileContent,
 			adminCertWant:   "",
 			adminKeyWant:    "",
-			isError:         true,
+			isError:         false,
 		},
 		{
 			testCaseDescription: "Some mandatory path not given and flag is opensearch service",
diff --git a/components/automate-cli/cmd/chef-automate/verify_test.go b/components/automate-cli/cmd/chef-automate/verify_test.go
index df3040c50..aa73d3c01 100644
--- a/components/automate-cli/cmd/chef-automate/verify_test.go
+++ b/components/automate-cli/cmd/chef-automate/verify_test.go
@@ -6,12 +6,15 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
 	"github.com/chef/automate/components/automate-cli/pkg/verifysystemdcreate"
 	"github.com/chef/automate/lib/config"
 	"github.com/chef/automate/lib/httputils"
 	"github.com/chef/automate/lib/majorupgrade_utils"
+	"github.com/chef/automate/lib/reporting"
 	"github.com/chef/automate/lib/sshutils"
 	"github.com/chef/automate/lib/version"
+	"github.com/gofiber/fiber/v2"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -603,3 +606,119 @@ func newMockVerifyCmdFlow(mockHttputils *httputils.MockHTTPClient, cw *majorupgr
 		mockVerifyCmdDeps,
 	)
 }
+
+func TestBuildReports(t *testing.T) {
+	type args struct {
+		batchCheckResults []models.BatchCheckResult
+	}
+	tests := []struct {
+		name string
+		args args
+		want []reporting.VerificationReport
+	}{
+		{
+			name: "If Report is successfully created",
+			args: args{
+				batchCheckResults: []models.BatchCheckResult{
+					{
+						NodeType: "automate",
+						Ip:       "1.1.1.1",
+						Tests: []models.ApiResult{
+							{
+								Passed:  true,
+								Message: "SSH User Access Check",
+								Check:   "ssh-user",
+								Checks: []models.Checks{
+									{
+										Title:         "SSH user accessible",
+										Passed:        true,
+										SuccessMsg:    "SSH user is accessible for the node: 1.1.1.1",
+										ErrorMsg:      "",
+										ResolutionMsg: "",
+									},
+									{
+										Title:         "Sudo password valid",
+										Passed:        true,
+										SuccessMsg:    "SSH user sudo password is valid for the node: 1.1.1.1",
+										ErrorMsg:      "",
+										ResolutionMsg: "",
+									},
+								},
+								Skipped: false,
+							},
+						},
+					},
+				},
+			},
+			want: []reporting.VerificationReport{
+				{
+					TableKey: "automate",
+					Report: reporting.Info{
+						Hostip:    "1.1.1.1",
+						Parameter: "ssh-user",
+						Status:    "Success",
+						StatusMessage: &reporting.StatusMessage{
+							MainMessage: "SSH User Access Check - Success",
+							SubMessage:  nil,
+						},
+						SummaryInfo: &reporting.SummaryInfo{
+							SuccessfulCount: 2,
+							FailedCount:     0,
+							ToResolve:       nil,
+						},
+					},
+				},
+			},
+		},
+		{
+			name: "Error was there form the handler or Trigger response",
+			args: args{
+				batchCheckResults: []models.BatchCheckResult{
+					{
+						NodeType: "automate",
+						Ip: "1.1.1.1",
+						Tests: []models.ApiResult{
+							{
+								Passed: false,
+								Message: "SSH User Access Check",
+								Check: "ssh-user",
+								Checks: nil,
+								Error: &fiber.Error{
+									Code: 400,
+									Message: "Permissions on the ssh key file do not satisfy the requirement",
+								},
+								Skipped: false,
+							},
+						},
+					},
+				},
+			},
+			want: []reporting.VerificationReport{
+				{
+					TableKey: "automate",
+					Report: reporting.Info{
+						Hostip:    "1.1.1.1",
+						Parameter: "ssh-user",
+						Status:    "Failed",
+						StatusMessage: &reporting.StatusMessage{
+							MainMessage: "SSH User Access Check - Failed",
+							SubMessage: nil,
+						},
+						SummaryInfo: &reporting.SummaryInfo{
+							SuccessfulCount: 0,
+							FailedCount: 1,
+							ToResolve: []string{"Permissions on the ssh key file do not satisfy the requirement"},
+						},
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := buildReports(tt.args.batchCheckResults)
+			assert.Equal(t, got, tt.want)
+
+		})
+	}
+}
diff --git a/components/automate-cli/pkg/verifyserver/models/batchcheck_test.go b/components/automate-cli/pkg/verifyserver/models/batchcheck_test.go
index 4a5d29526..aca4d2b7a 100644
--- a/components/automate-cli/pkg/verifyserver/models/batchcheck_test.go
+++ b/components/automate-cli/pkg/verifyserver/models/batchcheck_test.go
@@ -343,7 +343,7 @@ func TestPopulateWith(t *testing.T) {
 			filePath: "./testdata/InvalidHaOnPrem.toml",
 			want:     &Config{},
 			wantErr:  true,
-			err:      errors.New("invalid or empty: ssh_user\ninvalid or empty: ssh_key_file\nautomate private ip 1324.2534.1is not valid\ninvalid or empty: chef_server_private_ips\ninvalid or empty: opensearch_private_ips\ninvalid or empty: postgresql_private_ips\nurl should not include the protocol (http:// or https://): automate fqdn\nempty value: automate instance_count\ninvalid value 'automate.toml' for field 'config_file'. Expected values are: configs/automate.toml\ninvalid format. Failed to decode root_ca for automate\ninvalid format. Failed to decode private_key for automate\ninvalid format. Failed to decode public_key for automate\ninvalid format. Failed to decode private_key for automate ip\ninvalid format. Failed to decode public_key for automate ip\ninvalid value 'chef server instance_count' for field 'two'\ninvalid format. Failed to decode private_key for chef-infra-server\ninvalid format. Failed to decode public_key for chef-infra-server\ninvalid format. Failed to decode private_key for chef server ip\ninvalid format. Failed to decode public_key for chef server ip\nempty value: opensearch instance_count\nopensearch root_ca and/or admin_key and/or admin_cert and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false\nopensearch ippublic_key and/or private_key are missing in certs_by_ip. Otherwise set enable_custom_certs to false\nempty value: postgresql instance_count\ninvalid format. Failed to decode root_ca for postgresql\ninvalid format. Failed to decode private_key for postgresql\ninvalid format. Failed to decode public_key for postgresql\npostgresql ip 0.0.1 for certs is not valid\ninvalid format. Failed to decode private_key for postgresql ip\ninvalid format. Failed to decode public_key for postgresql ip"),
+			err:      errors.New("invalid or empty: ssh_user\ninvalid or empty: ssh_key_file\nautomate private ip 1324.2534.1is not valid\ninvalid or empty: chef_server_private_ips\ninvalid or empty: opensearch_private_ips\ninvalid or empty: postgresql_private_ips\nurl should not include the protocol (http:// or https://): automate fqdn\nempty value: automate instance_count\ninvalid value 'automate.toml' for field 'config_file'. Expected values are: configs/automate.toml\ninvalid format. Failed to decode private_key for automate\ninvalid format. Failed to decode public_key for automate\ninvalid format. Failed to decode private_key for automate ip\ninvalid format. Failed to decode public_key for automate ip\ninvalid value 'chef server instance_count' for field 'two'\ninvalid format. Failed to decode private_key for chef-infra-server\ninvalid format. Failed to decode public_key for chef-infra-server\ninvalid format. Failed to decode private_key for chef server ip\ninvalid format. Failed to decode public_key for chef server ip\nempty value: opensearch instance_count\nopensearch root_ca and/or admin_key and/or admin_cert and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false\nopensearch ippublic_key and/or private_key are missing in certs_by_ip. Otherwise set enable_custom_certs to false\nempty value: postgresql instance_count\ninvalid format. Failed to decode root_ca for postgresql\ninvalid format. Failed to decode private_key for postgresql\ninvalid format. Failed to decode public_key for postgresql\npostgresql ip 0.0.1 for certs is not valid\ninvalid format. Failed to decode private_key for postgresql ip\ninvalid format. Failed to decode public_key for postgresql ip"),
 		},
 		{
 			name:     "PopulateWith OnPrem Db Aws Managed Config",
diff --git a/components/automate-cli/pkg/verifyserver/server/api/v1/systemresource_test.go b/components/automate-cli/pkg/verifyserver/server/api/v1/systemresource_test.go
index 2f3fb220c..688dfbc77 100644
--- a/components/automate-cli/pkg/verifyserver/server/api/v1/systemresource_test.go
+++ b/components/automate-cli/pkg/verifyserver/server/api/v1/systemresource_test.go
@@ -109,7 +109,7 @@ func TestSystemResourceAPI(t *testing.T) {
 				},
 			},
 			expectedCode:    200,
-			expectedBody:    "{\"status\":\"SUCCESS\",\"result\":{\"passed\":true,\"msg\":\"\",\"check\":\"\",\"checks\":[{\"title\":\"CPU count check\",\"passed\":true,\"success_msg\":\"CPU count is \\u003e=4\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"CPU speed check\",\"passed\":true,\"success_msg\":\"CPU speed should be \\u003e=2Ghz\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"Memory size check\",\"passed\":true,\"success_msg\":\"Memory should be \\u003e=14GB\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"Hab free space check\",\"passed\":true,\"success_msg\":\"/hab should have free space \\u003e=80GB\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"Temp free space check\",\"passed\":true,\"success_msg\":\"/tmp should have free space \\u003e=10GB or 5% of total size of /hab\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"/(root volume) free space check\",\"passed\":true,\"success_msg\":\"/(root volume) should have free space \\u003e=20GB or 20% of total size of /hab\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}}",
+			expectedBody:    "{\"status\":\"SUCCESS\",\"result\":{\"passed\":true,\"msg\":\"\",\"check\":\"\",\"checks\":[{\"title\":\"CPU count check\",\"passed\":true,\"success_msg\":\"CPU count is \\u003e=2\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"CPU speed check\",\"passed\":true,\"success_msg\":\"CPU speed should be \\u003e=2Ghz\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"Memory size check\",\"passed\":true,\"success_msg\":\"Memory should be \\u003e=7GB\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"Hab free space check\",\"passed\":true,\"success_msg\":\"/hab should have free space \\u003e=80GB\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"Temp free space check\",\"passed\":true,\"success_msg\":\"/tmp should have free space \\u003e=10GB or 5% of total size of /hab\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"/(root volume) free space check\",\"passed\":true,\"success_msg\":\"/(root volume) should have free space \\u003e=20GB or 20% of total size of /hab\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}}",
 			nodeType:        enums.NodeTypeAutomate,
 			deploymentState: enums.DeploymentStatePreDeploy,
 		},
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/firewallchecktrigger_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/firewallchecktrigger_test.go
index f5b06fa8a..de50e43a4 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/firewallchecktrigger_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/firewallchecktrigger/firewallchecktrigger_test.go
@@ -109,12 +109,6 @@ const (
 						"success_msg": "The <protocol> service running at <destination_node_ip>:<destination_node_port> is reachable fro <source_node_ip>",
 						"error_msg": "",
 						"resolution_msg": ""
-					},{
-						"title": "Check for reachability of service at destination port from the source node",
-						"passed": true,
-						"success_msg": "The <protocol> service running at <destination_node_ip>:<destination_node_port> is reachable fro <source_node_ip>",
-						"error_msg": "",
-						"resolution_msg": ""
 					}
 				]
 			}
@@ -159,12 +153,6 @@ const (
 						"success_msg": "",
 						"error_msg": "The <protocol> service running at <destination_node_ip>:<destination_node_port> is not reachable from <source_ip>",
 						"resolution_msg": "Check your firewall settings to provide access to <destination_node_port> port at <destination_node_ip> from <source_node_ip>"
-					},{
-						"title": "Check for reachability of service at destination port",
-						"passed": false,
-						"success_msg": "",
-						"error_msg": "The <protocol> service running at <destination_node_ip>:<destination_node_port> is not reachable from <source_ip>",
-						"resolution_msg": "Check your firewall settings to provide access to <destination_node_port> port at <destination_node_ip> from <source_node_ip>"
 					}
 				]
 			}
@@ -246,7 +234,7 @@ func TestMakeRequests(t *testing.T) {
 
 	requestsForBastion, ok := mapRequests[constants.BASTION]
 	assert.True(t, ok)
-	assert.Equal(t, len(requestsForBastion), 11)
+	assert.Equal(t, len(requestsForBastion), 7)
 
 	require.Equal(t, expected[0].SourceNodeIP, requestsForautomate[0].SourceNodeIP)
 	require.Equal(t, expected[0].DestinationNodeIP, requestsForautomate[0].DestinationNodeIP)
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/hardwareresourcechecktrigger/hardwareresourcecountchecktrigger_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/hardwareresourcechecktrigger/hardwareresourcecountchecktrigger_test.go
index 037adcbb9..e97bfb050 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/hardwareresourcechecktrigger/hardwareresourcecountchecktrigger_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/hardwareresourcechecktrigger/hardwareresourcecountchecktrigger_test.go
@@ -264,11 +264,10 @@ func TestHardwareResourceCountCheck_Run(t *testing.T) {
 			}
 		}
 	})
-
 	t.Run("Returns error", func(t *testing.T) {
 		mockServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 			w.WriteHeader(http.StatusOK)
-			w.Write([]byte(`invalid JSON`))
+			w.Write([]byte(`{"status":"failure", "result":[], "error":{"code":400, "message":"Invalid JSON"}}`))
 		}))
 		err := startMockServerOnCustomPort(mockServer, "1134")
 		assert.NoError(t, err)
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/nfsmountbackupchecktrigger/nfsmountbackupcheck_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/nfsmountbackupchecktrigger/nfsmountbackupcheck_test.go
index 047fb4572..5e9b1ba9f 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/nfsmountbackupchecktrigger/nfsmountbackupcheck_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/nfsmountbackupchecktrigger/nfsmountbackupcheck_test.go
@@ -686,7 +686,7 @@ func TestNfsBackupConfigCheck_Run(t *testing.T) {
 					},
 				},
 			},
-			response:       "Error while triggering NFS Mount API from Batch Check API: 500 Internal Server Error",
+			response:       "unexpected end of JSON input",
 			isPassed:       false,
 			httpStatusCode: http.StatusInternalServerError,
 			isError:        true,
@@ -766,7 +766,7 @@ func TestNfsBackupConfigCheck_Run(t *testing.T) {
 				assert.Len(t, got, 4)
 				assert.NotNil(t, got[0].Result.Error)
 				assert.NotNil(t, got[0].Host)
-				assert.Contains(t, tt.response, got[0].Result.Error.Error())
+				assert.Contains(t, tt.response, got[0].Result.Error.Message)
 
 			} else {
 				var want []models.CheckTriggerResponse
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/opensearchs3bucketaccesschecktrigger/opensearchs3bucketaccesscheck_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/opensearchs3bucketaccesschecktrigger/opensearchs3bucketaccesscheck_test.go
index 7f76416bd..3efdae90b 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/opensearchs3bucketaccesschecktrigger/opensearchs3bucketaccesscheck_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/opensearchs3bucketaccesschecktrigger/opensearchs3bucketaccesscheck_test.go
@@ -16,12 +16,14 @@ import (
 
 var (
 	externalOs = &models.ExternalOS{
-		OSDomainName:   "Name of the domain",
-		OSDomainURL:    "open-search-url",
-		OSRoleArn:      "Role-ARN",
-		OSUsername:     "username",
-		OSUserPassword: "password",
-		OSCert:         "___CERT____",
+		OSDomainName:                  "Name of the domain",
+		OSDomainURL:                   "https://open-search-url",
+		OSRoleArn:                     "Role-ARN",
+		OSUsername:                    "username",
+		OSUserPassword:                "password",
+		OSCert:                        "___CERT____",
+		OsSnapshotUserAccessKeySecret: "secret-key",
+		OsSnapshotUserAccessKeyId:     "access-kkey",
 	}
 
 	s3Properties = &models.ObjectStorage{
@@ -77,7 +79,7 @@ const (
 	[
 		{
 			"status": "SUCCESS",
-			"host": "open-search-url",
+			"host": "https://open-search-url",
 			"node_type": "opensearch",
 			"result": {
 				"passed": true,
@@ -99,7 +101,7 @@ const (
 	apiTriggerResponseFailure = `[
 		{
 		"status": "SUCCESS",
-		"host": "open-search-url",
+		"host": "https://open-search-url",
 		"node_type": "opensearch",
 		"result": {
 			"passed": false,
@@ -221,7 +223,7 @@ func TestOpensearchS3BucketAccessCheck_Run(t *testing.T) {
 				assert.NotNil(t, got[0].Result.Error.Error)
 				assert.Equal(t, constants.OPENSEARCH, got[0].NodeType)
 				assert.Equal(t, tt.httpResponseCode, got[0].Result.Error.Code)
-				assert.Equal(t, "open-search-url", got[0].Host)
+				assert.Equal(t, "https://open-search-url", got[0].Host)
 				assert.Equal(t, tt.response, got[0].Result.Error.Error())
 			} else {
 				if tt.name == "Nil OS and Object storage" {
@@ -242,8 +244,7 @@ func TestOpensearchS3BucketAccessCheck_Run(t *testing.T) {
 				} else {
 					assert.Equal(t, want, got)
 					assert.NotNil(t, got)
-					assert.Nil(t, got[0].Result.Error)
-					assert.Equal(t, "open-search-url", got[0].Host)
+					assert.Equal(t, "https://open-search-url", got[0].Host)
 				}
 			}
 
diff --git a/components/automate-cli/pkg/verifyserver/services/certificatevalidation/certificatevalidation_test.go b/components/automate-cli/pkg/verifyserver/services/certificatevalidation/certificatevalidation_test.go
index 1f8f2356d..4a9c88cce 100644
--- a/components/automate-cli/pkg/verifyserver/services/certificatevalidation/certificatevalidation_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/certificatevalidation/certificatevalidation_test.go
@@ -550,49 +550,7 @@ func TestCertificateValidation(t *testing.T) {
 				},
 			},
 		},
-		{
-			TestName: "Private Key is not in PKCS8 format",
-			ReqBody: models.CertificateCheckRequest{
-				RootCertificate:  ROOT_CERTIFICATE,
-				PrivateKey:       INVALID_KEY_FORMAT,
-				NodeCertificate:  NODE_CERTIFICATE,
-				AdminPrivateKey:  ADMIN_PRIVATE_KEY,
-				AdminCertificate: ADMIN_CERTIFICATE,
-			},
-			ExpectedResBody: models.CertificateCheckResponse{
-				Passed: false,
-				Checks: []models.Checks{
-					{
-						Title:         constants.CERTIFICATE_EXPIRY_TITLE,
-						Passed:        true,
-						SuccessMsg:    constants.CERTIFICATE_EXPIRY_SUCCESS_MESSAGE,
-						ErrorMsg:      "",
-						ResolutionMsg: "",
-					},
-					{
-						Title:         constants.CERTIFICATE_FORMAT_TITLE,
-						Passed:        true,
-						SuccessMsg:    constants.CERTIFICATE_FORMAT_SUCCESS_MESSAGE,
-						ErrorMsg:      "",
-						ResolutionMsg: "",
-					},
-					{
-						Title:         constants.KEY_FORMAT_TITLE,
-						Passed:        false,
-						SuccessMsg:    "",
-						ErrorMsg:      fmt.Sprintf(constants.KEY_FORMAT_ERROR_MESSAGE, constants.NODE_KEY),
-						ResolutionMsg: fmt.Sprintf(constants.KEY_FORMAT_RESOLUTION_MESSAGE, constants.NODE_KEY),
-					},
-					{
-						Title:         constants.CERTIFICATE_ALGORITHM_TITLE,
-						Passed:        true,
-						SuccessMsg:    constants.CERTIFICATE_ALGORITHM_SUCCESS_MESSAGE,
-						ErrorMsg:      "",
-						ResolutionMsg: "",
-					},
-				},
-			},
-		},
+
 		{
 			TestName: "There is no root certificate",
 			ReqBody: models.CertificateCheckRequest{
diff --git a/components/automate-cli/pkg/verifyserver/utils/checkutils/checkutil_test.go b/components/automate-cli/pkg/verifyserver/utils/checkutils/checkutil_test.go
index a3875677e..0b4e7e19a 100644
--- a/components/automate-cli/pkg/verifyserver/utils/checkutils/checkutil_test.go
+++ b/components/automate-cli/pkg/verifyserver/utils/checkutils/checkutil_test.go
@@ -44,9 +44,10 @@ func TestPrepareTriggerResponse_WithNoError(t *testing.T) {
 	check := "test check"
 	msg := "test message"
 	isError := false
+	statusCode := 200
 
 	// Act
-	result := PrepareTriggerResponse(resp, host, nodeType, errorString, check, msg, isError)
+	result := PrepareTriggerResponse(resp, host, nodeType, errorString, check, msg, isError, statusCode)
 
 	// Assert
 	assert.Equal(t, resp.Status, result.Status)
@@ -68,9 +69,9 @@ func TestPrepareTriggerResponse_WithError(t *testing.T) {
 	check := "test check"
 	msg := "test message"
 	isError := true
-
+	statusCode := 400
 	// Act
-	result := PrepareTriggerResponse(resp, host, nodeType, errorString, check, msg, isError)
+	result := PrepareTriggerResponse(resp, host, nodeType, errorString, check, msg, isError, statusCode)
 
 	// Assert
 	assert.Equal(t, resp.Status, result.Status)
diff --git a/lib/config/hadeployconfigverify_test.go b/lib/config/hadeployconfigverify_test.go
index 47d348eac..d905371cd 100644
--- a/lib/config/hadeployconfigverify_test.go
+++ b/lib/config/hadeployconfigverify_test.go
@@ -33,7 +33,7 @@ func TestParseAndVerify(t *testing.T) {
 			name:    "Verify OnPrem Config",
 			args:    args{configFile: "./testdata/HaOnPrem.toml"},
 			wantErr: true,
-			err:     errors.New("invalid or empty: ssh_user\ninvalid or empty: ssh_key_file\nautomate private ip 1324.2534.1is not valid\ninvalid or empty: chef_server_private_ips\ninvalid or empty: opensearch_private_ips\ninvalid or empty: postgresql_private_ips\nurl should not include the protocol (http:// or https://): automate fqdn\nempty value: automate instance_count\ninvalid value 'automate.toml' for field 'config_file'. Expected values are: configs/automate.toml\ninvalid format. Failed to decode root_ca for automate\ninvalid format. Failed to decode private_key for automate\ninvalid format. Failed to decode public_key for automate\ninvalid format. Failed to decode private_key for automate ip\ninvalid format. Failed to decode public_key for automate ip\ninvalid value 'chef server instance_count' for field 'two'\ninvalid format. Failed to decode private_key for chef-infra-server\ninvalid format. Failed to decode public_key for chef-infra-server\ninvalid format. Failed to decode private_key for chef server ip\ninvalid format. Failed to decode public_key for chef server ip\nempty value: opensearch instance_count\nopensearch root_ca and/or admin_key and/or admin_cert and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false\nopensearch ippublic_key and/or private_key are missing in certs_by_ip. Otherwise set enable_custom_certs to false\nempty value: postgresql instance_count\ninvalid format. Failed to decode root_ca for postgresql\ninvalid format. Failed to decode private_key for postgresql\ninvalid format. Failed to decode public_key for postgresql\npostgresql ip 0.0.1 for certs is not valid\ninvalid format. Failed to decode private_key for postgresql ip\ninvalid format. Failed to decode public_key for postgresql ip"),
+			err:     errors.New("invalid or empty: ssh_user\ninvalid or empty: ssh_key_file\nautomate private ip 1324.2534.1is not valid\ninvalid or empty: chef_server_private_ips\ninvalid or empty: opensearch_private_ips\ninvalid or empty: postgresql_private_ips\nurl should not include the protocol (http:// or https://): automate fqdn\nempty value: automate instance_count\ninvalid value 'automate.toml' for field 'config_file'. Expected values are: configs/automate.toml\ninvalid format. Failed to decode private_key for automate\ninvalid format. Failed to decode public_key for automate\ninvalid format. Failed to decode private_key for automate ip\ninvalid format. Failed to decode public_key for automate ip\ninvalid value 'chef server instance_count' for field 'two'\ninvalid format. Failed to decode private_key for chef-infra-server\ninvalid format. Failed to decode public_key for chef-infra-server\ninvalid format. Failed to decode private_key for chef server ip\ninvalid format. Failed to decode public_key for chef server ip\nempty value: opensearch instance_count\nopensearch root_ca and/or admin_key and/or admin_cert and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false\nopensearch ippublic_key and/or private_key are missing in certs_by_ip. Otherwise set enable_custom_certs to false\nempty value: postgresql instance_count\ninvalid format. Failed to decode root_ca for postgresql\ninvalid format. Failed to decode private_key for postgresql\ninvalid format. Failed to decode public_key for postgresql\npostgresql ip 0.0.1 for certs is not valid\ninvalid format. Failed to decode private_key for postgresql ip\ninvalid format. Failed to decode public_key for postgresql ip"),
 		},
 		{
 			name:    "Verify OnPrem Db Self-Managed Config",
diff --git a/lib/config/helper_test.go b/lib/config/helper_test.go
index 9fca0c043..c7bf12f1a 100644
--- a/lib/config/helper_test.go
+++ b/lib/config/helper_test.go
@@ -10,6 +10,8 @@ import (
 	"math/big"
 	"testing"
 	"time"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestValidateStringBasedBoolean(t *testing.T) {
@@ -426,3 +428,554 @@ func generateExpiredCert() []byte {
 	derBytes, _ := x509.CreateCertificate(rand.Reader, template, template, &publicKey, privateKey)
 	return derBytes
 }
+
+const (
+	PublicKey  = "-----BEGIN CERTIFICATE-----\nMIIDqzCCApOgAwIBAgIUMUG0kbGT5s8Vo415aNfii81CzRcwDQYJKoZIhvcNAQEL\nBQAwZTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM\nB1NlYXR0bGUxGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApj\naGVmcm9vdGNhMB4XDTIyMDYwMTA3MzMwMFoXDTI1MDUzMTA3MzMwMFowZTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUx\nGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApjaGVmcm9vdGNh\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4aKz6j+Otgeg+oZSsHtq\nz5Phyb2cWr+CdbLb2qZA9ZDcCjEzkxvuvI6QBScF9feVk7YVXg59DXlHJRP7AHx5\nnG5iQmND0jUUQGjyZfKTWO8Z9F8D/w0HAFOk4LTvVR/AAC6f9PpxjaydfSzkh5cc\nNxeotjlkYUgh6D7fFma9gRtjX4a36miA5FqCv0Lkwrk9tXHdDk6skdjBvCS0URnq\nzDfyTcK7R/L/iaGYtY43c8tB8eHKwK+ZQ2fR4V90YI3xkbkwr6j0efZyU2Kp/03r\n2nDtFQXHwwuj6Sg465DJB9MQ01IbR30NUjJiFGOJaxdRlhsEeWWNL8BELQuGpJe9\nVQIDAQABo1MwUTAdBgNVHQ4EFgQU3XAfDSRK1wCTf0wiDjlR4m06FCQwHwYDVR0j\nBBgwFoAU3XAfDSRK1wCTf0wiDjlR4m06FCQwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAQEAVTiFfpOzYfXzQxTrl4VxctdQJI52jPjWP55PmK/IO7sb\nn/DKzRNnvn7mN3EDGmshh5i76/hoGA09pK4SdAwJl2QIRJSu3ChH4QCf7n/iIYww\nSxhpOp9QtJA5Cyu4MoemK49Lld/7xf3Qdt1pOgEMz4AGLt2uwS5SdmR4OkSPHqt0\nQq/lgSoiawVOd0UE+5Ocu5S472du/REcVS4KkQdkzZaw9Q7OGIN9G0X0wb0V3UZs\nR2XuApUUaGOl0s0a1uNMv3WkyOrCGS3JkIem6+R59pfliaz6FPcQD+0oXI2gjG3c\nH4VFwOis/oT/2FwYM89j7XZPDpRfUUGMHD53W7YPHA==\n-----END CERTIFICATE-----"
+	PrivateKey = "-----BEGIN CERTIFICATE-----\nMIIDqzCCApOgAwIBAgIUMUG0kbGT5s8Vo415aNfii81CzRcwDQYJKoZIhvcNAQEL\nBQAwZTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM\nB1NlYXR0bGUxGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApj\naGVmcm9vdGNhMB4XDTIyMDYwMTA3MzMwMFoXDTI1MDUzMTA3MzMwMFowZTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUx\nGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApjaGVmcm9vdGNh\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4aKz6j+Otgeg+oZSsHtq\nz5Phyb2cWr+CdbLb2qZA9ZDcCjEzkxvuvI6QBScF9feVk7YVXg59DXlHJRP7AHx5\nnG5iQmND0jUUQGjyZfKTWO8Z9F8D/w0HAFOk4LTvVR/AAC6f9PpxjaydfSzkh5cc\nNxeotjlkYUgh6D7fFma9gRtjX4a36miA5FqCv0Lkwrk9tXHdDk6skdjBvCS0URnq\nzDfyTcK7R/L/iaGYtY43c8tB8eHKwK+ZQ2fR4V90YI3xkbkwr6j0efZyU2Kp/03r\n2nDtFQXHwwuj6Sg465DJB9MQ01IbR30NUjJiFGOJaxdRlhsEeWWNL8BELQuGpJe9\nVQIDAQABo1MwUTAdBgNVHQ4EFgQU3XAfDSRK1wCTf0wiDjlR4m06FCQwHwYDVR0j\nBBgwFoAU3XAfDSRK1wCTf0wiDjlR4m06FCQwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAQEAVTiFfpOzYfXzQxTrl4VxctdQJI52jPjWP55PmK/IO7sb\nn/DKzRNnvn7mN3EDGmshh5i76/hoGA09pK4SdAwJl2QIRJSu3ChH4QCf7n/iIYww\nSxhpOp9QtJA5Cyu4MoemK49Lld/7xf3Qdt1pOgEMz4AGLt2uwS5SdmR4OkSPHqt0\nQq/lgSoiawVOd0UE+5Ocu5S472du/REcVS4KkQdkzZaw9Q7OGIN9G0X0wb0V3UZs\nR2XuApUUaGOl0s0a1uNMv3WkyOrCGS3JkIem6+R59pfliaz6FPcQD+0oXI2gjG3c\nH4VFwOis/oT/2FwYM89j7XZPDpRfUUGMHD53W7YPHA==\n-----END CERTIFICATE-----"
+	RootCa     = "-----BEGIN CERTIFICATE-----\nMIIDqzCCApOgAwIBAgIUMUG0kbGT5s8Vo415aNfii81CzRcwDQYJKoZIhvcNAQEL\nBQAwZTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM\nB1NlYXR0bGUxGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApj\naGVmcm9vdGNhMB4XDTIyMDYwMTA3MzMwMFoXDTI1MDUzMTA3MzMwMFowZTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUx\nGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApjaGVmcm9vdGNh\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4aKz6j+Otgeg+oZSsHtq\nz5Phyb2cWr+CdbLb2qZA9ZDcCjEzkxvuvI6QBScF9feVk7YVXg59DXlHJRP7AHx5\nnG5iQmND0jUUQGjyZfKTWO8Z9F8D/w0HAFOk4LTvVR/AAC6f9PpxjaydfSzkh5cc\nNxeotjlkYUgh6D7fFma9gRtjX4a36miA5FqCv0Lkwrk9tXHdDk6skdjBvCS0URnq\nzDfyTcK7R/L/iaGYtY43c8tB8eHKwK+ZQ2fR4V90YI3xkbkwr6j0efZyU2Kp/03r\n2nDtFQXHwwuj6Sg465DJB9MQ01IbR30NUjJiFGOJaxdRlhsEeWWNL8BELQuGpJe9\nVQIDAQABo1MwUTAdBgNVHQ4EFgQU3XAfDSRK1wCTf0wiDjlR4m06FCQwHwYDVR0j\nBBgwFoAU3XAfDSRK1wCTf0wiDjlR4m06FCQwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAQEAVTiFfpOzYfXzQxTrl4VxctdQJI52jPjWP55PmK/IO7sb\nn/DKzRNnvn7mN3EDGmshh5i76/hoGA09pK4SdAwJl2QIRJSu3ChH4QCf7n/iIYww\nSxhpOp9QtJA5Cyu4MoemK49Lld/7xf3Qdt1pOgEMz4AGLt2uwS5SdmR4OkSPHqt0\nQq/lgSoiawVOd0UE+5Ocu5S472du/REcVS4KkQdkzZaw9Q7OGIN9G0X0wb0V3UZs\nR2XuApUUaGOl0s0a1uNMv3WkyOrCGS3JkIem6+R59pfliaz6FPcQD+0oXI2gjG3c\nH4VFwOis/oT/2FwYM89j7XZPDpRfUUGMHD53W7YPHA==\n-----END CERTIFICATE-----"
+	AdminCert  = "-----BEGIN CERTIFICATE-----\nMIIDqzCCApOgAwIBAgIUMUG0kbGT5s8Vo415aNfii81CzRcwDQYJKoZIhvcNAQEL\nBQAwZTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM\nB1NlYXR0bGUxGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApj\naGVmcm9vdGNhMB4XDTIyMDYwMTA3MzMwMFoXDTI1MDUzMTA3MzMwMFowZTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUx\nGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApjaGVmcm9vdGNh\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4aKz6j+Otgeg+oZSsHtq\nz5Phyb2cWr+CdbLb2qZA9ZDcCjEzkxvuvI6QBScF9feVk7YVXg59DXlHJRP7AHx5\nnG5iQmND0jUUQGjyZfKTWO8Z9F8D/w0HAFOk4LTvVR/AAC6f9PpxjaydfSzkh5cc\nNxeotjlkYUgh6D7fFma9gRtjX4a36miA5FqCv0Lkwrk9tXHdDk6skdjBvCS0URnq\nzDfyTcK7R/L/iaGYtY43c8tB8eHKwK+ZQ2fR4V90YI3xkbkwr6j0efZyU2Kp/03r\n2nDtFQXHwwuj6Sg465DJB9MQ01IbR30NUjJiFGOJaxdRlhsEeWWNL8BELQuGpJe9\nVQIDAQABo1MwUTAdBgNVHQ4EFgQU3XAfDSRK1wCTf0wiDjlR4m06FCQwHwYDVR0j\nBBgwFoAU3XAfDSRK1wCTf0wiDjlR4m06FCQwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAQEAVTiFfpOzYfXzQxTrl4VxctdQJI52jPjWP55PmK/IO7sb\nn/DKzRNnvn7mN3EDGmshh5i76/hoGA09pK4SdAwJl2QIRJSu3ChH4QCf7n/iIYww\nSxhpOp9QtJA5Cyu4MoemK49Lld/7xf3Qdt1pOgEMz4AGLt2uwS5SdmR4OkSPHqt0\nQq/lgSoiawVOd0UE+5Ocu5S472du/REcVS4KkQdkzZaw9Q7OGIN9G0X0wb0V3UZs\nR2XuApUUaGOl0s0a1uNMv3WkyOrCGS3JkIem6+R59pfliaz6FPcQD+0oXI2gjG3c\nH4VFwOis/oT/2FwYM89j7XZPDpRfUUGMHD53W7YPHA==\n-----END CERTIFICATE-----"
+	AdminKey   = "-----BEGIN CERTIFICATE-----\nMIIDqzCCApOgAwIBAgIUMUG0kbGT5s8Vo415aNfii81CzRcwDQYJKoZIhvcNAQEL\nBQAwZTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcM\nB1NlYXR0bGUxGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApj\naGVmcm9vdGNhMB4XDTIyMDYwMTA3MzMwMFoXDTI1MDUzMTA3MzMwMFowZTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUx\nGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQDDApjaGVmcm9vdGNh\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4aKz6j+Otgeg+oZSsHtq\nz5Phyb2cWr+CdbLb2qZA9ZDcCjEzkxvuvI6QBScF9feVk7YVXg59DXlHJRP7AHx5\nnG5iQmND0jUUQGjyZfKTWO8Z9F8D/w0HAFOk4LTvVR/AAC6f9PpxjaydfSzkh5cc\nNxeotjlkYUgh6D7fFma9gRtjX4a36miA5FqCv0Lkwrk9tXHdDk6skdjBvCS0URnq\nzDfyTcK7R/L/iaGYtY43c8tB8eHKwK+ZQ2fR4V90YI3xkbkwr6j0efZyU2Kp/03r\n2nDtFQXHwwuj6Sg465DJB9MQ01IbR30NUjJiFGOJaxdRlhsEeWWNL8BELQuGpJe9\nVQIDAQABo1MwUTAdBgNVHQ4EFgQU3XAfDSRK1wCTf0wiDjlR4m06FCQwHwYDVR0j\nBBgwFoAU3XAfDSRK1wCTf0wiDjlR4m06FCQwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAQEAVTiFfpOzYfXzQxTrl4VxctdQJI52jPjWP55PmK/IO7sb\nn/DKzRNnvn7mN3EDGmshh5i76/hoGA09pK4SdAwJl2QIRJSu3ChH4QCf7n/iIYww\nSxhpOp9QtJA5Cyu4MoemK49Lld/7xf3Qdt1pOgEMz4AGLt2uwS5SdmR4OkSPHqt0\nQq/lgSoiawVOd0UE+5Ocu5S472du/REcVS4KkQdkzZaw9Q7OGIN9G0X0wb0V3UZs\nR2XuApUUaGOl0s0a1uNMv3WkyOrCGS3JkIem6+R59pfliaz6FPcQD+0oXI2gjG3c\nH4VFwOis/oT/2FwYM89j7XZPDpRfUUGMHD53W7YPHA==\n-----END CERTIFICATE-----"
+
+	DummyPublicKey  = "DummyPublicKey"
+	DummyPrivateKey = "DummyPrivateKey"
+)
+
+func Test_validateAutomateCerts(t *testing.T) {
+	t.Run("NIL CERTByIP", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Automate: &AutomateSettings{
+				Config: &ConfigAutomateSettings{
+					CertsByIP: nil,
+				},
+			},
+		}
+
+		expectedErr := "automate root_ca and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false"
+		err := validateAutomateCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("Invalid common Certs", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Automate: &AutomateSettings{
+				Config: &ConfigAutomateSettings{
+					CertsByIP:  nil,
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		expectedErr := "invalid format. Failed to decode private_key for automate\ninvalid format. Failed to decode public_key for automate"
+		err := validateAutomateCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+	t.Run("No private key", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Automate: &AutomateSettings{
+				Config: &ConfigAutomateSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP: "10.10.10.10",
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		expectedErr := "cannot find private key for ip 10.10.10.10"
+		err := validateAutomateCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("No private key", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Automate: &AutomateSettings{
+				Config: &ConfigAutomateSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: DummyPrivateKey,
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		expectedErr := "cannot find public key for ip 10.10.10.11"
+		err := validateAutomateCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("No CertByIP validate", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Automate: &AutomateSettings{
+				Config: &ConfigAutomateSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: DummyPrivateKey,
+							PublicKey:  DummyPublicKey,
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		expectedErr := "invalid format. Failed to decode private_key for automate\ninvalid format. Failed to decode public_key for automate"
+		err := validateAutomateCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("NO Error", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Automate: &AutomateSettings{
+				Config: &ConfigAutomateSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: PrivateKey,
+							PublicKey:  PublicKey,
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		err := validateAutomateCerts(depConfig)
+		assert.NoError(t, err)
+	})
+}
+
+func Test_validateChefServerCerts(t *testing.T) {
+	t.Run("NIL CERTByIP", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			ChefServer: &ChefServerSettings{
+				Config: &ConfigSettings{
+					CertsByIP: nil,
+				},
+			},
+		}
+
+		expectedErr := "chefServer public_key and/or private_key are missing. Otherwise set enable_custom_certs to false"
+		err := validateChefServerCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("Invalid common Certs", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			ChefServer: &ChefServerSettings{
+				Config: &ConfigSettings{
+					CertsByIP:  nil,
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		expectedErr := "invalid format. Failed to decode private_key for chef-infra-server\ninvalid format. Failed to decode public_key for chef-infra-server"
+		err := validateChefServerCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+	t.Run("No private key", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			ChefServer: &ChefServerSettings{
+				Config: &ConfigSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP: "10.10.10.10",
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		expectedErr := "cannot find private key for ip 10.10.10.10"
+		err := validateChefServerCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("No private key", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			ChefServer: &ChefServerSettings{
+				Config: &ConfigSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: "-----PK-----",
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		expectedErr := "cannot find public key for ip 10.10.10.11"
+		err := validateChefServerCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("No CertByIP validate", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			ChefServer: &ChefServerSettings{
+				Config: &ConfigSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: DummyPrivateKey,
+							PublicKey:  DummyPublicKey,
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		expectedErr := "invalid format. Failed to decode private_key for chef-infra-server\ninvalid format. Failed to decode public_key for chef-infra-server"
+		err := validateChefServerCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("NO Error", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			ChefServer: &ChefServerSettings{
+				Config: &ConfigSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: PrivateKey,
+							PublicKey:  PublicKey,
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+				},
+			},
+		}
+
+		err := validateChefServerCerts(depConfig)
+		assert.NoError(t, err)
+	})
+}
+
+func Test_validatePostgresqlCerts(t *testing.T) {
+	t.Run("NIL CERTByIP", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Postgresql: &PostgresqlSettings{
+				Config: &ConfigSettings{},
+			},
+		}
+
+		expectedErr := "postgresql root_ca and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false"
+		err := validatePostgresqlCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("Invalid common Certs", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Postgresql: &PostgresqlSettings{
+				Config: &ConfigSettings{
+					CertsByIP:  nil,
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+					RootCA:     "dummy_root_cert",
+				},
+			},
+		}
+
+		expectedErr := "invalid format. Failed to decode root_ca for postgresql\ninvalid format. Failed to decode private_key for postgresql\ninvalid format. Failed to decode public_key for postgresql"
+		err := validatePostgresqlCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+	t.Run("No private key", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Postgresql: &PostgresqlSettings{
+				Config: &ConfigSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP: "10.10.10.10",
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+					RootCA:     "-----ROOTCA KEY-----",
+				},
+			},
+		}
+
+		expectedErr := "cannot find private key for ip 10.10.10.10"
+		err := validatePostgresqlCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("No private key", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Postgresql: &PostgresqlSettings{
+				Config: &ConfigSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: DummyPrivateKey,
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+					RootCA:     "-----ROOTCA KEY-----",
+				},
+			},
+		}
+
+		expectedErr := "cannot find public key for ip 10.10.10.11"
+		err := validatePostgresqlCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("No CertByIP validate", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Postgresql: &PostgresqlSettings{
+				Config: &ConfigSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: DummyPrivateKey,
+							PublicKey:  DummyPublicKey,
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+					RootCA:     "-----ROOTCA KEY-----",
+				},
+			},
+		}
+
+		expectedErr := "invalid format. Failed to decode root_ca for postgresql\ninvalid format. Failed to decode private_key for postgresql\ninvalid format. Failed to decode public_key for postgresql"
+		err := validatePostgresqlCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("NO Error", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Postgresql: &PostgresqlSettings{
+				Config: &ConfigSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: PrivateKey,
+							PublicKey:  PublicKey,
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+					RootCA:     RootCa,
+				},
+			},
+		}
+
+		err := validatePostgresqlCerts(depConfig)
+		assert.NoError(t, err)
+	})
+
+	t.Run("RootCert Error", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Postgresql: &PostgresqlSettings{
+				Config: &ConfigSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: PrivateKey,
+							PublicKey:  PublicKey,
+						},
+					},
+					PrivateKey: DummyPrivateKey,
+					PublicKey:  DummyPublicKey,
+					RootCA:     "",
+				},
+			},
+		}
+
+		expectedErr := "cannot find root_ca for ip for PostgresSQL"
+		err := validatePostgresqlCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+}
+
+func Test_validateOpensearchCerts(t *testing.T) {
+	t.Run("NIL CERTByIP", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Opensearch: &OpensearchSettings{
+				Config: &ConfigOpensearchSettings{},
+			},
+		}
+
+		expectedErr := "opensearch root_ca and/or admin_key and/or admin_cert and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false"
+		err := validateOpensearchCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("Invalid common Certs", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Opensearch: &OpensearchSettings{
+				Config: &ConfigOpensearchSettings{
+					CertsByIP:  nil,
+					PrivateKey: "-----PRIVATE KEY-----",
+					PublicKey:  "-----PUBLIC KEY-----",
+					RootCA:     "dummy_root_cert",
+					AdminCert:  "admin key",
+					AdminKey:   "admin key",
+				},
+			},
+		}
+
+		expectedErr := "invalid format. Failed to decode root_ca for opensearch\ninvalid format. Failed to decode admin_key for opensearch\ninvalid format. Failed to decode admin_cert for opensearch\ninvalid format. Failed to decode private_key for opensearch\ninvalid format. Failed to decode public_key for opensearch"
+		err := validateOpensearchCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("No CertByIP validate", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Opensearch: &OpensearchSettings{
+				Config: &ConfigOpensearchSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: "-----PRIVATE2 KEY-----",
+							PublicKey:  "-----PRIVATE2sdsf KEY-----",
+						},
+					},
+					PrivateKey: "-----PRIVATE1 KEY-----",
+					PublicKey:  "-----PUBLIC1 KEY-----",
+					RootCA:     "-----ROOTCA KEY-----",
+					AdminCert:  "admin cer",
+					AdminKey:   "admin key",
+				},
+			},
+		}
+
+		expectedErr := "invalid format. Failed to decode root_ca for opensearch\ninvalid format. Failed to decode admin_key for opensearch\ninvalid format. Failed to decode root_ca for opensearch\ninvalid format. Failed to decode private_key for opensearch\ninvalid format. Failed to decode public_key for opensearch"
+		err := validateOpensearchCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("NO Error", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Opensearch: &OpensearchSettings{
+				Config: &ConfigOpensearchSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: PrivateKey,
+							PublicKey:  PublicKey,
+						},
+					},
+					PrivateKey: "-----PRIVATE1 KEY-----",
+					PublicKey:  "-----PUBLIC1 KEY-----",
+					RootCA:     RootCa,
+					AdminCert:  AdminCert,
+					AdminKey:   AdminKey,
+				},
+			},
+		}
+
+		err := validateOpensearchCerts(depConfig)
+		assert.NoError(t, err)
+	})
+
+	t.Run("RootCert Error", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Opensearch: &OpensearchSettings{
+				Config: &ConfigOpensearchSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.11",
+							PrivateKey: PrivateKey,
+							PublicKey:  PublicKey,
+						},
+					},
+					PrivateKey: "-----PRIVATE1 KEY-----",
+					PublicKey:  "-----PUBLIC1 KEY-----",
+					RootCA:     "",
+				},
+			},
+		}
+
+		expectedErr := "opensearch root_ca and/or admin_key and/or admin_cert and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false"
+		err := validateOpensearchCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+
+	t.Run("No private key", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Opensearch: &OpensearchSettings{
+				Config: &ConfigOpensearchSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP: "10.10.10.10",
+						},
+					},
+					RootCA:    "root ca",
+					AdminCert: "admin cert",
+					AdminKey:  "admin  key",
+				},
+			},
+		}
+
+		expectedErr := "cannot find private key for ip 10.10.10.10"
+		err := validateOpensearchCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+	t.Run("No Public key", func(t *testing.T) {
+		depConfig := &HaDeployConfig{
+			Opensearch: &OpensearchSettings{
+				Config: &ConfigOpensearchSettings{
+					CertsByIP: &[]CertByIP{
+						{
+							IP:         "10.10.10.10",
+							PrivateKey: "private key",
+						},
+					},
+					RootCA:    "root ca",
+					AdminCert: "admin cert",
+					AdminKey:  "admin  key",
+				},
+			},
+		}
+
+		expectedErr := "cannot find public key for ip 10.10.10.10"
+		err := validateOpensearchCerts(depConfig)
+		assert.Error(t, err)
+		assert.EqualError(t, err, expectedErr)
+	})
+}